树莓派笔记5:自制小车(简单避障)

树莓派笔记5:自制小车(简单避障)利用树莓派做智能小车是个很常见的玩法 整个过程涉及手工制作 GPIO 控制 Python 程序编写 网络通信等内容 知乎上有的大神还加入图像识别甚至人工智能元素 我自己在制作过程中真的感觉非常有意思 也很有成就感 为了做这个小车 我不惜破费买了各种小车零件和电子元器件 其实花不了多少钱

大家好,我是讯享网,很高兴认识大家。

利用树莓派做智能小车是个很常见的玩法,整个过程涉及手工制作、GPIO控制、Python程序编写、网络通信等内容,知乎上有的大神还加入图像识别甚至人工智能元素,我自己在制作过程中真的感觉非常有意思,也很有成就感。为了做这个小车,我不惜破费买了各种小车零件和电子元器件(其实花不了多少钱),还突击学习了Python,参考书上的内容和网络上的信息也搞出了自己的小车,虽然功能真的很简单,这次记录的是简单的避障功能实现。

1 小车结构

整个小车涉及的元件:
①树莓派,作为主控芯片;
②小车底盘及其他配件,淘宝上30块钱就能买到;
③L298N 电机驱动模块,用来控制小车电机;
④HC-SR04 超声波测距模块,用来检测距离;
⑤干电池组或者锂电池,为L298N供电,需要12V左右;
⑥杜邦线、导线、螺丝刀等工具。

下图是整体的结构示意,小车的每个轮子都装有电机,电机与L298N模块相连,而树莓派控制L298N模块进而控制电机运动,同时树莓派也控制超声波测距模块检测距离。
这里写图片描述
讯享网

2 树莓派GPIO概念

之所以树莓派可以直接控制电子元件,是因为它提供了GPIO区,所谓GPIO就是可以自由配置引脚的模式,比如可以配置成输出模式从而输出高低电平,或者可以配置成输入模式接收电平信号,正因为树莓派的GPIO功能,让它看起来似乎更加底层化了。

使用GPIO前首先需要了解的是引脚的编号方式,如下图所示,按照物理方位的话是按照从左到右、从上到下的方式从1编号到40,但是在编写程序是一般不用物理编码方式,如果用C语言写的话需要导入wiringPi库,用Python写的话需要导入RPi.GPIO库,这两种库的引脚编号方式都不同,例如按照RPi.GPIO编号方式,物理号码为38的引脚编号为BCM.20,而物理号码为40的引脚编号为BCM.21。完整的编码表可以查阅网上资料。
这里写图片描述
Python程序下涉及GPIO的主要代码摘录如下:

import RPi.GPIO as GPIO #导入GPIO模块 GPIO.setmode(GPIO.BCM) #设定编号模式 GPIO.setwarnings(False) #关闭警告说明 GPIO.setup(1, GPIO.IN) #设置引脚1(BCM编号)为输入通道 GPIO.setup(2, GPIO.OUT) #设置引脚2为输出通道 value=GPIO.input(1) #读取通道1的输入值( 0 / GPIO.LOW / False 或者 1 / GPIO.HIGH / True) GPIO.output(2, GPIO.HIGH) #设置通道2输出高电平的状态,状态可以为0 / GPIO.LOW / False 或者 1 / GPIO.HIGH / True GPIO.cleanup() #清理通道资源

讯享网

3 车体拼装

首先把小车车架拼好,买来的车架是有说明书的,而且车架本身也很简单,用固定片把各个电机固定好就基本OK了,需要注意的是马达朝向、螺丝朝向等小细节,有时候朝向不对会卡住轮子,或者不方便后续的绕线等操作。下图是我拼装好的车架图(这里已经把电机控制模块和测距模块加上去了)。
这里写图片描述

4 测距模块

这里写图片描述
超声波模块利用超声波反射检测距离,测量范围较小但是精度较高。模块工作电压5v,使用时首先需要在trig引脚上输入一个长为20us的高电平方波,这样会触发模块发射8个40kHz的超声波,同时echo引脚的电平会由0变为1。当超声波返回被接收时,echo的电平由1变为0。用定时器记录下这个时长t,则距离=340*t/2。
根据工作原理编写超声波测距模块的Python文件:

讯享网import RPi.GPIO as GPIO import time class Ultrasonic_Module(object): '''超声波测距模块''' def __init__(self,trig_pin,echo_pin): self.trig_pin=trig_pin self.echo_pin=echo_pin self.pins_for_motor=[5,6,13,19,21,22,23,24] #这些引脚被电机模块占用了 self.pins_available=[12,16,17,18,20,25,26,27] def setup(self): '''引脚初始化''' if (self.trig_pin not in self.pins_for_motor) and (self.trig_pin in self.pins_available): print("trig_pin is vaild") else: print("trig_pin is invalid") return False if (self.echo_pin not in self.pins_for_motor) and (self.echo_pin in self.pins_available): print("echo_pin is vaild") else: print("echo_pin is invalid") return False GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(self.trig_pin,GPIO.OUT,initial=GPIO.LOW) GPIO.setup(self.echo_pin,GPIO.IN) return True def getdistance(self): '''轮询方法获取距离''' GPIO.output(self.trig_pin,GPIO.HIGH) time.sleep(0.000015) GPIO.output(self.trig_pin,GPIO.LOW) while not GPIO.input(self.echo_pin): pass t1=time.time() #开始计时 while GPIO.input(self.echo_pin): pass t2=time.time() distance=(t2-t1)*340/2 return distance

代码中获取距离是用简单的轮询方法实现的,也可以使用GPIO中的上/下沿检测加回调方法的手段实现。

5 电机模块

用L298N模块控制马达运动,首先需要了解L298N的工作逻辑。
这里写图片描述
如上图所示,左下角的三个接线柱是12V电源输入、GND和5V输出;左右的接线柱分别控制两个电机,OUT1和OUT2控制左边的马达,OUT3和OUT4控制右边的马达;右下角的接线柱是使能及控制脚,ENA、IN1和IN2为左侧电机的控制组,控制OUT1和OUT2的输出逻辑,ENB、IN3和IN4为右侧控制组,控制OUT3和OUT4。以左侧控制组为例,下表是其真值表:
这里写图片描述
此外,如果给使能脚提供PWM波,则电机转速可调。

正常情况下是一个L298N带2个马达,一左一右,现在小车四个轮子都有马达,因此可以将一侧的两个马达并联,这样一个L298N带4个马达,只要电池电力足够4个马达是可以带动的(当然也可以再加一个L298N)。马达连线时需要注意一侧的两个不要接反了,因为要保证一侧的两个运动方向一致。

小车的运动逻辑还是很简单的,两侧同时正转就是前进,同时反转就是后退,左侧不动右侧正转就是左转弯,右侧不动左侧正转就是右转弯。由此编写电机控制模块的Python文件:

import RPi.GPIO as GPIO import time import sys class Motor_Module(object): '''电机控制模块''' def __init__(self): self.enab_pin=[5,6,13,19] #使能脚编号 self.inx_pin=[21,22,23,24] #控制脚编号 self.RightAhead_pin=self.inx_pin[3] self.RightBack_pin=self.inx_pin[2] self.LeftAhead_pin=self.inx_pin[1] self.LeftBack_pin=self.inx_pin[0] self.setup() def setup(self): '''引脚初始化''' GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) for pin in self.inx_pin: GPIO.setup(pin,GPIO.OUT) GPIO.output(pin,GPIO.LOW) for pin in self.enab_pin: GPIO.setup(pin,GPIO.OUT) GPIO.output(pin,GPIO.HIGH) def ahead(self,secondvalue=0): self.setup() GPIO.output(self.RightAhead_pin,GPIO.HIGH) GPIO.output(self.LeftAhead_pin,GPIO.HIGH) if secondvalue!=0: time.sleep(secondvalue) self.stop() def left(self,secondvalue=0): self.setup() GPIO.output(self.RightAhead_pin,GPIO.HIGH) if secondvalue!=0: time.sleep(secondvalue) self.stop() def right(self,secondvalue=0): self.setup() GPIO.output(self.LeftAhead_pin,GPIO.HIGH) if secondvalue!=0: time.sleep(secondvalue) self.stop() def rear(self,secondvalue=0): self.setup() GPIO.output(self.RightBack_pin,GPIO.HIGH) GPIO.output(self.LeftBack_pin,GPIO.HIGH) if secondvalue!=0: time.sleep(secondvalue) self.stop() def stop(self): for pin in self.inx_pin: GPIO.output(pin,GPIO.LOW)

程序中默认了使能脚连接5、6、13、19号引脚,控制脚连接21、22、23、24号引脚(使能脚上是由跳线帽插着的,连接树莓派时拔掉,这样会有四个引脚,这里把四个脚都连到树莓派上了,其实只需要两个就够了);对于前进、后退、左转和右转方法,可以传入一个时间值,指示运动多长时间。

6 主程序

调试好测距和电机控制,把所有东西组装好,下图是我完整的小车展示,用充电宝为树莓派供电,多个干电池为电机供电,此外我这里还把摄像头接了上去,虽然暂时没有用到。
这里写图片描述
下面需要编写一个主程序来启动小车,我的目的是简单的避障,因此可以考虑让小车直行并不停地检测前方障碍距离,当靠近障碍物时右转一定角度继续前进,下图是主程序的简单流程。
这里写图片描述
根据设计的流程,编写主程序:

讯享网#! /usr/bin/python3 import RPi.GPIO as GPIO import sys import time import Ultrasonic_Module as Ul import Motor_Module as Mo  模块初始化 ultrasonic=Ul.Ultrasonic_Module(25,26) motor=Mo.Motor_Module() print("initing the ultrasonic module...") if not ultrasonic.setup(): print("ultrasonic setup fall , programe stop") exit() print("initing the motor module...") motor.setup()  一开始小车处于前进状态 print("the car start running...") motor.ahead()  循环判断距离 distance=10 limited_d=0.05 #障碍最小距离 time_rear=0.8 #后退时长 time_right=1.2 #右转时长 try: while True: distance=ultrasonic.getdistance() if distance <= limited_d: print("run into blank wall") motor.stop() motor.rear(time_rear) motor.right(time_right) motor.ahead() except KeyboardInterrupt: motor.stop() print("") print("stop the car")

程序中加入一个键盘中断处理,是为了在按下ctrl+c终止程序时及时停止小车。

7 测试

通过ssh连接小车上的树莓派,执行上面的主程序文件启动程序,运行小车。测试结果良好。由于只有一个位于车头的传感器,如果小车侧边撞上障碍物就卡在那了;此外影响小车运行的最大因素是电池,四个马达的负载有点大,干电池组很快就欠压了,所以最好还是得买锂电池。

小讯
上一篇 2025-03-13 13:41
下一篇 2025-03-10 14:35

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/127509.html