0%

倒车雷达(四):PyQt5的多线程

这可能也是我毕业设计的最终章了,由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()

# distance 就是 my_signal emit 返回的距离信息
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

感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章