基于Arduino的红外遥控智能坦克车设计与开发
硬件:Arduino UNO开发板,HC-SR04超声波模块,PS2无线遥控器以及接收器,L298N电机驱动模块,坦克车套件
软件:Arduino IDE,串口调试助手
供电:稳压模块,7.4V充电锂电池
前言
作为物联网专业的同学,而且做过好几个物联网系统项目,却一次都没有写过硬件项目的博客,实属惭愧。所以想着先写个我们实验室大一新生都会做的智(智)能(障)车,以本文作为我的硬件博客处女座,如有遗漏错误,欢迎指正。
本文将从功能分析、系统设计、系统实现和系统测试四个方面对项目进行详细说明,
功能分析
1)PS2无线游戏手柄远程控制坦克车的前、后、左、右方向的运动,并且操作稳定、流畅
2)操控过程中为避免与物体碰撞采用超声波传感器测距,准确、快速避障,功能架构图如下图所示:
系统设计
从电路设计的角度来说,整个系统应该分成两个电路部分:开发板与传感器部分、坦克车部分
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无线红外手柄
原理:接收器上电后,与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红外接收功能才能正常运行?如何解决?