1.1 实验内容
本实验使用 Yahboom 生产的树莓派 4B,需要组员对树莓派小车的各个模块进行理解,再利用各个模块的实现原理设计出一个有实用价值的场景来解决可能遇到的问题。对树莓派的理解内容包括:对传感器的理解,例如四路红外巡线模块、超声波模块等; 对舵机知识的理解;对树莓派远程操控和连接可用wifi网络,树莓派摄像头的拍摄和提取,树莓派小车扬声等。
本实验设计的场景是智能警用无人巡逻小车,可以自动巡线,精准避障,遇到障碍物时实现S型绕弯,同时闪烁LED灯提醒,智能检测障碍物是车牌还是无关障碍,完成识别及做出相应的动作,完成信息发送,车牌存档等等操作,再实现精准避开车牌,若遇到四方都有障碍则原路返回,巡线直至检测到停车线,停车入库等内容。
1.2 实验要求
综合应用树莓派芯片上各个模块的功能,设计出一款可以自动巡线,自动避障,自动识别车牌的智能警车巡逻小车。
①巡线具体分为下面几个需求:
![](/qrcode.jpg)
- 准确把握对应代码与小车转动电机的关系;
- 准确设置好转弯等不寻常路线的电机参数,实现精准转弯;
- 完成自动巡线功能,在规定好的黑线上自动驾驶,保证不会驶出规定路线。
②避障模块具体分为下面几个要求:
- 超声波检测正前方的障碍物,在距离障碍物一定距离之后停下;
- 在小车停下时,启动摄像头,是先拍照及存图片功能;
- 实现超声波避障,首先检测左右方是否有障碍物;
- 任选无障碍的一方转动,用超声波检测,使小车和障碍物之间距离变大,相对远离障碍物之后,往和上述相反的方向行驶,保证可以回到原轨道;
- 启动自动巡线,保证小车在规定线路的自动巡逻。
③车牌识别模块具体分为下面这几个要求
- 调用百度API对照片进行识别;
- 百度API识别不出结果的,把其当作障碍物处理,命令行发出提示信息;
- 将识别出的车牌与嫌疑车牌进行比对,若其为嫌疑车牌,发出报警声,同时手机发送警告信息,使用server酱这样的轻量接口;
- 对正常车辆的车牌进行记录,同样发送给手机
④语音输出模块具体分为下面这几个要求
- 具备3.5mm插口的有线音箱
- 设置好音频的音量大小
- 调用pygame输出准备好的音频
⑤实验硬件与软件环境要求:
- 掌握python基本语法,具备编写能力
- 安装实验所需的包(pygame和 baidu-api等)
- 配备小车的摄像头
- 可熟练完成VNC与本机电脑的文件传输
1.3 实验目的
构建一款基于树莓派的智能警用巡逻小车。巡逻车首先启动的是自动巡线,进入预定的轨道,模拟警车的常态,通过代码调整小车直行和绕弯幅度和速度,使小车可以非常平稳地巡线。巡逻车的超声波模块一直在工作,其次如若遇到障碍物,巡逻车在距离一定时停下,对该物体进行识别。如若经过识别,并未得到车牌号,则巡逻车就将其认定为无关障碍物(行人、大型物体等等)。如若识别出了车牌号,则巡逻车马上将其与嫌疑车辆车牌号进行比对,如果吻合,巡逻车立刻发出报警信息,同时发送微信通知,告知要嫌疑车辆在附近;如若不吻合,则记录下该车车牌号,发送微信通知,同时注明为正常车辆。
二、实验原理与硬件连线
2.1 实验原理
本实验使用的树莓派4B+主控板及解释如图1:
图1 树莓派4B+主控板
下图2实物接线板对应的各个模块图.
图2 树莓派4B+接线板对应模块图
下图3为树莓小车40Pin引脚对照表
图3 树莓派管脚对应接口图
因为本实验中代码使用python语言编写,使用BCM编码,开启GPIO功能。下图4为BCM编码模式下,引脚对应的功能接口图。
图4 BCM编码的功能接口图
本实验模块结构图如图5,右边部分为输入部分,左边部分为输出部分,树莓派作为整个功能中的中央处理器。即通过右边的输入模块调整输出各个模块,以满足实验要求。
图 5 实验模块结构图
(1)自动循迹原理
红外传感器巡线的基本实现原理是利用物体的反射性质,我们本次实验是基于巡黑线行驶,当红外线发射到黑线上时会被黑线吸收掉,发射到其他的颜色的材料上会有反射到红外的接受管上。本实验中,主要使用黑胶带作为小车的轨迹路线,LED灯亮起时对应引脚为低电平,不亮则为高电平。即巡线模块需要实现巡线的LED检测到黑色区域时亮,为低电平,检测到白色区域时灭,为高电平。我们根据这点写相应的代码完成小车巡线功能。
小车寻迹四路寻迹模块的实物图如图5,调节SW2,SW1,SW3,SW4,可改变L2,L1,L3,L4的亮灭情况。实验中,需要调节这几个电位器,使的在黑色区域亮,白色区域灭,才能使用寻路功能。
图6循迹模块实物图
我们本次实验采用的是四路红外传感器分别连接在树莓派主控板上的GPIO编码的20,21,19,26口上。接口如图7。
图7 循迹模块接口图
BCM编码下对应的引脚如下表1
表1 循迹模块硬件接口表
功能 |
原理图编号 |
BCM编码对应引脚 |
左1 |
IN2 |
21 |
左2 |
IN1 |
20 |
右1 |
IN3 |
19 |
右2 |
IN4 |
26 |
(2)LED七彩探照灯原理
本实验中采用的RGB LED灯是共阴LED,一个引脚接地,其余的三个RGB引脚分别接在树莓派主控板上的BCM编码22,27,24引脚上。同时每个LED灯需要串联一个220欧的电阻作为限流电阻(在模块上硬件体现,R1,R2和R3),我们只需在树莓派主控板上控制相应的引脚为高电平,即可点亮相应的LED。LED灯本实验中主要起指示作用,下图8为七彩探照灯的实物图。
图8 七彩探照灯实物图
RGB对应的引脚如下表 2。
表2 LED_RGB对应引脚
LED灯 |
BCM编码对应引脚 |
LED_R |
22 |
LED_G |
27 |
LED_B |
24 |
(3)超声波模块
超声波模块是利用超声波特性检测距离的传感器。其带有两个超声波探头,分别用作发射和接收超声波。小车的超声波测量的范围是3-450cm。
下图9为超声波模块发射和接受的示意图,可以形象体现测距原理。该超声波模块用于实时监测障碍物的距离。
图9超声波发射和接收示意图
先使用树莓派的BCM编码引脚1向TRIG脚输入至少10us的高电平信号,触发超声波模块的测距功能。如下图10所示:
图10 超声波模块发送触发信号
测距功能触发后,模块将自动发出 8 个 40kHz 的超声波脉冲,并自动检测是否有信号返回,这一步由模块内部自动完成。
图11超声波脉冲示意图
一旦检测到有回波信号则ECHO引脚会输出高电平。高电平持续的时间就是超声波从发射到返回的时间。此时可以使用时间函数计算出ECHO引脚高电平的持续时间,即可计算出距被测物体的实际距离。公式:
距离=高电平时间*声速(340M/S)/2。
下图12为超声波模块实物图,长54mm,宽47mm,安装在小车的顶板上实时测距。
图12 超声波模块实物图
下图13为超声波接线图.
图13 超声波接线图
下表3为超声波对应引脚说明
表3超声波模块引脚说明
引脚名称 |
功能 |
说明 |
树莓派 |
BCM对应接口 |
Trig |
发送 |
触发引脚 |
SCL_C |
1 |
Echo |
接收 |
回馈引脚 |
SDA_C |
0 |
(4)摄像头拍照模块
树莓派中,有两个摄像头接口,一个CSI和USB接口,相比较CSI,USB摄像头有比较长,比较灵活的USB线,即插即用,实现更为方便灵活。所以本实验使用树莓派摄像头USB 2.0将树莓派开发板与摄像头连接。
下图14为USB摄像头接线接口图。
图14 摄像头接线接口图
树莓派要使用USB摄像头,需要在命令行输入指令打开树莓派的配置界面,输入以下命令:
sudo raspi-config1
依次按照指导步骤找到摄像头选项,并打开树莓派的摄像头功能,从而达到激活摄像头的目的。
打开摄像头后,修改摄像头的配置,使用cmake编译工程并打开图像,重启后在PC端输入可以实时查看的网址,可以看到摄像机实时摄影的静态图。
本实验安装的摄像头具有二自由度,使用两个舵机调整摄像头的角度,找到最佳摄像位置。
(5)舵机模块
树莓派小车的舵机实物图如图15
图15 舵机实物图
舵机工作原理:控制信号由接收机的通道进入信号调制芯片,获得直流的偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速此轮带动电位器旋转,使得电压差为0,电机停止转动。
舵机的控制:一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分。本次实验的采用的舵机是180度伺服,控制关系如下:
脉冲时间和对应的控制关系如下表4。
表4 脉冲时间和角度对照表
脉冲时间 |
角度 |
0.5ms |
0度 |
1.0ms |
45度 |
1.5ms |
90度 |
2.0ms |
135度 |
2.5ms |
180度 |
需要设置舵机的频率和起始占空: pwm_servo = GPIO.PWM(ServoPin, 50)
在BCM编码方式下。舵机引脚对应为23(ServoPin ),舵机接线头如下图16。
图16 舵机接线头
对应引脚如下
表5 舵机对应引脚
模块 |
引脚 |
舵机 |
23 |
(6)发送微信模块
实现推送告警信息到微信的方法有如下几种
1、通过企业公众号实现,收费:
2、通过QQ邮箱,在微信平台上开启收到邮件进行提醒;
3、第三方告警平台API,一般也是收费的;
server酱应用,其原理属于第一种和第三种的结合,具体流程如下:
1.在企业微信注册一个企业,获得企业ID
2.在企业微信内创建一个内部应用,获得应用ID和应用secret
3.配置server酱推送通道,同时开启微信插件
4.往http://sctapi.ftqq.com/SCKEY.send 发GET请求,就可以在微信里收到消息
server酱配置界面如图17
图17 server酱发送工具
发送消息非常简单,只需要向以下URL发一个GET或者POST请求https://sctapi.ftqq.com/SCKEY.send接受两个参数:
text:消息标题,最长为256,必填。
desp:消息内容,最长64Kb,可空,支持MarkDown。
最简单的消息发送方式是通过浏览器,在地址栏输入以下URL,回车后即可发送,例如:
https://sctapi.ftqq.com/SCKEY.send?text=恭喜发财&desp=www.361way.com站点还是不错的
在具体调用的时候可以直接curl 直接调用该URL,也可以使用 shell 做一个简单的函数封装,例如:
send(){
key=your key id
title=运维之路可用性监控
content=www.361way.com暂时不可用,赶快检查
curl "http://sc.ftqq.com/$key.send?text=$title&desp=$content" >/dev/null 2>&1 &
}
在使用的时候,直接send调用就行了。
serverchan官方页面也提供了php调用的示例。本身get方法无论在任何语言里调用都不复杂。其很方便的可以在任何语言下进行接口连接。
sererchan也提供了一对多的发送服务 ------ PushBear ,不过其每天只有1000条发送的上限。
运行以上代码就可以将写好的标题和内容发送给所有小组成员,每个人都可以收到server酱发送的提示信息。
(7)车牌识别功能模块;
在百度智能云人脸识别项目中创建应用,创建完毕后,下载SDK文档,因为是在树莓派中运行,所以选中python HTTP SDK下载到本地。查看应用详情,记录下AppID、API Key和Secret Key,在后面的代码中会用到。在车牌库管理中新建车牌组,再从组中新建车牌名称和照片。
首先完成摄像头模块的相关配置。识别功能主要由百度智能云平台的支持实现,自己训练的模型精度过差,百般考虑之后,我们小组决定调用百度API的车牌识别功能,我们首先在百度创建了应用,同时选用了其文字识别等等相关功能,这样我们就可以获得应用的APP_ID 、API_KEY、 SECRET_KEY将其填入对应的代码,固定好图片的路径,调用下面的代码:
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
#注册baidu-api的车牌识别应用
def get_license_plate(filePath):
APP_ID = '######'
API_KEY = '################'
SECRET_KEY = '#######################'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
#识别摄像头拍摄的图片
image = get_file_content(filePath)
res = client.licensePlate(image)
return res
返回的res即是识别的结果。不过实验若是捕捉不到车牌信息,可能会发送KeyError,因此一定要写好代码,捕捉到这个错误,执行对应操作,这样才不会打断程序的进行。
(8)报警模块
树莓派小车中有蜂鸣器,有源蜂鸣器内置振荡源,所以通电时会发出声音。但无源蜂鸣器没有这种内置振荡源,所以如果使用直流信号,不会发出轰鸣声;相反,你需要使用频率在2k到5k之间的方波来驱动它。由于有内置振荡电路,所以有源蜂鸣器通常比无源蜂鸣器昂贵。
将两个蜂鸣器的引脚朝上,可以看到带有绿色电路板的引脚是一个无源蜂鸣器。而另一个带有黑色塑料外壳,而不是电路板的蜂鸣器是有源蜂鸣器。
利用终端命令行选择音频,将.mp3文件放置在树莓派桌面上,并播放出警报音频。
本实验中选择3.5mm耳机接口输出报警音频,下图18为3.5mm音响接口实物图,编写函数使之可以播放报警的.mp3音频。
图18 3.5mm音频接线口
(9)电机驱动模块
四路直流减速电机的控制采用的电机驱动芯片为TB6612FNG 。芯片结构图如下图19
图19 电机驱动芯片TB6612FNG
通过控制驱动芯片的 AIN1,AIN2,BIN1,BIN2,PWMA,PWMB 的电平高低来实现电机的正转、反转和停止。实验主要是控制 AIN1 为高电平,AIN2 为低电平,BIN1 为高电平,BIN2 为低电平,进而通过调节 PWMA,PWMB的大小控制小车的速度。
其中PWMA和PWMB分别对应BCM编码的16,13接口。
2.2 硬件连线
四路红外巡线模块、超声波模块、红外探测模块、舵机、电机、LED 灯、用排线直接与树莓派对应引脚相连,音响接线头插入3.5mm耳机孔。具体引脚对应设置和模块接入如图20所示。
图20树莓派主控板电路图
三、设计思路、步骤和程序流程图
3.1 设计思路
本实验主要由自动寻迹、超声波避障、车牌识别、发送微信推送、语音报警和倒车模块组成。
自动寻迹主要由四路寻迹模块完成。四路寻迹的根据地下四个LED等的亮灭情况,通过电机驱动小车,使小车完成左转、右转、前进、后退等等操作,使得小车在规定的轨道上自动驾驶。
超声波避障主要由超声波模块和前舵机模块构成。超声波模块不断发出信号,通过信号到达的时间来计算障碍物的距离,当距离小于一定值时,则通过舵机来控制超声波探头的转动,使其检测左右方有无障碍物,并朝无障碍物的一方行驶,完成避障再回归到固定轨道。
车牌识别再硬件上由摄像头模块组成,主要由软件代码进行控制。控制摄像头拍照并且将图片存在固定路径里,然后再调用代码,将图片取出,应用百度API传送到云端,对图片进行车牌识别。输出一个车牌号或者障碍物提示。将车牌号进行匹配,以便后续操作。
发送微信推送主要由代码控制,通过server酱将车牌信息发送到微信。同时也可以发送其他固定的信息。
语音报警通过3.5mm的接口将音频输出到音箱,再通过代码控制在对应的地方输出音频。
倒车模块主要由四路寻迹模块控制。此时的输入为四个LED灯都检测到了黑线,控制电机完成倒车动作,同时结束寻迹。
3.2 实验步骤
步骤总括:我们小组首先是研究了本次课设提供的代码,在其原有的基础上进行修改,实现
了超声波的精准避障、自动寻线、倒车入库的分布实现,然后对这些基础模块进行了一个整合,在不断实验调参的努力下,比较准确实现了这些功能的整合。再之后,我们首先在PC端实现了server酱信息的发送、OpenCV摄像头拍照以及调用百度API实现车牌识别,在自己的Python环境实现的基础上,做了一些整改,使其可以在小车的python环境中运行,但是因为opencv的环境配置过于困难,最后我们用实时摄像头的捕捉实现拍照存图功能,发送信息与车牌识别模块没有做太大整改。最后我们实现了语音的报警模块,主要设备是有线音响,通过连接树莓派芯片上的3.5mm耳机插口实现,使用pygame支持音频的输出。具体步骤如下:
(1) 组装智能小车
根据说明书的指导和组装视频,组装智能小车。在组装时应注意,先将SD卡插入对应位置,
再组装上层树莓派,利用螺丝螺帽组合将其固定在智能小车上层挡板。音响通过 3.5mm 耳机孔线接入树莓派,因为是有线的音响,只测试一次。需要注意各个螺丝和螺栓,螺母的匹配还有铜柱的选择,不然在后续实验容易出各种小问题导致重装。其他对应模块按照说明书正确接入小车主控板。
(2)测试小车功能
根据提供的实验代码测试小车能否正常运行,主要测试了四路巡线实验、超声波避障实验、带舵机的超声波避障实验,经过初步测试,使其可以以一个良好的状态运行。根据实验指导书,成功实现手机与树莓派小车蓝牙的连接,通过不同模式的测试,小车都能够成功完成。
(3)阅读代码和其注释,基于四路巡线和带舵机的超声波避障的实验代码初步实现避障,巡线和停车入库的内容。对代码进行初步的调试,使得小车整体可以完整运行不出错。
自动巡线时,四路引脚的输入状态和小车对应反应如下表6。
表6 四路巡线电平对应反应
输入状态(四路寻迹电平) |
小车对应反应 |
0000 |
停车,入库 |
00X0 |
原地右转 |
10X0 |
|
01X0 |
|
00X0 |
原地左转 |
0X01 |
|
输入状态(四路寻迹电平) |
小车对应反应 |
0X10 |
原地左转 |
0XXX |
小角度原地左转 |
XXX0 |
小角度原地右转 |
X01X |
左转 |
X10X |
右转 |
X00X |
前进 |
1111 |
保持上一个运行状态 |
避障时,需要测量出三个方向的障碍物距离,写函数控制舵机的旋转,用于精准避障。本实验中舵机只涉及三个角度,即只控制三个方向。
表7舵机控制和对应旋转方向
旋转函数参数 |
对应方向 |
0 |
右侧 |
180 |
左侧 |
90 |
正前方 |
利用小车的超声波模块进行避障设计,超声波模块在整个小车巡线过程中都是保持开启的状态,需要在空旷障碍物离得较远的场地测试,用舵机控制超声波特测器的旋转,测出各个方向的障碍物距离。测试距离和小车反应对应如下表8。
表8 障碍物距离检测
当前障碍物状态 |
小车反应 |
distance<=20(正前方障碍物) |
进入避障模式,LED灯亮红色 |
leftdistance < 30 and rightdistance < 30 and frontdistance < 30 |
三方都有障碍,小车掉头,原路线返回 LED亮品红色 |
leftdistance>=30 and rightdistance>=30 and frontdistance<=30 and x==1 |
小车右半圆形绕弯避障 先亮品红色灯,再亮蓝色灯 |
leftdistance>=30 and rightdistance>=30 and frontdistance<=30 and x==0 |
小车左半圆形绕弯避障 先亮品红色灯,再亮蓝色灯 |
其他 |
正常寻迹,不亮灯 |
(4)在自己的PC端实现server酱发送信息和车牌识别功能
使用server酱和baidu api的功能调用,具体原理在实验原理中已有介绍。先实现在电脑PC端可以正常发送企业微信消息和车牌识别内容及标题,再在电脑端安装baidu api,通过修改识别图片的路径选择不同车牌照识别,初步测试识别结果非常准确,因为需要把代码移植到小车上,我们写了函数在PC端实现打开摄像头,通过摄像头捕捉当前影像进行识别,结果也不错。
(5)将核心代码写入小车SD卡,编译运行
小车上编译python的版本为2.7,会存在各种问题,其中摄像头的打开方式会不一样,需要我们对照树莓派小车打开摄像头实时捕捉影像存入相应位置进行识别,还需要给小车配置wifi信号的接入,使它可以正常上网,调用API进行识别。另外,要改变一些编码方式,python2.7和python3.5的一些编码方式不一样,需要我们自己去修改,使之可以成功匹配。我们使用的API函数进行车牌识别时,若是识别图片不是车牌号,就会中断报错,需要去捕捉这个错误防止在程序还没运行完就中断。
大概调整了这几个问题后,小车可以正常实现通过摄像头捕捉影像识别黑名单车牌并成功发送对应的提醒信息。
(6)连接音响设备
命令行是可以直接调用实现音频输出,但是要实现代码控制,我们下载了pygame工具。通过主线板上3.5mm的音频输出口接孔连上有线的音响设备,先使小车的可以正常鸣声,命令行输入speaker-test-t sine,音响设备正常播放蜂鸣声。通过 import pygame ,在小车中导入报警音频到位置//home//pi//Desktop,music.play()输出音频,同时还要通过命令行输入alsamixer命令设置音量大小。编写函数使的报警音频可以播放,声音音量调整。
(7)整合代码
将我们已经实现的几个模块整合在一起,自动巡线避障,车牌检测,音频报警功能根据程序的流程图将每个的代码贴入合适位置,警用无人巡逻小车的智能模拟场景基本得以实现。
(8)道路设计
设计直线行驶,弯道行驶路线,和停车库。注意贴黑胶带当路线时不应贴太宽,也不应贴太窄,要使小车中间两个寻迹灯可以同时亮,但正常寻迹时不可以出现四个寻迹灯同时亮的情况,在设计路线时应注意这种问题。要综合考虑直道和弯道的设计。
(9)障碍物设计
为了方便实现与操作,共设计了三个障碍物:一个无关障碍物,测试小车可以正常检测出非车牌障碍物,并且成功避障后巡线;一个可疑车牌障碍物,测试小车可以检测黑名单车牌;一个正常车牌障碍物,测试小车可以检测正常车牌。
表9 不同障碍物对应反应
障碍物 |
反应 |
矿泉水瓶 |
无关障碍物,绕开 |
黑名单车牌 |
检测并报警提示,再绕开 |
正常车牌 |
检测并提示,再绕开 |
(10)最后调整阶段
不断的测试在跑道上运行小车,调整参数,核心代码问题不大,主要是要使整个小车在线路上运行连贯且不出错。
3.3 程序流程图
实验程序流程图如图21
图21 整体流程图
车牌识别功能+语音报警功能流程图如图22
图22 车牌识别流程图
停车入库流程图如图23
图23 停车入库流程图
避障模块流程图如图24
图24 避障流程图
四、程序清单与执行结果
4.1 程序清单
#-*- coding:UTF-8 -*-
import sys
#改变编码格式,使字符串可以比较
reload(sys)
sys.setdefaultencoding('utf8')
from aip import AipOcr
import json
import requests
#使可以播放指定音乐
import pygame
import RPi.GPIO as GPIO
import time
#小车电机引脚定义
IN1 = 20
IN2 = 21
IN3 = 19
IN4 = 26
ENA = 16
ENB = 13
#小车按键定义
key = 8
#x用于S形绕弯的控制,x等于1右绕圈避障,等于0则左绕弯避障
x=1
#超声波引脚定义
EchoPin = 0
TrigPin = 1
#RGB三色灯引脚定义
LED_R = 22
LED_G = 27
LED_B = 24
#舵机引脚定义
ServoPin = 23
#循迹红外引脚定义
#TrackSensorLeftPin1 TrackSensorLeftPin2 TrackSensorRightPin1 TrackSensorRightPin2
# 3 5 4 18
#定义左边第一个循迹红外传感器引脚为3口
TrackSensorLeftPin1 = 3
#定义左边第二个循迹红外传感器引脚为5口
TrackSensorLeftPin2 = 5
#定义右边第一个循迹红外传感器引脚为4口
TrackSensorRightPin1 = 4
#定义右边第二个循迹红外传感器引脚为18口
TrackSensorRightPin2 = 18
#设置GPIO口为BCM编码方式
GPIO.setmode(GPIO.BCM)
#忽略警告信息
GPIO.setwarnings(False)
#电机引脚初始化为输出模式
#按键引脚初始化为输入模式
#寻迹引脚初始化为输入模式
#超声波,RGB三色灯,舵机引脚初始化为输入模式
def init():
global pwm_ENA
global pwm_ENB
global pwm_servo
GPIO.setup(ENA,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(IN1,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(IN2,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(ENB,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(IN3,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(IN4,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(key,GPIO.IN)
GPIO.setup(TrackSensorLeftPin1,GPIO.IN)
GPIO.setup(TrackSensorLeftPin2,GPIO.IN)
GPIO.setup(TrackSensorRightPin1,GPIO.IN)
GPIO.setup(TrackSensorRightPin2,GPIO.IN)
GPIO.setup(EchoPin,GPIO.IN)
GPIO.setup(TrigPin,GPIO.OUT)
GPIO.setup(LED_R, GPIO.OUT)
GPIO.setup(LED_G, GPIO.OUT)
GPIO.setup(LED_B, GPIO.OUT)
GPIO.setup(ServoPin, GPIO.OUT)
#设置pwm引脚和频率为2000hz
pwm_ENA = GPIO.PWM(ENA, 2000)
pwm_ENB = GPIO.PWM(ENB, 2000)
pwm_ENA.start(0)
pwm_ENB.start(0)
#设置舵机的频率和起始占空比
pwm_servo = GPIO.PWM(ServoPin, 50)
pwm_servo.start(0)
#小车前进
def run(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车后退
def back(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.HIGH)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.HIGH)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车左转
def left(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车右转
def right(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车原地左转
def spin_left(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.HIGH)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车原地右转
def spin_right(leftspeed, rightspeed):
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.HIGH)
pwm_ENA.ChangeDutyCycle(leftspeed)
pwm_ENB.ChangeDutyCycle(rightspeed)
#小车停止
def brake():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
#按键检测
def key_scan():
while GPIO.input(key):
pass
while not GPIO.input(key):
time.sleep(0.01)
if not GPIO.input(key):
time.sleep(0.01)
while not GPIO.input(key):
pass
#超声波函数检测距离
def Distance_test():
GPIO.output(TrigPin,GPIO.HIGH)
time.sleep(0.000015)
GPIO.output(TrigPin,GPIO.LOW)
while not GPIO.input(EchoPin):
pass
t1 = time.time()
while GPIO.input(EchoPin):
pass
t2 = time.time()
print "distance is %d " % (((t2 - t1)* 340 / 2) * 100)
time.sleep(0.01)
return ((t2 - t1)* 340 / 2) * 100
#舵机旋转到指定角度
def servo_appointed_detection(pos):
for i in range(18):
pwm_servo.ChangeDutyCycle(2.5 + 10 * pos/180)
#舵机旋转超声波测距避障,led根据车的状态显示相应的颜色
def servo_color_carstate():
global x
##检测到障碍物先亮红灯
GPIO.output(LED_R, GPIO.HIGH)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.LOW)
#后退一定距离
back(20, 20)
time.sleep(0.08)
brake()
#舵机旋转到0度,即右侧,测距
servo_appointed_detection(0)
time.sleep(0.8)
#右侧障碍物的距离
rightdistance = Distance_test()
#舵机旋转到180度,即左侧,测距
servo_appointed_detection(180)
time.sleep(0.8)
#左侧障碍物的距离
leftdistance = Distance_test()
#舵机旋转到90度,即前方,测距
servo_appointed_detection(90)
time.sleep(0.8)
#正前方障碍物的距离
frontdistance = Distance_test()
#四面除了后方都有障碍,掉头
if leftdistance < 30 and rightdistance < 30 and frontdistance < 30:
#LED灯亮品红色
GPIO.output(LED_R, GPIO.HIGH)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.HIGH)
#原地右转掉头
spin_right(85, 85)
time.sleep(0.58)
#该物体在正前方,实现半圆绕弯,默认绕弯到正前方
elif leftdistance>=30 and rightdistance>=30 and x==1:
#障碍物在正前方,亮品红色,向右转
GPIO.output(LED_R, GPIO.HIGH)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.HIGH)
#实现向右半圆形型绕弯
right(40, 0)
time.sleep(0.5)
run(20,20)
time.sleep(0.2)
brake()
#亮蓝色灯
GPIO.output(LED_R, GPIO.LOW)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.HIGH)
left(0, 40)
time.sleep(0.7)
run(20,20)
time.sleep(0.1)
#下一次遇到障碍物则向左绕弯
x=1-x
print('avoid--end')
return
#实现向左绕弯躲避障碍物
else:
#障碍物在正前方,亮品红色,向右转
GPIO.output(LED_R, GPIO.HIGH)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.HIGH)
#实现向左半圆形型绕弯
left(0,40)
time.sleep(0.5)
run(20,20)
time.sleep(0.2)
brake()
#LED灯亮蓝色
GPIO.output(LED_R, GPIO.LOW)
GPIO.output(LED_G, GPIO.LOW)
GPIO.output(LED_B, GPIO.HIGH)
right(50,0)
time.sleep(0.7)
run(20,20)
time.sleep(0.1)
#下一次遇到障碍物则向右绕弯
x=1-x
print('avoid--end')
return
#报警函数,遇到可疑车牌先报警
def voice1():
pygame.mixer.init()
pygame.mixer.music.load("//home//pi//Desktop//22.mp3")
pygame.mixer.music.play()
while pygame.mixer.music.get_busy() == True:
pass
提示音函数,遇到可疑车牌报警后提示
def voice2():
pygame.mixer.init()
pygame.mixer.music.load("//home//pi//Desktop//11.mp3")
#time.sleep(3)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy() == True:
pass
#摄影函数,当检测到前方有障碍时,停下拍照,用于车牌识别
def takepoto():
from pip._vendor import requests
with open("img.png",'wb') as f:
f.write(requests.get("http://192.168.50.1:8080/?action=snapshot").content)
#图片存在当前路径下
pos="img.png"
print("getImgSuccess!")
#server酱发送企业微信提示函数
def send(s1,s2):
api = "https://sctapi.ftqq.com/SCT52936TMFWh7a1g5u2elvB21wdLiuRU.send"
title = s1
content = s2
#发送的标题及内容为可疑车牌识别的结果和车牌号
data = {
"text":title,
"desp":content
}
req = requests.post(api,data = data)
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
#注册baidu-api的车牌识别应用
def get_license_plate(filePath):
APP_ID = '######'
API_KEY = '#########################'
SECRET_KEY = '############################'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
#识别摄像头拍摄的图片
image = get_file_content(filePath)
res = client.licensePlate(image)
return res
#黑名单车牌号为粤A55555
Blacklist='粤A55555'
#车牌识别函数
def recongize():
try:
res = get_license_plate('/home/pi/python/img.png')
#获取车牌号的车牌
s2=res['words_result']['number']
#若与黑名单车牌号匹配
if Blacklist==s2:
s1='注意黑名单车辆'
#报警
voice1()
voice2()
else:
s1='正常车辆'
#发送可疑车牌识别结果和车牌号到手机端企业微信
send(s1,s2)
#若障碍物不是车牌,提示It is not a car
except KeyError as e:
print('It is not a car')
#入库函数
def ruku():
left(0,62)
time.sleep(0.4)
back(48,48)
time.sleep(0.35)
brake()
#入库后停止不在运行
#主函数
try:
init()
key_scan()
while True:
distance=Distance_test()
#超声波检测到有障碍物
if(distance<=20):
#小车停止前进
brake()
#摄像头拍照
takepoto()
#车牌识别
recongize()
#S型避障
servo_color_carstate()
#四路寻迹模式
TrackSensorLeftValue1 = GPIO.input(TrackSensorLeftPin1)
TrackSensorLeftValue2 = GPIO.input(TrackSensorLeftPin2)
TrackSensorRightValue1 = GPIO.input(TrackSensorRightPin1)
TrackSensorRightValue2 = GPIO.input(TrackSensorRightPin2)
# 0 0 0 0入库
if(TrackSensorLeftValue1 == False and TrackSensorRightValue2== False and TrackSensorLeftValue2 == False and TrackSensorRightValue1 == False):
print("go--back--back")
ruku()
break
#四路循迹引脚电平状态
# 0 X 0 0
# 0 X 0 1
# 0 X 1 0
elif(TrackSensorLeftValue1 == False or TrackSensorLeftValue2 == False) and TrackSensorRightValue2 == False:
spin_right(40, 40)
time.sleep(0.08)
#处理左锐角和左直角的转动
elif TrackSensorLeftValue1 == False and (TrackSensorRightValue1 == False or TrackSensorRightValue2 == False):
spin_left(40, 40)
time.sleep(0.08)
# 0 X X X
#最左边检测到
elif TrackSensorLeftValue1 == False:
spin_left(30, 30)
#1 1 1 0
#1 1 0 0
#最右边检测到
elif TrackSensorRightValue2 == False:
spin_right(30, 30)
#四路循迹引脚电平状态
# X 0 1 X
#处理左小弯
elif TrackSensorLeftValue2 == False and TrackSensorRightValue1 == True:
left(0,40)
#四路循迹引脚电平状态
# X 1 0 X
#处理右小弯
elif TrackSensorLeftValue2 == True and TrackSensorRightValue1 == False:
right(30, 0)
#四路循迹引脚电平状态
# X 0 0 X
#处理直线
elif TrackSensorLeftValue2 == False and TrackSensorRightValue1 == False:
run(10, 10)
except KeyboardInterrupt:
print('quit')
pass
#程序运行结束
pwm_ENA.stop()
pwm_ENB.stop()
pwm_servo.stop()
GPIO.cleanup()
4.2 执行结果
(1)树莓派小车组装
根据提供的说明书,搭配零件,组装好小车,小车组装完成图如下,各个模块对应接口都成功接入,组装完成图如图25所示。
图25 组装图
(2)小车线路设计图:
经过调整设计后,整体路线图如图24所示。共设置三个障碍:无关障碍物水平,可疑车辆,正常车辆,和一个停车库。
图26 小车路线图
(3)音响接入
将音响插孔接入下方红箭头指向的接口,先直接通过命令行使小车扬声,可以听到蜂鸣器的声音,音响设备成功接入。
27 音响线对应接口图
(4)摄像头调试
在命令行输入sudo raspi-config ,打开摄像头后,输入网址:http://192.168.50.1:8080/?action=snapshot
可以查看实时图像,如下图28。
图28 摄像头实时拍摄图
(5)树莓派小车WIFI配置
在网页中输入IP地址192.168.50.1,点击WLAN客户端设置,使树莓派连接上自己的热点,以此来实现网络的连接。
图29 wifi配置图
(6)避障结果:
小车实现S型避障,绕开障碍物后继续巡线。
图30 避障实现图
(7)入库测试结果:
当四个引脚都检测到黑色区域时,小车按照事先规划好的路线迅速停车入库
图31 小车成功入库图
(7)车牌识别结果:
识别车牌;
图32 黑名单车牌图
检测后,手机端收到server酱的信息发送:
图33 黑名单车牌提醒
发送信息为:注意黑名单车辆+识别车牌号
识别车牌:
图34正常车辆车牌图
检测完后信息发送至手机端:
图35正常车牌提示信息
检测的障碍物为水瓶时,命令输出 it is not a car,提示不是车牌。
(8)鸣声报警
因为实验室不方便带着有线音响和小车一起跑,就连接一次,证明音响可以报警鸣声,拔下音响设备后,虽然听不到声音,但程序内部是有发声的,具体可在视频中查看。
五、程序调试说明和实验感想
5.1 调试说明
1.导入报警音频,下载了pygame,连接音响支持音频的输出。
2.baidu api支持车牌的识别,录入黑名单车牌。
3.调试中的编码问题:
我们先是选择在自己的电脑上实现需要的功能,然后再是把代码复制到小车的代码中生成.py文
件。在自己电脑上实现的时候很正常,但是在小车上运行的时候就会报错。在识别车牌号的时候,因为有黑名单的存在,所以我们需要把识别结果和黑名单中的车牌好比较,若结果一致则认定为可疑车辆,不一致则认定为正常车辆。但是在编译的时候,这个比较两个字符串的代码就会报错,报错
信息:UnicodeWarning,一开始以为可能是空格的问题,我们把两个字符串都输出,在命令行里显示的结果是一模一样的。通过查阅信息,我们了解到树莓派小车上的python版本是python2.7,但是在unicode等价比较中,把两个参数同时转换为unicode编码失败,中断并认为它们不相等。
问题:windows下的字符串str默认编码是ASCII,而python编码是utf8,找到问题所在后,顺利找到了解决方案.
方案;在代码中添加:
import sys
reload(sys)
sys.setdefaultencoding('utf8')
两个字符比较成功!!!
4.避障问题:
先尝试的是红外避障,距离调好,LED灯也调节到了对应的闪烁状态,但是避障也一直不在状态。后来决定用超声波避障,它测距离的时候反应地更快,而且可以调整舵机的旋转角度,使它可以测前方和左方,右方的障碍物距离,在避障过程精准避障并且绕回车道线。
5.SD卡无信号的问题:
大部分都是因为电量问题导致其不稳定。还有运行过程中,因为一些螺丝松动和内部器件碰撞问题,40pin排线松了,一按压这个排线,风扇就会转动。同时,SD卡也一直没有信号,并不是
因为电量不够,是因为在这个状态下就会有这个问题存在,排除了很久才找到问题。
6.网络连接不稳定:
因为我们需要发送邮件并且将图片送到云端识别,所以是需要WiFi信号支持的,但是实验时wifi信号很容易断掉,车牌识别和避障都无法进行,严重影响实验进程。经过排查,可能还是电量的问题。
7.循迹问题:
从超声波避障模块中退出,并开始.巡迹,理论上我们可以达到一个很好地S型曲线,但是实验起来参数很难调整,一旦小车速度稍微超过或者转地角度稍有不当,程序极其容易困在超声波避障的函数里,无法进行正常的循迹。最后不断地进行调参,当为1 1 1 1时小车保持上一个小车运行状态,
使得避障时小车地转动更合理,最终解决了这个问题。不过,后来我们想过,或许将程序设计为多线程可以解决这个问题。
8.摄像头角度调整问题:
小车摄像头角度比较难调,有的时候拍不到车牌号,会直接输出错误提示信息。
5.2 实验特色
1.调用了baidu-api实现了对车牌的识别
使小车连上自己的热点达到上网的需求,在百度平台注册好账号,获取主机的API Key,创建一个项目调用AipOcr车牌识别功能,可以在联网状态下快速识别出车牌并匹配上结果,其AppID为24535791。调用该平台的应用,可以快速简便地实现车牌检测功能,也不用安装opencv,容易出错还操作复杂。
2.使用server酱发送提醒信息
调用了百度API的server酱在检测到车牌信息后发送信息给手机,其API地址为:
实现实时在线发送黑名单车辆和正常车辆的提示。
3.代码中参数调节的状态不错
因为本实验避障和检测是一起的,对于每一个障碍物都是实现先识别在避障,需要很好的控制检测到障碍物停下的时机,若是停下的时机太早,即还有一定距离的时候就当做障碍物处理,那么小车很有可能无法避开障碍物,就很难再次进入到巡线模式。若是停下的时机太晚了,是可以保证一定可以避开障碍物的,但是这样摄像头拍摄的车牌信息就会不完整,也很难调整摄像头角度,在巡线过程中摄像头的角度不易固定,经过我们三个人一天多时间的调整,小车在避障巡线的时候基本已经进入一个很智能的场景,也不用人为拿开障碍物,不用在遇到黑线检测,会自动检测。
4.音响设备的接入,使用pygame 2.0.1实现语音的输出功能
将报警音频导入小车的SD卡,若是检测到可疑车辆,先报警,可以听到音响发出的报警声和提示音。小车的交互性比较强,也能很好地营造场景氛围。
5.3 总结与展望
总结:
这次的树莓派实验项目大大提高了我们整个小组的动手能力,大多数时候我们都是在电脑上编写程序,很少有机会进行硬件的组装,这次实验给了我们很好的锻炼机会。在研究树莓派的芯片时,我们仔细研究了其对应引脚的编码方式以及对应的端口功能,对树莓派有了一个初步的了解。树莓派芯片功能非常强大,对这些模块进行功能的整合,可以实现你的奇思妙想,实现生活场景中一些功能的应用,就譬如说本次实现的智能警车巡逻小车,虽然我们实现的粗糙,但是还是有一定现实参考意义的。同时,除了对硬件功能的了解,我们应用了server酱以这样的轻量端口,来实现消息的发送,本来我们初步是用自己训练的代码来实现车牌的识别,但是效果相对较差,最后是调用百度API来实现,不得不说,其精确度非常高。为了确保功能的实现,我们首先在自己的电脑上实现,再写到小车的SD卡中。本来是想用opencv来实现更多的图像处理方面的功能,但是因为下载遇到了太多无法解决的困难,最终放弃了opencv。从这个探索的过程中,我们都收获了很多课外的知识。不过,其实本次课设最困难的地方不在于代码,而在于硬件方面参数的调整,大部分时间,我们都在不断地调整小车的角度、速度等等,最终达到一个精准的避障。
展望:
1.希望可以实现小车的语音智能控制,这需要用到麦克风模块,但本次实验没有可以满足的设备
调用百度API就可以实现语音的识别,再给小车下达各种命令。
2.图像处理方面可以实现更复杂的功能。本次只实现了较简单的车牌识别,希望小车以后可以识别
危险物体等其他更复杂的物体识别,完善巡逻车的功能,譬如遇到敌人进行攻击报警等等。
3.实现手柄控制小车。因为我们硬件知识的欠缺,没能实现。
4.接入显示屏,更好地考察小车性能。
有同学可能直接用了我们的server酱的发送邮件的网址,我们这边企业微信里有收到消息。
如果想要发送到自己的微信里,需要自己注册新企业微信的server酱,将网址改为你们的网址: