硬件:Arduino UNO开发板,HC-SR04超声波模块,PS2无线遥控器以及接收器,L298N电机驱动模块,坦克车套件git
软件:Arduino IDE,串口调试助手github
供电:稳压模块,7.4V充电锂电池安全
做为物联网专业的同窗,并且作过好几个物联网系统项目,却一次都没有写过硬件项目的博客,实属惭愧。因此想着先写个咱们实验室大一新生都会作的智(智)能(障)车,以本文做为个人硬件博客处女座,若有遗漏错误,欢迎指正。多线程
本文将从功能分析、系统设计、系统实现和系统测试四个方面对项目进行详细说明,文末附上源码。架构
1)PS2无线游戏手柄远程控制坦克车的前、后、左、右方向的运动,而且操做稳定、流畅函数
2)操控过程当中为避免与物体碰撞采用超声波传感器测距,准确、快速避障,功能架构图以下图所示:工具
从电路设计的角度来讲,整个系统应该分红两个电路部分:开发板与传感器部分、坦克车部分oop
1)PS2无线手柄的接收器与Arduino UNO进行链接,负责接收遥控器端传来的指令,链接图与引脚表以下:性能
PS2接收器引脚(编号1-9) | Arduino UNO 引脚 |
---|---|
1 | 13 |
2 | 11 |
4 | GND |
5 | 5V |
6 | 10 |
7 | 12 |
2)超声波传感器与Arduino UNO相连,采集测距数据,链接图和引脚表以下:测试
HC-SR04超声波传感器引脚 | Arduino UNO 引脚 |
---|---|
Trig | 8 |
Echo | 7 |
VCC | 3.3V |
GND | GND |
3)电机驱动模块L298N与Arduino UNO相连,经过控制电机驱动模块来控制电机转动,同时须要给电机和开发板进行供电,L298N、arduino、电源模块链接图以下:
4)坦克车的组装和各硬件的链接,如图所示:
这部分主要是讲述Arduino功能代码的实现和相关模块的原理
原理:单片机控制超声波发射引脚Trig向某一方向发射超声波,在发射的同时开始计时;超声波在空气中传播,在传播过程当中遇到障碍物后当即折返,超声波接收引脚Echo收到反射波后当即中止对传播计时。根据定理,声波在空气中传播速度为340m/s,使用计时器记录在声波从发射到接收到整个过程在空气中的传播时间t,便可算出发射点距离障碍物的距离S,计算公式为S=340m/s*t/2,所运用的方法是时间差测距法。算出距离后,将数值处理用于坦克智能车的避障,防止坦克智能车与障碍物进行碰撞,保障小车和障碍物的安全。
关键代码:
//--- Arduino 超声波测距代码 --- #define TrigPin 9 //发出超声波 #define EchoPin 8 //收到反射回来的超声波 float cm; //由于测得的距离是浮点型的 void setup() { Serial.begin(57600); pinMode(TrigPin, OUTPUT); pinMode(EchoPin, INPUT); } void loop(){ digitalWrite(TrigPin, LOW); //低高低电平发一个短期脉冲去TrigPin delayMicroseconds(2); // delayMicroseconds在更小的时间内延时准确 digitalWrite(TrigPin, HIGH); delayMicroseconds(10); digitalWrite(TrigPin, LOW); //经过这里控制超声波的发射 cm = pulseIn(EchoPin, HIGH) / 58.0; //将回波时间换算成cm cm = (int(cm * 100.0)) / 100.0; //保留两位小数 Serial.print("Distance:"); Serial.print(cm); Serial.println("cm"); if (cm < 20) { Serial.println("Wranning!");//距离太近,开始自动后退避障 digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); delay(1000); //向后1S后中止 digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } delay(1000); }
原理:接收器上电后,与PS2无线手柄直接配对,接收器将接收到的信号进行解析,解析过程封装与PS2X_lib.h第三方库中。解析后Arduino 经过库中的函数发送控制命令,控制坦克智能小车的运动。
关键代码:
//--- Arduino PS2无线红外遥控代码 --- #include <PS2X_lib.h> //这个库文件请在github上下载后解压放在arduino的库文件夹中 PS2X ps2x; // 建立PS2控制类 int error = 0; byte type = 0; byte vibrate = 0; void setup() { Serial.begin(57600); //串口使能 delay(300); //延时300ms,让接收器上电后正常工做 error = ps2x.config_gamepad(13,11,10,12, true, true); //PS2接收器引脚设置 ps2x.read_gamepad(false, vibrate); type = ps2x.readType(); switch (type) { case 0: Serial.println("Unknown Controller type"); break; case 1: Serial.println("DualShock Controller Found"); break; case 2: Serial.println("GuitarHero Controller Found"); break; } } void loop(){ if (error == 1) //skip loop if no controller found return; if (type == 2) { //Guitar Hero Controller return; } else { //DualShock Controller ps2x.read_gamepad(false, vibrate); if (ps2x.Button(PSB_START)) { //开始键,按下后启动控制 digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_UP)) { //按钮上键 digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_RIGHT)) { //按钮右键 digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_LEFT)) { //按钮左键 digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_DOWN)) { //按钮下键 digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } if (ps2x.Analog(PSS_LY) < 127) { //摇杆上键 digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Analog(PSS_LY) > 127) { //摇杆下键 digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } if (ps2x.Analog(PSS_LX) < 128) { //摇杆右键 digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } if (ps2x.Analog(PSS_LX) > 128) { //摇杆左键 digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if(ps2x.Analog(PSS_LX)==128&ps2x.Analog(PSS_LY)==127){ //不控制则中止 Serial.println("STOP"); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } vibrate = ps2x.Analog(PSAB_BLUE); } delay(50); }
#include <PS2X_lib.h> #define IN1 4 #define IN2 5 #define IN3 6 #define IN4 7 #define TrigPin 9 //发出超声波 #define EchoPin 8 //收到反射回来的超声波 float cm; //由于测得的距离是浮点型的 PS2X ps2x; int error = 0; byte type = 0; byte vibrate = 0; void distance(); void PS2(); void setup() { Serial.begin(57600); pinMode(TrigPin, OUTPUT); pinMode(EchoPin, INPUT); delay(300); error = ps2x.config_gamepad(13, 11, 10, 12, true, true); type = ps2x.readType(); switch (type) { case 0: Serial.println("Unknown Controller type"); break; case 1: Serial.println("DualShock Controller Found"); break; case 2: Serial.println("GuitarHero Controller Found"); break; } delay(3000); } void loop() { PS2(); distance(); } void PS2() { digitalWrite(TrigPin, LOW); //低高低电平发一个短期脉冲去TrigPin delayMicroseconds(2); // delayMicroseconds在更小的时间内延时准确 digitalWrite(TrigPin, HIGH); delayMicroseconds(10); digitalWrite(TrigPin, LOW); //经过这里控制超声波的发射 cm = pulseIn(EchoPin, HIGH) / 58.0; //将回波时间换算成cm cm = (int(cm * 100.0)) / 100.0; //保留两位小数 Serial.print("Distance:"); Serial.print(cm); Serial.println("cm"); if (cm < 20) { Serial.println("Wranning!");//距离太近,开始自动后退避障 digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); delay(1000); //向后1S后中止 digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } } void distance() { if (error == 1) //skip loop if no controller found return; if (type == 2) { //Guitar Hero Controller return; } else { //DualShock Controller ps2x.read_gamepad(false, vibrate); if (ps2x.Button(PSB_START)) { digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_UP)) { digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_RIGHT)) { digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_LEFT)) { digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Button(PSB_PAD_DOWN)) { digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } if (ps2x.Analog(PSS_LY) < 127) { //UP Serial.println("up"); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Analog(PSS_LY) > 127) { //DOWN Serial.println("DOWN"); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); } if (ps2x.Analog(PSS_LX) < 128) { //LEFT Serial.println("LEFT"); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); } if (ps2x.Analog(PSS_LX) > 128) { //RIGHT Serial.println("RIGHT"); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); } vibrate = ps2x.Analog(PSAB_BLUE); } delay(50); }
系统整合后,须要将各部分功能模块进行测试,在计算机实际运行环境下,与硬件、软件、数据和人员等其余因素结合起来,对系统进行一系列的集成测试。从测试的过程当中发现系统中的错误,测试是为了发现问题,更好的解决问题,从而让系统的性能更加优秀,能适应更复杂的使用环境,让系统更加稳定、高效。
本系统的发送数据是采用红外远程遥控发送控制指令,经过Serial串口进行数据测试,测试采用白盒测试的方法。测试的结果以下表所示。
|
正常输入 |
正常输出 |
---|---|---|
白盒测试 |
按压无线PS2手柄up键 |
Arduino单片机串口输出:UP |
按压无线PS2手柄down键 |
Arduino单片机串口输出:DOWN |
|
按压无线PS2手柄left键 |
Arduino单片机串口输出:LEFT |
|
按压无线PS2手柄right键 |
Arduino单片机串口输出:RIGHT |
|
超声波传感器 |
Distance:xxcm |
小车的控制测试过程严格按照如下方案执行:
测试方案:使用红外控制手柄,操做无线手柄将控制指令数据输入Arduino开发板中,Arduino红外接收装置收到模块检测信息,根据收到的检测信息和超声波传感器数据作出相应的处理,将处理信息经过Serial串口调试工具显示出来,串口端口号为57600。
测试结果:操做无线手柄后,在Arduino端收到检测的数据处理,控制坦克智能车开始运动,运动包括:UP前进、DOWN后退、LEFT左转、RIGHT右转。遇到与障碍物之间小于20cm的距离时,执行后退一秒的动做。
性能结果总结:使用红外无线PS2手柄远程控制智能坦克车的先后左右运动功能正常,避障反应敏捷。测试结果与实际状况相吻合。测试效果图以下图所示。
本次项目比较简单,是我本科毕业设计的一部分,里面主要须要注意的就是电源供电设计和遥控接收,没有什么技术含量,几乎都是现成的库函数和代码,只须要根据你本身的需求来进行代码整合便可。
过程当中考虑到遥控器接收和HC-SR04超声波传感器是同时运行,因此采用过多线程的形式,发现Arduino的多线程支持并不完美,而且就这个项目而言只有两个功能,用轮询的方式来执行函数也足够用了。
开发过程当中遇到的一点问题:为何每次从新上电后都须要按下板载Reset按钮,PS2红外接收功能才能正常运行?如何解决?