失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 树莓派综合项目2:智能小车(三)无线电遥控

树莓派综合项目2:智能小车(三)无线电遥控

时间:2022-11-22 23:50:27

相关推荐

树莓派综合项目2:智能小车(三)无线电遥控

一、介绍

阅读本篇文章前建议先参考前期文章:

树莓派基础实验34:L298N模块驱动直流电机实验

树莓派综合项目2:智能小车(一)四轮驱动

树莓派综合项目2:智能小车(二)tkinter图形界面控制

《智能小车(一)四轮驱动》中,实现了代码输入对四个电机的简单控制。《智能小车(二)tkinter图形界面控制》中,实现了本地图形界面控制小车的前进后退、转向和原地转圈。

本实验中将使用无线电遥控设备控制小车的前进后退、转向和原地转圈。使用传统无线电通信设备通信仍然是非常重要的通信方式,比如无线电台、对讲机,航模、车模、船模遥控等等。与手机移动网络、WIFI连接相比,无线电连接有它独特的优势,遥控距离远,实时性好,操作灵活,不受网络信号限制。

其它基础内容可以参考文集:树莓派基础实验。

二、组件

★Raspberry Pi 3 B+全套*1

★睿思凯Frsky XM+ 迷你接收机*1

★电平反向器模块*1

★睿思凯Frsky Taranis X9D PLUS SE遥控器*1

★L298N扩展板模块*1

★智能小车底板模块*1

★减速电机和车轮*4

★跳线若干

三、实验原理

本实验中使用的遥控系统可以自行选择其它品牌的产品,如国产的天地飞还不错。

这里的遥控器就是像电视机遥控器、空调遥控器一样可以不用接触到被控设备,而通过一个手持器件,使用无线电与被控设备进行通信,从而达到对设备的控制。

遥控器想到达到与小车通信的功能需要有两部分配合完成。即:发射器(遥控器)与接收机。遥控器上的控制杆转为无线电波发送给接收机,而接收机通过接收无线电波,读取遥控器上控制杆的读数,并转为数字信号发送到树莓派中。

接收机输出两种信号,一种是模拟信号PWM,一种是SBUS数字信号。

由于每一路遥控器通道都需要一个PWM采集器进行采集,但是对于树莓派来说不可能使用多个定时器来采集多个通道的PWM,这对于树莓派的GPIO端口资源来说十分浪费,因此我采用的就是SBUS信号,可以在一个管脚中传输多路控制信号。

S-BUS其实是一种串口通信协议,采用100000的波特率,数据位点8bits,停止位点2bits,偶效验,即8E2的串口通信。使用树莓派的串口GPIO(TXD/RXD)中的RXD端口接收接收机的SBUS输出信号,解析出每路通道的控制信号,进而控制小车行进。

但是S-BUS采用的是反向电平传输,也就是说,在S-BUS的发送端高低电平是反向的,协议中的所有高电平都被转换成低电平,协议中的所有低电平都被转换成高电平。所以在S-BUS的接收端需要增加一个高低电平反向器来进行电平反转。

关于解析无线电接收机PWM、SBUS信号的更详细内容,请参考树莓派基础实验39:解析无线电接收机PWM、SBUS信号。

四、实验步骤

第1步:连接电路。在树莓派综合项目2:智能小车(一)四轮驱动中的接线基础上,接入电平反向器、无线电接收机。

关于这里树莓派GND、L298N小车扩展板的电池组供电负极相连,是特殊情况下的情况,经测试发现:

如果树莓派用的是充电头供电,而L298N扩展板用的是电池组供电,这两个负极必须相连,否则马达不动。

如果树莓派用的是L298N扩展板接出来的5V供电,即两者同一个电源,则这里不用连接。

这里也要注意,由于树莓派的GPIO只能接收3.3V的最高输入,所以电平反相器的电源也只能使用3.3V,若反向后接收的信号需要是5V,则电平反相器的电源就使用5V。

这里我将18650电池组换成了航模使用的格氏ACE锂电池(3S/11.1V/2200MAH/40C),电压更高,能给树莓派提供更稳定的电源,动力性也更好,效果非常不错。

第2步:编写电机的驱动程序,文件名为motor_4w.py。与树莓派综合项目2:智能小车(一)四轮驱动中的程序完全相同。

该车的行进控制与履带车的行进控制类似:

前进和后退很简单,左右两边的方向都朝前或朝后,速度一致;

原地顺时针旋转时,左边轮子前进,右边轮子后退,速度一致;

原地逆时针旋转时,左边轮子后退,右边轮子前进,速度一致;

偏左前进时,左右两边的方向都朝前,左轮速度比右轮速度慢一点;

偏右前进时,左右两边的方向都朝前,左轮速度比右轮速度快一点;

偏左后退时,左右两边的方向都朝后,左轮速度比右轮速度慢一点;

偏右后退时,左右两边的方向都朝后,左轮速度比右轮速度快一点;

motor_4w.py:

#!/usr/bin/env python#-*- coding: utf-8 -*-#本模块只含SMPcar()一个类,用于树莓派对电机信号的控制#通过GPIO输出信号,直接对某个电机的转动方向、速度进行控制import RPi.GPIO as GPIOclass SMPcar():'''控制小车四轮动作的类'''ENA = 11 #使能信号A,左边两轮IN1 = 13 #信号输入1IN2 = 15 #信号输入2ENB = 12 #使能信号B,右边两轮IN3 = 16 #信号输入3IN4 = 18 #信号输入4GPIO.setwarnings(False) #关闭警告def setGPIO(self):'''初始化引脚'''GPIO.setmode(GPIO.BOARD)GPIO.setup(SMPcar.ENA, GPIO.OUT)GPIO.setup(SMPcar.IN1, GPIO.OUT)GPIO.setup(SMPcar.IN2, GPIO.OUT)GPIO.setup(SMPcar.ENB, GPIO.OUT)GPIO.setup(SMPcar.IN3, GPIO.OUT)GPIO.setup(SMPcar.IN4, GPIO.OUT)def pwm(self,pwm): '''初始化PWM(脉宽调制),返回PWM对象'''EN_pwm = GPIO.PWM(pwm, 500)EN_pwm.start(0)return EN_pwmdef changespeed(self,pwm,speed):'''通过改变占空比改变马达转速'''pwm.ChangeDutyCycle(speed)def clockwise(self,in1_pin,in2_pin):'''马达顺时针转的信号'''GPIO.output(in1_pin, 1)GPIO.output(in2_pin, 0)def counter_clockwise(self,in1_pin,in2_pin):'''马达逆时针转的信号'''GPIO.output(in1_pin, 0)GPIO.output(in2_pin, 1)def stop_car(self,in1_pin,in2_pin):'''马达制动的信号'''GPIO.output(in1_pin, 0)GPIO.output(in2_pin, 0)#使能信号为低电平,或者高电平(占空比设为100,IN1和IN2都为0或1时)马达制动def destroy(self,A,B):'''结束程序时清空GPIO状态'''A.stop()B.stop()GPIO.cleanup() # Release resourceif __name__ == '__main__':# 本模块单独测试时运行使用try:smpcar = SMPcar() #创建树莓派小车对象smpcar.setGPIO() #初始化引脚ENA_pwm=smpcar.pwm(smpcar.ENA) #初始化使能信号PWM,A为左边车轮ENB_pwm=smpcar.pwm(smpcar.ENB) #初始化使能信号PWM,B为右边车轮while True:'''通过输入的命令改变马达转动'''cmd = raw_input("Command, E.g. ff30ff30 :")direction = cmd[0] #只输入字母b时,小车刹车A_direction = cmd[0:2] #字符串0/1两位为控制A(左边车轮)方向信号B_direction = cmd[4:6] #4/5位为控制B(右边车轮)方向信号A_speed = cmd[2:4] #字符串2/3两位为控制A(左边车轮)速度信号B_speed = cmd[6:8] #字符串6/7两位为控制B(右边车轮)速度信号print (A_direction,B_direction,A_speed,B_speed) #测试用if A_direction == "ff": #控制A(左边车轮)顺时针信号smpcar.clockwise(smpcar.IN1,smpcar.IN2)if A_direction == "00": #控制A(左边车轮)逆时针信号smpcar.counter_clockwise(smpcar.IN1,smpcar.IN2)if B_direction == "ff": #控制B(右边车轮)顺时针信号smpcar.clockwise(smpcar.IN3,smpcar.IN4)if B_direction == "00": #控制B(右边车轮)逆时针信号smpcar.counter_clockwise(smpcar.IN3,smpcar.IN4)if direction == "b": #小车刹车,IN1和IN2都为0,马达制动smpcar.stop_car(smpcar.IN1,smpcar.IN2)smpcar.stop_car(smpcar.IN3,smpcar.IN4)continue #跳出本次循环# 通过输入的两位数字设置占空比,改变马达转速smpcar.changespeed(ENA_pwm,int(A_speed))smpcar.changespeed(ENB_pwm,int(B_speed))except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.smpcar.destroy(ENA_pwm,ENB_pwm)finally:smpcar.destroy(ENA_pwm,ENB_pwm)

第3步:小车行进控制模块,文件名为moving_control.py,设计小车的移动行为,方向控制分为:原地左转弯、原地右转弯、直线前进、直线后退、刹车,而向前左偏移开进或右偏移开进等,由左右两边不同的速度(油门)来控制。

moving_control.py:

#-*- coding: utf-8 -*-#本模块只含MovingControl()一个类,针对遥控器的控制方式,设计小车的移动行为#方向控制分为:原地左转弯、原地右转弯、直线前进、直线后退、刹车#而向前左偏移开进或右偏移开进等,由左右两边不同的速度(油门)来控制class MovingControl():def __init__(self, smpcar,pwm1,pwm2):self.smpcar=smpcarself.ENA_pwm=pwm1self.ENB_pwm=pwm2self.rudder_value = 0self.acc_value = 0def accelerator(self,rate_left=1,rate_right=1):'''速度(油门)控制'''#rate_left为向左偏移行进时,降低左边轮的速度#rate_right为向右偏移行进时,降低右边轮的速度self.smpcar.changespeed(self.ENA_pwm,self.acc_value * rate_left)self.smpcar.changespeed(self.ENB_pwm,self.acc_value * rate_right)def leftTurn(self):'''原地左转弯'''self.smpcar.counter_clockwise(self.smpcar.IN1,self.smpcar.IN2) #左边车轮后退self.smpcar.clockwise(self.smpcar.IN3,self.smpcar.IN4) #右边车轮前进def rightTurn(self):'''原地右转弯'''self.smpcar.clockwise(self.smpcar.IN1,self.smpcar.IN2)self.smpcar.counter_clockwise(self.smpcar.IN3,self.smpcar.IN4)def forward(self):'''直线前进'''self.smpcar.clockwise(self.smpcar.IN1,self.smpcar.IN2)self.smpcar.clockwise(self.smpcar.IN3,self.smpcar.IN4)def reverse(self):'''直线后退'''self.smpcar.counter_clockwise(self.smpcar.IN1,self.smpcar.IN2)self.smpcar.counter_clockwise(self.smpcar.IN3,self.smpcar.IN4)def brake(self):'''刹车'''self.smpcar.stop_car(self.smpcar.IN1,self.smpcar.IN2)self.smpcar.stop_car(self.smpcar.IN3,self.smpcar.IN4)

第4步:编写SBUS信号接收解析模块,文件名为sbus_receiver_pi.py,与树莓派基础实验39:解析无线电接收机PWM、SBUS信号中的Python2程序有所不同,下面的程序在Python3中运行,并标注了两者的不同之处。

sbus_receiver_pi.py:

#!/usr/bin/env python#-*- coding: utf-8 -*-#本模块只含SBUSReceiver()一个类,用于获取和解析遥控器接收机的SBUS输出信号#并能返回每个通道的数值,遥控器的failsafe信号状态,每次获得数据帧的长度和这次数据的延迟时间#import array #python2运行时需要,array模块是python中实现的一种高效的数组存储类型import serial #serial模块封装了对串行端口的访问#import binascii #python2运行时需要,binascii模块包含很多在二进制和ASCII编码的二进制表示转换的方法#import codecs #python2运行时需要,Python中专门用作编码转换的模块import timeclass SBUSReceiver():def __init__(self, _uart_port='/dev/ttyAMA0'):#初始化树莓派串口参数self.ser = serial.Serial(port=_uart_port, #树莓派的硬件串口/dev/ttyAMA0baudrate = 100000,#波特率为100kparity=serial.PARITY_EVEN, #偶校验stopbits=serial.STOPBITS_TWO,#2个停止位bytesize=serial.EIGHTBITS, #8个数据位timeout = 0,)# 常数#这里注意:Python2 与Python3 的编码方式是不同的#self.START_BYTE = b'\x0f' #python2运行时用这句,起始字节为0x0f#self.END_BYTE = b'\x00'#python2运行时用这句,结束字节为0x00self.START_BYTE = 0x0f #python3运行时用这句,起始字节为0x0fself.END_BYTE = 0x00 #python3运行时用这句,结束字节为0x00self.SBUS_FRAME_LEN = 25 #SBUS帧有25个字节self.SBUS_NUM_CHAN = 18#18个通道self.OUT_OF_SYNC_THD = 10self.SBUS_NUM_CHANNELS = 18 #18个通道self.SBUS_SIGNAL_OK = 0#信号正常为0self.SBUS_SIGNAL_LOST = 1 #信号丢失为1self.SBUS_SIGNAL_FAILSAFE = 2 #输出failsafe信号时为2# 堆栈变量初始化self.isReady = Trueself.lastFrameTime = 0self.sbusBuff = bytearray(1) # 用于同步的单个字节#bytearray(n) 方法返回一个长度为n的初始化数组;'''这里Python2与Python3存储数据的编码格式会不同'''self.sbusFrame = bytearray(25) # 单个SBUS数据帧,25个字节# 接收到的各频道值,前面的Python2中使用了数组#self.sbusChannels = array.array('H', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) #array.array(typecode,[initializer]) --typecode:元素类型代码;initializer:初始化器,若数组为空,则省略初始化器# 接收到的各频道值,Python3中这里也可以使用列表self.sbusChannels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]self.failSafeStatus = self.SBUS_SIGNAL_FAILSAFEdef get_rx_channels(self):"""用于读取最后的SBUS通道值返回:由18个无符号短元素组成的数组,包含16个标准通道值+ 2个数字(ch17和18)"""return self.sbusChannelsdef get_rx_channel(self, num_ch):"""用于读取最后的SBUS某一特定通道的值num_ch: 要读取的某个通道的通道序号返回:某一通道的值"""return self.sbusChannels[num_ch]def get_failsafe_status(self):"""用于获取最后的FAILSAFE状态返回: FAILSAFE状态值"""return self.failSafeStatusdef decode_frame(self):"""对每帧数据进行解码,每个通道的值在两个或三个不同的字节之间,要读取出来很麻烦不过futaba已经发布了下面的解码代码"""def toInt(_from):#encode() 方法以指定的编码格式编码字符串。#int() 函数用于将一个字符串或数字转换为整型。#return int(codecs.encode(_from, 'hex'), 16) #Python2中要使用这句,转换编码格式return _from#Python3中要使用这句,即不用转换编码格式#CH1 = [data2]的低3位 + [data1]的8位(678+12345678 = 678,12345678)self.sbusChannels[0] = ((toInt(self.sbusFrame[1])|toInt(self.sbusFrame[2])<<8)& 0x07FF);#CH2 = [data3]的低6位 + [data2]的高5位(345678+12345 = 345678,12345 )self.sbusChannels[1] = ((toInt(self.sbusFrame[2])>>3 |toInt(self.sbusFrame[3])<<5)& 0x07FF);#CH3 = [data5]的低1位 + [data4]的8位 + [data3]的高2位(8+12345678+12 = 8,12345678,12)self.sbusChannels[2] = ((toInt(self.sbusFrame[3])>>6 |toInt(self.sbusFrame[4])<<2 |toInt(self.sbusFrame[5])<<10)& 0x07FF);self.sbusChannels[3] = ((toInt(self.sbusFrame[5])>>1 |toInt(self.sbusFrame[6])<<7)& 0x07FF);self.sbusChannels[4] = ((toInt(self.sbusFrame[6])>>4 |toInt(self.sbusFrame[7])<<4)& 0x07FF);self.sbusChannels[5] = ((toInt(self.sbusFrame[7])>>7 |toInt(self.sbusFrame[8])<<1 |toInt(self.sbusFrame[9])<<9)& 0x07FF);self.sbusChannels[6] = ((toInt(self.sbusFrame[9])>>2 |toInt(self.sbusFrame[10])<<6) & 0x07FF);self.sbusChannels[7] = ((toInt(self.sbusFrame[10])>>5 |toInt(self.sbusFrame[11])<<3) & 0x07FF);self.sbusChannels[8] = ((toInt(self.sbusFrame[12])|toInt(self.sbusFrame[13])<<8) & 0x07FF);self.sbusChannels[9] = ((toInt(self.sbusFrame[13])>>3 |toInt(self.sbusFrame[14])<<5) & 0x07FF);self.sbusChannels[10] = ((toInt(self.sbusFrame[14])>>6 |toInt(self.sbusFrame[15])<<2|toInt(self.sbusFrame[16])<<10) & 0x07FF);self.sbusChannels[11] = ((toInt(self.sbusFrame[16])>>1 |toInt(self.sbusFrame[17])<<7) & 0x07FF);self.sbusChannels[12] = ((toInt(self.sbusFrame[17])>>4 |toInt(self.sbusFrame[18])<<4) & 0x07FF);self.sbusChannels[13] = ((toInt(self.sbusFrame[18])>>7 |toInt(self.sbusFrame[19])<<1|toInt(self.sbusFrame[20])<<9)& 0x07FF);self.sbusChannels[14] = ((toInt(self.sbusFrame[20])>>2 |toInt(self.sbusFrame[21])<<6) & 0x07FF);self.sbusChannels[15] = ((toInt(self.sbusFrame[21])>>5 |toInt(self.sbusFrame[22])<<3) & 0x07FF);#17频道,第24字节的最低一位if toInt(self.sbusFrame[23]) & 0x0001 :self.sbusChannels[16] = 2047else:self.sbusChannels[16] = 0#18频道,第24字节的低第二位,所以要右移一位if (toInt(self.sbusFrame[23]) >> 1) & 0x0001 :self.sbusChannels[17] = 2047else:self.sbusChannels[17] = 0#帧丢失位为1时,第24字节的低第三位,与0x04进行与运算self.failSafeStatus = self.SBUS_SIGNAL_OKif toInt(self.sbusFrame[23]) & (1 << 2):self.failSafeStatus = self.SBUS_SIGNAL_LOST#故障保护激活位为1时,第24字节的低第四位,与0x08进行与运算 if toInt(self.sbusFrame[23]) & (1 << 3):self.failSafeStatus = self.SBUS_SIGNAL_FAILSAFEdef update(self):"""我们需要至少2帧大小,以确保找到一个完整的帧所以我们取出所有的缓存(清空它),读取全部数据,直到捕获新的数据首先找到END BYTE并向后查找SBUS_FRAME_LEN,看看它是否是START BYTE"""#我们是否有足够的数据在缓冲区和有没有线程在后台?if self.ser.inWaiting() >= self.SBUS_FRAME_LEN*2 and self.isReady: #inWaiting()返回接收缓存中的字节数self.isReady = False #表明有线程在运行,isReady = False# 读取所有临时帧数据tempFrame = self.ser.read(self.ser.inWaiting())# 在缓冲区帧的每个字符中,我们寻找结束字节for end in range(0, self.SBUS_FRAME_LEN):#寻找结束字节,从后向前查找if tempFrame[len(tempFrame)-1-end] == self.END_BYTE :#从最后的命中点减去SBUS_FRAME_LEN寻找起始字节if tempFrame[len(tempFrame)-end-self.SBUS_FRAME_LEN] == self.START_BYTE :# 如果相等,则帧数据正确,数据以8E2包到达,因此它已经被校验过# 从临时帧数据中取出刚验证正确的一段正确帧数据lastUpdate = tempFrame[len(tempFrame)-end-self.SBUS_FRAME_LEN:len(tempFrame)-1-end]if not self.sbusFrame == lastUpdate: #相等即表示没有操作,不用再次解码self.sbusFrame = lastUpdateself.decode_frame() #调用解码函数self.lastFrameTime = time.time() # 跟踪最近的更新时间self.isReady = Truebreakif __name__ == '__main__':sbus = SBUSReceiver('/dev/ttyAMA0')while True:time.sleep(0.005)# X8R的SBUS信号是间隔6ms发送一次,一次持续发送3ms;# 不要调用sbus.update()太快,如果sbus.ser.inWaiting()>50,且增长很多,可以调用sbus.update()快点,即time.sleep()延迟短点;# 如果sbus.ser.inWaiting()<50,可以调用sbus.update()慢点,即time.sleep()延迟长点;sbus.update()#在您的代码中,您可以调用sbus.get_rx_channels()来获取所有数据,或者调用sbus.get_rx_channels()[n]来获取第n个通道的值;#或get_rx_channel(self, num_ch)来获得第num_ch个通道的值;print(sbus.get_failsafe_status(), sbus.get_rx_channels(), str(sbus.ser.inWaiting()).zfill(4) , (time.time()-sbus.lastFrameTime))#str() 函数将对象转化为适于人阅读的形式,将指定的值转换为字符串。#zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。#(time.time()-sbus.lastFrameTime)用于展示得到最近这次数据的延迟

第5步:编写树莓派智能小车的主程序,文件名为smartcar.py,将这4个Python文件放入一个文件夹后,只运行本文件就可以了。

smartcar.py:

#!/usr/bin/env python#-*- coding: utf-8 -*-#本模块为树莓派小车的主程序from motor_4w import SMPcarfrom sbus_receiver_pi import SBUSReceiverfrom moving_control import MovingControlimport timetry:sbus_receiver = SBUSReceiver('/dev/ttyAMA0') #初始化实例smp_car = SMPcar() #初始化实例smp_car.setGPIO() #初始化引脚ENA_pwm = smp_car.pwm(smp_car.ENA) #初始化使能信号PWM,A为左边车轮ENB_pwm = smp_car.pwm(smp_car.ENB) #初始化使能信号PWM,B为右边车轮smartcar = MovingControl(smp_car,ENA_pwm,ENB_pwm) ##初始化实例acc_value_sbus = 172 #3通道,即油门的值,最低时为172while True:time.sleep(0.005)sbus_receiver.update() #获取一个完整的帧数据aileron_value = sbus_receiver.get_rx_channel(0) #1通道为副翼通道,这里控制车原地转向elevator_value = sbus_receiver.get_rx_channel(1)#2通道为升降舵通道,这里控制车前进后退smartcar.rudder_value = sbus_receiver.get_rx_channel(3)#4通道为方向舵通道,这里控制车左右偏移行进if sbus_receiver.get_rx_channel(2):acc_value_sbus = sbus_receiver.get_rx_channel(2) #3通道为油门通道,这里控制车速度#将172~1811的油门通道值转换为0~100的占空比信号,smartcar.acc_value = int(100*(acc_value_sbus-172)/(1811-172))if 970 < smartcar.rudder_value < 1100: #没有左右偏移时,中间值为992,但遥控器微调时会有上下浮动smartcar.accelerator()elif smartcar.rudder_value >=1100:#向右偏移行进时rate_right = (1811.0 - smartcar.rudder_value)/(1811-1100) #最大值一般为1811,这里使用浮点类型,所以一定要使用1811.0smartcar.accelerator(1,rate_right)elif smartcar.rudder_value <=970: #向左偏移行进时rate_left = (smartcar.rudder_value - 172.0)/(970-172) #最小值为172,这里使用浮点类型,所以一定要使用172.0smartcar.accelerator(rate_left,1)print(elevator_value,smartcar.acc_value,aileron_value,smartcar.rudder_value)#测试数据用if aileron_value >= 1100: #原地左转弯smartcar.leftTurn()elif aileron_value <= 970: #原地右转弯smartcar.rightTurn()else : smartcar.brake() #停车if elevator_value >=1100: #前进smartcar.forward()elif elevator_value <=970: #后退smartcar.reverse()#循环最后,这里不能再用停车了except KeyboardInterrupt: smp_car.destroy(ENA_pwm,ENB_pwm)finally:smp_car.destroy(ENA_pwm,ENB_pwm)

操控演示视频:

/video/BV1Q5411L75a/

最近太忙,这篇文章拖了很久才完成,当中克服了一个个不懂的技术难点和BUG,不过终于实现了遥控小车的想法,成功将无线电遥控和树莓派结合起来。

辛苦是值得的,学到不少东西,后面将陆续把各种传感器加入进来,实现智能化。

这个项目的代码90%是我原创瞎写的,有需要参考的同学可以下载:

树莓派智能小车项目python源代码下载

如果觉得《树莓派综合项目2:智能小车(三)无线电遥控》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。