接上文 梦幻联动——python+EV3
1.1 尝试
首先要实现打印机的txt打印功能,首先要把txt转化为打印黑(1)和打印白(0)组成的数组,然后逐行打印出来,而这个转换过程需要自创一种‘字体’来做一一对应

之后我先是尝试了点阵图

把每一个字母映射成6*3的点阵图,这样每一行字有6行点阵,再把每一行的文本转换为6行的字符,然后对于每一行字重复以上操作,得到多重多行多组的‘趣味’数据结构,我想你已经被我说晕了,我写的时候也很晕。那就让我们更直观地感受一下:
a1 | a2 | a3 | a1 | a2 | a3 | a1 | a2 | a3 | |||
b1 | b2 | b3 | b1 | b2 | b3 | b1 | b2 | b3 | |||
c1 | c2 | c3 | c1 | c2 | c3 | c1 | c2 | c3 | |||
d1 | d2 | d3 | d1 | d2 | d3 | d1 | d2 | d3 | |||
e1 | e2 | e3 | e1 | e2 | e3 | e1 | e2 | e3 | |||
f1 | f2 | f3 | f1 | f2 | f3 | f1 | f2 | f3 | |||
a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 | a11 | a12 |
b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 | b10 | b11 | b12 |
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c9 | c10 | c | c |
d1 | d2 | d3 | d4 | d5 | d6 | d7 | d8 | d9 | d10 | d11 | d12 |
e1 | e2 | e3 | e4 | e5 | e6 | e7 | e8 | e9 | e10 | e11 | e12 |
f1 | f2 | f3 | f4 | f5 | f6 | f7 | f8 | f9 | f10 | f11 | f12 |
这样做的清晰度会很低,但由于我当时还没有想出更好的方法,便开始写代码:
def convert(i):#one digital to image
word = list('1234567890qwertyuiopasdfghjklzxcvbnmQ
WERTYUIOPASDFGHJKLZXCVBNM')
if i not in word:
return [0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]
dic = {'1':([0,0,0],[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]),
'2':([0,0,0],[0,1,0],[1,0,1],[0,0,1],[0,1,0],[1,1,1]),
'3':([0,0,0],[1,1,1],[0,0,1],[1,1,1],[0,0,1],[1,1,1]),
'4':([0,0,0],[1,0,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]),
'5':([0,0,0],[1,1,1],[1,0,0],[1,1,1],[0,0,1],[1,1,1]),
'6':([0,0,0],[1,1,1],[1,0,0],[1,1,1],[1,0,1],[1,1,1]),
'7':([0,0,0],[1,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]),
'8':([0,0,0],[1,1,1],[1,0,1],[1,1,1],[1,0,1],[1,1,1]),
'9':([0,0,0],[1,1,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]),
'0':([0,0,0],[0,1,0],[1,0,1],[1,0,1],[1,0,1],[0,1,0]),
'a':([0,0,0],[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]),
'A':([0,0,0],[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]),
'b':([0,0,0],[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]),
'B':([0,0,0],[0,1,0],[1,1,0],[0,1,0],[0,1,0],[1,1,1]),
'c':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'C':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'd':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'D':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'e':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'E':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'f':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'F':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'g':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'G':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'h':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'H':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'i':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'I':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'j':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'J':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'k':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'K':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'l':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'L':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'm':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'M':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'n':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'N':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'o':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'O':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'p':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'P':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'q':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'Q':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'r':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'R':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
's':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'S':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
't':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'T':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'u':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'U':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'v':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'V':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'w':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'W':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'x':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'X':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'y':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'Y':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'z':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
'Z':([0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]),
}
return dic[i]
def convertall(txt):#txt to image
now = 0
lines = [[]]
line = [[]]*6
for i in txt:
if i == '\n':
lines[now] = line
now += 1
lines.append([])
line = [[]]*6
continue
get = convert(i)
for i in range(0,6):
line[i] = line[i]+get[i]+[0]
lines[now] = line
return lines
def printall(lines):#EV3's display is too small for this
this = ''
for line in lines:
for i in line:
this = ''
for bw in i:
if bw == 1:
this += '#'
else:
this += ' '
print(this)
brick.display.text(this)
time.sleep(1)
if __name__ == '__main__':
#get file
txt = open('name.txt')
txt = txt.read()
lines = convertall(txt)
printall(lines)
B往后的几个字母就没写了,由于我没学过高数,不大懂矩阵的运算和转换,只好用一些极傻的方法,而后面的转换合并更是棘手,实话实说,我已经忘的差不多了。
2.1 Plan B
在做上面那个企划时我就开始想第二种方法。比起自创的字体我们更应该运用公式化、矢量图式的成熟字体,把txt转化为jpg格式的文字,再求出图片中的每一个像素的灰度值,灰度值大于某一个阈值的像素就会被选为黑色,反之为白色,再进行逐行打印

2.2 转换代码
其实整个项目最为有趣的是我用了两个之前从来没想到还可以这么用的
2.2.1 txttojpg.py #本段代码有乡村熊提供,略有修改
#导入pygame做字体转换(3.9不能用,在这里我废了好长时间
import os
import pygame
#ttj即为txttojpg
def ttj(txt = 'input.txt',size = 500):
pygame.init()#初始化
text = open(txt).read()#读文本
font = pygame.font.Font(os.path.join("fonts","mao's.ttf"), size)#载入字体
rtext = font.render(text,True ,[0, 0, 0], [255, 255, 255])#转换
file = 'printthis.jpg'
pygame.image.save(rtext, file)#保存
if __name__ == "__main__":
ttj()
注释写的很明白了(夸我
2.2.1 jpgtoprint.py #也就是之前的字符画
from PIL import Image
# 将256灰度映射到70个字符上
def get_char(r,g,b,alpha = 256):
if alpha == 0:
return ' '
gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
if gray <= 150:#由于打印时只有黑白,便没有字符画那么多的字符可用
return '$'#黑
else:
return ' '#白
def jtp(IMG = 'printthis.jpg'):
im = Image.open(IMG)
im = im.resize((200,int(im.size[1]//(im.size[0]/100))))
txt = ""
print(im.size,im.size)
for j in range(im.size[1]):
for i in range(im.size[0]):
a,b = i,j
#print(get_char(*im.getpixel((a,b))))
txt += get_char(*im.getpixel((a,b)))
txt += '\n'
print(txt)
im.save("thumbnail", "JPEG")
#字符画输出到文件
with open("output.txt",'w') as f:
f.write(txt)
if __name__ == "__main__":
jtp()
具体原理就看字符画内篇文章
2.3 打印代码
#这部分有一点pybricks的内容,文档见下
#!/usr/bin/env pybricks-micropython
from pybricks import ev3brick as brick
from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor,
InfraredSensor, UltrasonicSensor, GyroSensor)
from pybricks.parameters import (Port, Stop, Direction, Button, Color,
SoundFile, ImageFile, Align)
from pybricks.tools import print, wait, StopWatch
from pybricks.robotics import DriveBase
首先一股脑导入所有给定的模块,用起来方便
xmotor = Motor(Port.C)#x轴电机
ymotor = Motor(Port.B)#y轴电机
updownmotor = Motor (Port.D)#抬笔落笔电机
touch = TouchSensor (Port.S3)#触动传感器(初始化用
示例化电机对象,参数即为端口号
def initialization():
while touch.pressed() == 0:
xmotor.run(-300)
xmotor.run_angle(300, 100)
updownmotor.run_until_stalled(360)
xmotor.reset_angle(0)
初始化,在触动传感器触发前转动x轴电机,并做电机角度传感器器归零,并将抬笔落笔电机初始化:
run_until_stalled()——给定参数的速度旋转直至堵转
def nextline():#会车
xmotor.run_target(1000,0)#x轴电机走到零点(之前初始化时计数器归的零
ymotor.run_angle(1000, 7)#y轴电机转到下一行
def printwhite(space):#白色
xmotor.run_angle(1000, 7 * space)#x轴电机走空格数space个空格
def printblack(do):#黑色
updownmotor.run_angle(300, -30)#落笔
if do != 1:
xmotor.run_angle(1000,7 * do)#x轴电机走黑格数do个格画黑线
updownmotor.run_until_stalled(360)#抬笔
有了以上的函数,我们打印的所有所需要使用的东西都准备好了
content = open('output.txt')
content = content.read()
lines = content.split('\n')
把之前jpgtoprint.py输出的txt文件读取出来,并用换行进行分割
def printline(line):
do = 0#打印黑色格数
space = 0#打印白色格数
line += ' '
for i in line:
if i == ' ':
space += 1
if do != 0:
printblack(do)
do = 0
continue
if i == '$':
do += 1
if space != 0:
printwhite(space)
space = 0
continue
打印一行,逻辑如下:

def printpage():
for i in lines:
printline(i)
nextline()
打印一页 = 打印多行
if __name__ == "__main__":
initialization()
printpage()
最后的最后:打印!
3.2 硬件
People who are really serious about software should make their own hardware.
Alan Kay
这个打印机是我翻出来的,后来想起好像好像是按照一个图纸做的
我稍微改了一下,一开始我用的是粗笔,后来改成了细笔,其精确度至少提高了五倍,还有一些因为没有足够零件的无奈改动,先发布吧,过一会我会发一个视频上来看成果
PILOT给了你多少钱?以至于给两个链接
小的时候也做过这个打印机,当时用的是圆珠笔矢量打印。效果极差。你的像素打印解决方法可以,但也可以加上矢量打印的方法。
正在实现欧