在树莓派上做一个远程控制的小车(基于Python)

所需材料

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. 远程控制小车行为
  2. 接收小车的视频信号
  3. 使用超声波模块避免碰撞

系统架构

在这里插入图片描述

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

客户端实现代码

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值