所需材料
1.履带式底盘及电机:能适应大多数地形
2.L298N步进电机驱动板:驱动电机
3.AR9271无线网卡:虽然树莓派3B内置了wifi模块,但是此网卡信号更好。
4.树莓派500万摄像头:提供实时的视频信号
5.移动电源:树莓派的功耗低,移动电源为10000mA,给小车提供较长时间的续航。
6.超声波测距模块:使用距离探测避免碰撞
开发环境搭建
1.下载树莓派系统:在https://www.raspberrypi.org/downloads/下载Raspbian系统
2.下载win32软件
3.使用win32将下载好的系统文件烧录至内存卡
4.将内存卡插入树莓派并上电启动
5.使用sudo apt-get install python3 安装python3
6.使用python3 –m pip install upgrade pip 升级pip
7.使用pip install pyqt5 安装qt包
8.使用pip install pyqt-tools安装qt依赖包
9.使用sudo apt-get install mjpg-streamer安装视频服务
10.使用pip install socket 安装socket服务
实现的功能
- 远程控制小车行为
- 接收小车的视频信号
- 使用超声波模块避免碰撞
系统架构
效果
客户端实现代码
1.导入必要的包
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import cv2
import sys
import socket
2.定义 socket 方法:
def sent_msg(msg): # 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = '192.168.43.223' # 获取本地主机名
port = 9999 # 设置端口号
s.connect((host, port)) # 连接服务,指定主机和端口
s.sendall(msg.encode('utf-8')) #编码方式
s.close()
3.定义用户界面及行为 (利用QtDesigner和UIC对界面进行设计并转成Python文件)
class Ui_Controller(object):
def setupUi(self, Controller):
Controller.setObjectName("Controller")
Controller.resize(680, 535)
self.widget = QtWidgets.QWidget(Controller)
self.widget.setGeometry(QtCore.QRect(50, 30, 571, 271))
self.widget.setObjectName("widget")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_4.addItem(spacerItem)
self.label = QtWidgets.QLabel(self.widget)
self.label.setText("")
self.label.setObjectName("label")
self.horizontalLayout_4.addWidget(self.label)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_4.addItem(spacerItem1)
self.widget1 = QtWidgets.QWidget(Controller)
self.widget1.setGeometry(QtCore.QRect(0, 330, 671, 193))
self.widget1.setObjectName("widget1")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.widget1)
self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem2)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem3)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem4)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem5)
self.pushButton = QtWidgets.QPushButton(self.widget1)
self.pushButton.setObjectName("pushButton")
self.horizontalLayout.addWidget(self.pushButton)
spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem6)
spacerItem7 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem7)
self.verticalLayout.addLayout(self.horizontalLayout)
spacerItem8 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem8)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem9)
self.pushButton_3 = QtWidgets.QPushButton(self.widget1) self.pushButton_3.setObjectName("pushButton_3")
self.horizontalLayout_2.addWidget(self.pushButton_3)
spacerItem10 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem10)
self.pushButton_5 = QtWidgets.QPushButton(self.widget1) self.pushButton_5.setObjectName("pushButton_5")
self.horizontalLayout_2.addWidget(self.pushButton_5)
spacerItem11 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem11)
self.pushButton_4 = QtWidgets.QPushButton(self.widget1) self.pushButton_4.setObjectName("pushButton_4")
self.horizontalLayout_2.addWidget(self.pushButton_4)
spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem12)
self.verticalLayout.addLayout(self.horizontalLayout_2)
spacerItem13 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem13)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout() self.horizontalLayout_3.setObjectName("horizontalLayout_3")
spacerItem14 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem14)
spacerItem15 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem15)
self.pushButton_2 = QtWidgets.QPushButton(self.widget1) self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout_3.addWidget(self.pushButton_2)
spacerItem16 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem16)
spacerItem17 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem17)
self.verticalLayout.addLayout(self.horizontalLayout_3) self.horizontalLayout_5.addLayout(self.verticalLayout)
self.checkBox = QtWidgets.QCheckBox(self.widget1)
self.checkBox.setObjectName("checkBox")
self.horizontalLayout_5.addWidget(self.checkBox)
spacerItem18 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(spacerItem18)
self.retranslateUi(Controller)
QtCore.QMetaObject.connectSlotsByName(Controller)
4.用户行为方法调用
def retranslateUi(self, Controller):
_translate = QtCore.QCoreApplication.translate
Controller.setWindowTitle(_translate("Controller", "Form"))
self.pushButton.setText(_translate("Controller", "前进"))
self.pushButton.clicked.connect(self.on_Forward)
self.pushButton_3.setText(_translate("Controller", "左转"))
self.pushButton_3.clicked.connect(self.on_Turn_Left)
self.pushButton_4.setText(_translate("Controller", "右转"))
self.pushButton_4.clicked.connect(self.on_Turn_Right)
self.pushButton_2.setText(_translate("Controller", "后退"))
self.pushButton_2.clicked.connect(self.on_Back)
self.pushButton_5.setText(_translate("Controller", "停止"))
self.pushButton_5.clicked.connect(self.on_Stop)
self.checkBox.setText(_translate("Controller", "防撞"))
self.checkBox.stateChanged.connect(self.on_distance_module)
@staticmethod
def on_Forward(self):
sent_msg("Forward")
@staticmethod
def on_Turn_Left(self):
sent_msg("Left")
@staticmethod
def on_Turn_Right(self):
sent_msg("Right")
@staticmethod
def on_Back(self):
sent_msg("Back")
@staticmethod
def on_Stop(self):
sent_msg("Stop")
def on_distance_module(self,state):
if state == QtCore.Qt.Checked:
sent_msg("on")
else:
sent_msg("off")
5.使用 cv2 处理视频信号
class MainWindow(QMainWindow, Ui_Controller):
def __init__(self, parent=None): super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.timer_camera = QTimer(self)
self.cap = cv2.VideoCapture("http://192.168.43.223:8085/?action=stream")
self.timer_camera.timeout.connect(self.show_pic)
self.timer_camera.start(10)
def show_pic(self): success, frame = self.cap.read() frame = cv2.flip(frame, -1)
if success: show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
self.label.setPixmap(QPixmap.fromImage(showImage))
self.timer_camera.start(10)
6.主函数调用
if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
标题服务端实现代码
1.导入必要的包
import socket,sys,os
import wiringpi,time,multiprocessing
2.初始化使能引脚
wiringpi.wiringPiSetup()
wiringpi.pinMode(0, 1)
wiringpi.pinMode(1, 1)
wiringpi.pinMode(2, 1)
wiringpi.pinMode(3, 1)
wiringpi.pinMode(24,0)
wiringpi.pinMode(25,1)
wiringpi.pinMode(28,0)
wiringpi.pinMode(29,1)
3.定义小车类及方法
class Car(object):
def __init__(self):
self.Stop()
def right_stop(self):
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 0)
def left_back(self):
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1, 1)
def right_back(self):
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 1)
def left_stop(self):
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1, 0)
def left_roll(self):
wiringpi.digitalWrite(0, 1)
wiringpi.digitalWrite(1 ,0)
def right_roll(self):
wiringpi.digitalWrite(2, 1)
wiringpi.digitalWrite(3, 0)
def Stop(self):
self.right_stop()
self.left_stop()
def Run(self):
self.right_roll()
self.left_roll()
def Left(self):
self.left_back()
self.right_roll()
def Right(self):
self.left_roll()
self.right_back()
def Back(self):
self.left_back()
self.right_back()
4.使用 socket 进行连接
def rec_msg(car,num):
serversocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM)
host = '192.168.43.223'
print(host) port = 9999
serversocket.bind((host, port))
serversocket.listen(5)
while True:
clientsocket, addr = serversocket.accept()
rec_msg = clientsocket.recv(1024).decode('utf-8')
if rec_msg == "Forward":
car.Stop()
car.Run()
elif rec_msg == "Back":
car.Stop()
car.Back()
elif rec_msg == "Left":
car.Stop()
car.Left()
elif rec_msg == "Right":
car.Stop()
car.Right()
elif rec_msg == "on":
with num.get_lock():
num.value = 1
elif rec_msg == "off":
with num.get_lock():
num.value = 0
else :
car.Stop()
clientsocket.close()
5.距离探测进行防撞
#模块中的引脚操作较多是因为这是一个相对独立的模块,同时测试也比较方便
def detect_distance():
time.sleep(0.01)
wiringpi.digitalWrite(29, 1)
time.sleep(0.00001)
wiringpi.digitalWrite(29, 0)
while wiringpi.digitalRead(28) == 0:
pass
time_start_F = time.time()
while wiringpi.digitalRead(28) ==1:
pass
time_end_F = time.time()
distance_F = ((time_end_F - time_start_F) * 34000) / 2
wiringpi.digitalWrite(25, 1)
time.sleep(0.00001)
wiringpi.digitalWrite(25, 0)
while wiringpi.digitalRead(24) == 0:
pass time_start_B = time.time()
while wiringpi.digitalRead(24) ==1:
pass time_end_B = time.time()
distance_B = ((time_end_B - time_start_B) * 34000) / 2
if distance_F < 10 and distance_B > 15:
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1 ,1)
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 1)
time.sleep(0.5)
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1 ,0)
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 0)
if distance_F > 15 and distance_B < 10:
wiringpi.digitalWrite(0, 1)
wiringpi.digitalWrite(1 ,0)
wiringpi.digitalWrite(2, 1)
wiringpi.digitalWrite(3, 0)
time.sleep(0.5)
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1 ,0)
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 0)
if distance_F < 10 and distance_B < 10:
wiringpi.digitalWrite(0, 0)
wiringpi.digitalWrite(1 ,0)
wiringpi.digitalWrite(2, 0)
wiringpi.digitalWrite(3, 0)
time.sleep(0.1)
return distance_F, distance_B
def safe_distance(num):
while True:
if num.value == 0:
time.sleep(1)
continue
elif num.value == 1 :
F,B = detect_distance()
#print("f:",F,"b:",B)
time.sleep(0.2)
continue
6.主函数开启多进程
if __name__ == "__main__":
car = Car()
num = multiprocessing.Value('B',0)
pid = os.fork()
if pid == 0:
rec_msg(car,num)
else:
safe_distance(num)
总结
这个项目花了三周时间才调试完成,其中主要是对于Python库的学习花的时间比较长,但是也让我充分认识到了Python的强大,另外,毕竟是第一次用Python写的一个完整的项目,总还是有不足的地方,希望网友们指针批评。
参考内容:
[1] 简单WiFi控制小车系统(树莓派+python+web控制界面) https://blog.csdn.net/qq_41923622/article/details/85850780
[2]wiringPi使用手册
https://pypi.org/project/wiringpi/
[3] python +cv2实现视频流播放
https://www.baidu.com/link?url=VTy7HETvmQ6C73iq3Qz8xqFobfCZTMK52zyJZBXsQNPCPbmrihezYkhLdoKbK876IJzDtNWenbz8QkFcooalnciPbcrvv13pTPp0k2z6B93&wd=&eqid=f0fbfd88001672f5000000065cfbaddf
[4] PyQt5中的事件和信号
https://www.cnblogs.com/archisama/p/5454200.html