这可能也是我毕业设计的最终章了,由PyQt5创建两个线程,数据读取的线程负责读取Arduino发送的距离信息,主线程负责倒车雷达动画的实时显示。还是要认真一点,也算是给大学毕业一个交代。然而还不开学
前言
根据前几章内容,我们已经实现了倒车雷达的:硬件搭建与编程、PC端上位机的动画制作两部分。结合前两篇文章的基础,在PC端的动画部分添加指令发送的界面,将Arduino硬件层的测量距离返回PC端上位机,并用距离和发送的指令来调节动画的移动,就大功告成了。
多线程
举一个最通俗的例子,我们想实现以下的功能:在一个界面中,有一个按钮和一个计数器,点击按钮后,计数器每秒加一。就是这么简单的功能,却必须要使用多线程来实现。因为PyQt5运行后,界面所处的线程是显示线程,显示线程不断循环带保证界面的正常显示,但点击按钮后显示线程进入了每秒加一的循环且没有退出条件,所以显示线程会因长时间没响应而导致界面的卡死。
因此,必须要新建一个线程,即现在有两个线程。使得显示线程来显示界面,计算线程进入每秒加一的循环,互不干扰,互不卡死。至于为何不用进程,是因为多进程之间不共享变量,不便操作。
而倒车雷达也是如上的道理,需要两个线程,第一个显示线程来维护界面的显示,第二个串口线程来不断的读取距离数据和发送控制指令。因此,创建和串口进行交互的类(基于pyserial这个库)。因为需要将读取的距离信息返回主程序,所以在这个类内将读取的距离数据通过pyqt信号的形式发送。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| class Com(QThread): my_signal = pyqtSignal(str)
def __init__(self): super(Com, self).__init__() self.data = "" self.is_on = True self.s = serial.Serial(port='COM6', baudrate=9600, stopbits=serial.STOPBITS_ONE)
def recv(self): while True: self.sleep(1) data = self.s.readline() if data == '': continue else: break return data
def run(self): while self.is_on: if self.s.isOpen(): print("open success") else: print("open failed")
while True: self.data = self.recv() self.my_signal.emit(str(self.data))
def stop(self): self.s.write(b"2")
def forward(self): self.s.write(b"0")
def back(self): self.s.write(b"1")
def left(self): self.s.write(b"3")
def right(self): self.s.write(b"4")
def begin(self): self.s.write(b"5")
def lift(self): self.s.write(b"6")
|
在主程序中实例化这个线程的类,并通过start方法来启动。因为这个类会不断的将距离信息作为信号通过emit方法进行反馈。因此我们需要将这个信号连接至一个函数,那么这个函数就可以读取返回的距离信息。
1 2 3 4 5 6 7 8
| self.com = Com() self.com.my_signal.connect(self.run)
self.com.start()
def run(self, distance): self.dis = distance
|
指令发送
指令的发送主要负责控制车辆的前进、后退等移动。其实嘛,指令的发送很简单,我们只需要做几个按钮,点击按钮发送对应的指令就好了。在上一章节已经实现了接收Arduino发来的数据的功能,且预留了形如def lift()
等发送指令的接口,此时仅需要将发送指令的按钮和那些接口相对应即可,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| btn_start = QPushButton("倒车") btn_start.setFixedSize(100, 80)
btn_forward = QPushButton("前进") btn_forward.setFixedSize(100, 80)
btn_left = QPushButton("左转") btn_left.setFixedSize(100, 80)
btn_stop = QPushButton("停车") btn_stop.setFixedSize(100, 80)
btn_right = QPushButton("右转") btn_right.setFixedSize(100, 80)
exit_btn = QPushButton("退出") exit_btn.setFixedSize(100, 80)
btn_back = QPushButton("后退") btn_back.setFixedSize(100, 80)
btn_start.clicked.connect(self.run) exit_btn.clicked.connect(self.lift) btn_stop.clicked.connect(self.stop) btn_left.clicked.connect(self.left) btn_right.clicked.connect(self.right) btn_forward.clicked.connect(self.forward) btn_back.clicked.connect(self.back)
self.com = Com() self.com.my_signal.connect(self.run)
def lift(self): self.com.lift()
def back(self): self.com.back()
def left(self): self.com.left()
def right(self): self.com.right()
def stop(self): self.com.stop()
def forward(self): self.com.forward()
def run(self, distance): self.com.start() self.com.begin()
|
实时影像
其实,将以上两部分的内容整合起来并与前几章的内容(如车身的旋转)在结合一下,就是全部的倒车雷达的代码了。
完整代码如下:
https://github.com/muyuuuu/Reversing-radar-on-Arduino/blob/master/code/Reverse-radar/view.py