-
Arduino智能小车——循迹篇
2017-09-04 11:57:11Arduino智能小车——循迹篇 相信大家都在网上看到过类似下图这样的餐厅服务机器人,或者仓库搬运机器人,但是你们有没有注意到图片中地上的那条黑线?没错,他们都是沿着这条黑线来行进的,在这一篇将教大家...Arduino智能小车——循迹篇
Arduino智能小车系列教程时空门:
- Arduino智能小车——拼装篇 点击跳转
- Arduino智能小车——测试篇 点击跳转
- Arduino智能小车——调速篇 点击跳转
- Arduino智能小车——超声波避障 点击跳转
- Arduino智能小车——蓝牙小车 点击跳转
- Arduino智能小车——循迹篇 点击跳转
- Arduino智能小车——小车测速 点击跳转
相信大家都在网上看到过类似下图这样的餐厅服务机器人,或者仓库搬运机器人,但是你们有没有注意到图片中地上的那条黑线?没错,他们都是沿着这条黑线来行进的,在这一篇将教大家怎么用小车实现类似的循迹功能。
准备材料
黑色电工胶布
黑色胶布用于搭建小车运行的“轨道”,选用黑色宽度18mm左右的即可。
循迹模块
在此我们使用循迹模块TCRT5000,该模块体积小,灵敏度较高,还可以通过转动上面的电位器来调节检测范围。
模块特色
1、采用TCRT5000红外反射传感器
2、检测距离:1mm~8mm适用,焦点距离为2.5mm
3、比较器输出,信号干净,波形好,驱动能力强,超过15mA。
4、配多圈可调精密电位器调节灵敏度
5、工作电压3.3V-5V
6、输出形式 :数字开关量输出(0和1)
7、设有固定螺栓孔,方便安装
8、小板PCB尺寸:3.2cm x 1.4cm
9、使用宽电压LM393比较器工作原理
TCRT5000传感器的红外发射二极管不断发射红外线,当发射出的红外线没有被反射回来或被反射回来但强度不够大时,光敏三极管一直处于关断状态,此时模块的输出端为低电平,指示二极管一直处于熄灭状态;被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,光敏三极管饱和,此时模块的输出端为高电平,指示二极管被点亮。
由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常量。
##循迹模块安装
循迹模块的工作一般要求距离待检测的黑线距离1-2cm,因此我建议大家可以将循迹模块向下延伸。我自己是在硬纸板上面打了几个孔,固定循迹模块,每个模块之间可以留1cm左右的距离。传感器在接收到反射不同的距离的时候“AO”引脚电压会不同,是模拟信号,“DO”是数字信号输出。因为在这里我们只用判断是否检测到黑线,因此使用“DO”数字信号即可。按照车头为正方向,从右到左循迹模块的“DO”依次接到开发板“10”、“11”、“12”、“13”引脚。
测试代码
#include <Servo.h> #define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4 int leftMotor1 = 16; int leftMotor2 = 17; int rightMotor1 = 18; int rightMotor2 = 19; int trac1 = 10; //从车头方向的最右边开始排序 int trac2 = 11; int trac3 = 12; int trac4 = 13; int leftPWM = 5; int rightPWM = 6; Servo myServo; //舵机 int inputPin=7; // 定义超声波信号接收接口 int outputPin=8; // 定义超声波信号发出接口 void setup() { // put your setup code here, to run once: //串口初始化 Serial.begin(9600); //舵机引脚初始化 myServo.attach(9); //测速引脚初始化 pinMode(leftMotor1, OUTPUT); pinMode(leftMotor2, OUTPUT); pinMode(rightMotor1, OUTPUT); pinMode(rightMotor2, OUTPUT); pinMode(leftPWM, OUTPUT); pinMode(rightPWM, OUTPUT); //寻迹模块D0引脚初始化 pinMode(trac1, INPUT); pinMode(trac2, INPUT); pinMode(trac3, INPUT); pinMode(trac4, INPUT); } void loop() { // put your main code here, to run repeatedly: tracing(); } void motorRun(int cmd,int value) { analogWrite(leftPWM, value); //设置PWM输出,即设置速度 analogWrite(rightPWM, value); switch(cmd){ case FORWARD: Serial.println("FORWARD"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case BACKWARD: Serial.println("BACKWARD"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNLEFT: Serial.println("TURN LEFT"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: Serial.println("TURN RIGHT"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: Serial.println("STOP"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } } void tracing() { int data[4]; data[0] = digitalRead(10); data[1] = digitalRead(11); data[2] = digitalRead(12); data[3] = digitalRead(13); if(!data[0] && !data[1] && !data[2] && !data[3]) //左右都没有检测到黑线 { motorRun(FORWARD, 200); } if(data[0] || data[1]) //右边检测到黑线 { motorRun(TURNRIGHT, 150); } if(data[2] || data[3]) //左边检测到黑线 { motorRun(TURNLEFT, 150); } if(data[0] && data[3]) //左右都检测到黑线是停止 { motorRun(STOP, 0); while(1); } Serial.print(data[0]); Serial.print("---"); Serial.print(data[1]); Serial.print("---"); Serial.print(data[2]); Serial.print("---"); Serial.println(data[3]); }
代码详解
小车装有4个TCRT5000,从最右边模块开始读入数据,放入
data[]
数组中data[0] = digitalRead(10); data[1] = digitalRead(11); data[2] = digitalRead(12); data[3] = digitalRead(13);
4个模块可能存在的检测状态如下,其中“1”表示检测到黑线,“0”代表没有检测到黑线:
data[0] data[1] data[2] data[3] 小车运动状态 1 1 1 1 停止 0 0 1 1 左转 0 0 0 1 左转 0 0 1 0 左转 1 1 0 0 右转 1 0 0 0 右转 0 1 0 0 右转 0 0 0 0 直行 第一种情况,四个模块都没有检测到黑线时,直行:
if(!data[0] && !data[1] && !data[2] && !data[3]) //左右都没有检测到黑线 { motorRun(FORWARD, 200); }
右边任意一个模块检测到黑线时,右转:
if(data[0] || data[1]) //右边检测到黑线 { motorRun(TURNRIGHT, 150); }
左边任意一个模块检测到黑线时,左转:
if(data[2] || data[3]) //左边检测到黑线 { motorRun(TURNLEFT, 150); }
当四个模块都检测到黑线时,说明已经运动到轨道终点,此时停止运动:
if(data[0] && data[3]) //左右都检测到黑线是停止 { motorRun(STOP, 0); while(1); }
循迹效果展示
在起点出准备出发
弯道中
欢迎各位有兴趣的朋友加入Q群1:789127261点评、交流
-
Arduino智能小车——蓝牙小车
2019-05-20 10:25:11Arduino智能小车——蓝牙小车 上一章我们完成了小车的运动控制,虽然小车已经可以运动,但是不能远程遥控,不够高大上。在这一篇,我们将尝试用手机蓝牙遥控小车。蓝牙模块 蓝牙( Bluetooth® ):是一种无线...Arduino智能小车——蓝牙小车
Arduino智能小车系列教程时空门:
- Arduino智能小车——拼装篇 点击跳转
- Arduino智能小车——测试篇 点击跳转
- Arduino智能小车——调速篇 点击跳转
- Arduino智能小车——超声波避障 点击跳转
- Arduino智能小车——蓝牙小车 点击跳转
- Arduino智能小车——循迹篇 点击跳转
- Arduino智能小车——小车测速 点击跳转
上一章我们完成了小车的运动控制,虽然小车已经可以运动,但是不能远程遥控,不够高大上。在这一篇,我们将尝试用手机蓝牙遥控小车。
蓝牙模块
蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。
我们在此使用的蓝牙模块(HC-05)已经在内部实现了蓝牙协议,不用我们再去自己开发调试协议。这类模块一般都是借助于串口协议通信,因此我们只需借助串口将我们需要发送的数据发送给蓝牙模块,蓝牙模块会自动将数据通过蓝牙协议发送给配对好的蓝牙设备。
串口通信
由于要借助串口实现蓝牙通信功能,所以我们在此要先了解下Arduino的串口通信。
Arduino UNO开发板上的串口为0->RX,1->TX,在开发板内部也已经配置好了串口的功能,我们只需调用函数借口即可。以下列出串口通信里面常用的函数,并加以简单解释,详细用法可在用到时自行查询。
开启串行通信接口并设置通信波特率
Serial.begin(speed);
关闭串口通信
Serial.end();
判断串口缓冲器是否有数据写入
Serial.available();
读取串口数据
Serial.read();
返回下一字节(字符)输入数据,但不删除它
Serial.peek();
清空串口缓存
Serial.flush();
写入字符串数据到串口
Serial.print();
写入字符串数据+换行到串口
Serial.println();
写入二进制数据到串口
Serial.write();
read时触发的事件函数
Serial.SerialEvent();
读取固定长度的二进制流
Serial.readBytes(buffer,length);
打印接到数据十进制表示的ascii码
Serial.println(incomingByte, DEC);
蓝牙模块连接
TX: 接Arduino UNO开发板"RX"引脚
RX: 接Arduino UNO开发板"TX"引脚
GND: 接Arduino UNO开发板"GND"引脚
VCC: 接Arduino UNO开发板"5V"或"3.3V"引脚手机蓝牙助手
想实现手机蓝牙遥控小车,手机APP是必不可少的,目前网上有很多蓝牙串口助手,省去了我们自己写APP的时间,当然如果朋友你有能力或者想自己DIY的话也可以尝试自己写APP,在这里我推荐大家用这款手机蓝牙助手(百度上搜手机蓝牙串口助手就可以搜到,挺好用的)
下载并安装后打开APP,在这里可能会提示你有新版本请求更新,建议点击以后再说(暂时不更新),以我的经验,一般点击立即更新都会更新失败。
点击自定义按键[2],将其“显示名称”属性改为“前进”,“点击发送”属性改为“01”,并点击“确定”保存
点击自定义按键[4],将其“显示名称”属性改为“左转”,“点击发送”属性改为“03”,并点击“确定”保存
点击自定义按键[5],将其“显示名称”属性改为“后退”,“点击发送”属性改为“02”,并点击“确定”保存
点击自定义按键[6],将其“显示名称”属性改为“右转”,“点击发送”属性改为“04”,并点击“确定”保存以上修改的属性值即为我们点击对应按键之后,蓝牙串口助手自动通过蓝牙发送的数据,与上一篇所定义的小车的几个状态一致,这样方便在Arduino在接收到蓝牙模块的数据后对小车的状态进行控制。
#define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4
修改后属性如下图
下面就来看看我们修改的效果吧,点击“模式切换”
小科普:
蓝牙模块上电(只简单连接"VCC"和"GND"引脚)之后,其他蓝牙设备即可与其连接,一般蓝牙模块默认初始连接密码为"0000"或"1234",如果连接不上蓝牙,请尽快与厂商或者店家联系。蓝牙模块上电后LED指示灯不断闪亮,当有设备连接预期连接之后会隔一段闪两下,蓝牙串口助手也会有相应已连接的提示。##Arduino代码编写
新建一个工程,将下面代码复制到工程内#define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4 int leftMotor1 = 4; int leftMotor2 = 5; int rightMotor1 = 6; int rightMotor2 = 7; void setup() { // put your setup code here, to run once: Serial.begin(9600); pinMode(leftMotor1, OUTPUT); pinMode(leftMotor2, OUTPUT); pinMode(rightMotor1, OUTPUT); pinMode(rightMotor2, OUTPUT); } void loop() { // put your main code here, to run repeatedly: //usart read if(Serial.available()>0) { char cmd = Serial.read();//读取蓝牙模块发送到串口的数据 Serial.print(cmd); motorRun(cmd); } } void motorRun(int cmd) { switch(cmd){ case FORWARD: Serial.println("FORWARD"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case BACKWARD: Serial.println("BACKWARD"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNLEFT: Serial.println("TURN LEFT"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: Serial.println("TURN RIGHT"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: Serial.println("STOP"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } }
朋友们大概也发现了,这个代码和上一篇的代码基本上相同,只不过增加了串口的内容。
##代码详解
串口初始化函数,想要通过串口的库函数对串口进行操作,必须在void set_up()
函数中对其进行初始化。Serial.begin(9600);
在void loop()函数内,加入了检测串口接收内容的函数,并将接收到的命令输入到 void motorRun(int cmd)函数中控制小车运动。
if(Serial.available()>0) { char cmd = Serial.read(); Serial.print(cmd); motorRun(cmd); }
蓝牙小车测试
下载程序之后,重新连接蓝牙模块,切换到我们自定义的按键界面,快试试蓝牙遥控小车吧。
附件
安卓手机蓝牙串口点击下载,也可以复制链接 https://download.csdn.net/download/qq_16775293/11165678 到浏览器下载。
欢迎各位有兴趣的朋友加入Q群1:789127261点评、交流
-
Arduino智能小车——超声波避障
2017-09-01 21:17:36Arduino智能小车——超声波避障 经过这几篇的测试大家应该对小车有一定的认识了,在实际的操作过程中经常会由于操作不当各种碰壁吧?那这次我们将给小车装上一只“眼睛”,让小车看到障碍,躲避障碍。准备材料...Arduino智能小车——超声波避障
Arduino智能小车系列教程时空门:
- Arduino智能小车——拼装篇 点击跳转
- Arduino智能小车——测试篇 点击跳转
- Arduino智能小车——调速篇 点击跳转
- Arduino智能小车——超声波避障 点击跳转
- Arduino智能小车——蓝牙小车 点击跳转
- Arduino智能小车——循迹篇 点击跳转
- Arduino智能小车——小车测速 点击跳转
文章目录
经过前几篇的测试大家应该对小车有一定的认识了,在实际的操作过程中经常会由于操作不当各种碰壁吧?那这次我们将给小车装上一只“眼睛”,让小车看到障碍,躲避障碍。
准备材料
超声波模块HC-SR04
在这里简单说下超声波测距的原理,相信大家也都知道。超声波发射装置发出超声波,它的根据是接收器接到超声波时的时间差,与雷达测距原理相似。 超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。
- 采用Trig引脚触发,给至少10us的高电平脉冲信号
- 模块自动发送8个40kHz的方波,自动检测是否有信号返回
- 有信号返回,通过Echo引脚输出一个高电平脉冲,高电平脉冲持续的时间就是超声波从发射到反射返回的时间。
距离=(高电平脉冲时间*340)/2
。(声音在空气中传播速度为340m/s)
主要技术参数 工作电压 DC5V 静态电流 <2mA 输出电平 0-5V 感应角度 ≤15度 探测距离 2cm-450cm 最高精度 0.3cm ###舵机
在这里推荐9G舵机SG90,或者其他类似的舵机,这种舵机体积比较小,扭矩虽然不是大, 但是足够带动简易云台,很方便在小车上使用,大家购买时注意舵机的转动角度,有55度的,180度的等等,大家可以根据需要购买。舵机固定架
舵机固定架的购买一定要配合舵机,所以大家购买的时候注意尺寸哦!!~
舵机安装
舵机在安装之前大家一定要记得校准,为什么要校准那,这个跟舵机的工作原理有关。控制信号由接收机的通道进入信号调制芯片,获得直流偏置电压。它内部有一个基准电路,产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。最后,电压差的正负输出到电机驱动芯片决定电机的正反转。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。
舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分,总间隔为2ms。以180度角度伺服为例,那么对应的控制关系是这样的:高电平时间 对应位置 0.5ms 0度 1.0ms 45度 1.5ms 90度 2.0ms 135度 2.5ms 180度 也就是说当对舵机输入相同控制信号时,舵机会运动到固定位置,他的动作不是做圆周运动,而是在运动范围内,每一个位置对应一个控制信号。
因此我们需要在将舵机安装在固定架上之前,需要先将舵机初始化好,舵机一般为三根线:棕色——GND,红色——VCC,橙色——控制信号。因此我们将棕色线接到GND,红色线接到“+5V”引脚,橙色线接到“9”引脚,初始化程序如下:
#include <Servo.h> //加入含有舵机控制库的头文件 #define PIN_SERVO 9 //舵机信号控制引脚 Servo myservo; void setup() { myservo.attach(PIN_SERVO); //舵机初始化 } void loop() { myservo.write(90); //PWM输出 }
在舵机初始化好之后将其安装在固定架上,然后装配在小车上,此时保证超声波模块超前。
超声波接线
估计不少朋友早已经发现板子上的“+5V”和“GND”引脚已经不够用了,这个时候你们可以向我这样焊一个扩展板出来,上面固定两排排针,一排用来扩展“+5V”,一边用来扩展“GND”引脚。
超声波模块有四个引脚,“VCC”接到Arduino UNO开发板的“+5V”引脚,“GND”接到开发板“GND”引脚,“Trig”引脚接到开发板“8”引脚,“Echo”引脚接到开发板“7”引脚。
线太乱了,真的没办法整理,我自己都没眼看。
代码测试
#include <Servo.h> #define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4 int leftMotor1 = 16; int leftMotor2 = 17; int rightMotor1 = 18; int rightMotor2 = 19; int leftPWM = 5; int rightPWM = 6; Servo myServo; //舵机 int inputPin=7; // 定义超声波信号接收接口 int outputPin=8; // 定义超声波信号发出接口 void setup() { // put your setup code here, to run once: //串口初始化 Serial.begin(9600); //舵机引脚初始化 myServo.attach(9); //测速引脚初始化 pinMode(leftMotor1, OUTPUT); pinMode(leftMotor2, OUTPUT); pinMode(rightMotor1, OUTPUT); pinMode(rightMotor2, OUTPUT); pinMode(leftPWM, OUTPUT); pinMode(rightPWM, OUTPUT); //超声波控制引脚初始化 pinMode(inputPin, INPUT); pinMode(outputPin, OUTPUT); } void loop() { // put your main code here, to run repeatedly: avoidance(); } void motorRun(int cmd,int value) { analogWrite(leftPWM, value); //设置PWM输出,即设置速度 analogWrite(rightPWM, value); switch(cmd){ case FORWARD: Serial.println("FORWARD"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case BACKWARD: Serial.println("BACKWARD"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNLEFT: Serial.println("TURN LEFT"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: Serial.println("TURN RIGHT"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: Serial.println("STOP"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } } void avoidance() { int pos; int dis[3];//距离 motorRun(FORWARD,200); myServo.write(90); dis[1]=getDistance(); //中间 if(dis[1]<30) { motorRun(STOP,0); for (pos = 90; pos <= 150; pos += 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position } dis[2]=getDistance(); //左边 for (pos = 150; pos >= 30; pos -= 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position if(pos==90) dis[1]=getDistance(); //中间 } dis[0]=getDistance(); //右边 for (pos = 30; pos <= 90; pos += 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position } if(dis[0]<dis[2]) //右边距离障碍的距离比左边近 { //左转 motorRun(TURNLEFT,250); delay(500); } else //右边距离障碍的距离比左边远 { //右转 motorRun(TURNRIGHT,250); delay(500); } } } int getDistance() { digitalWrite(outputPin, LOW); // 使发出发出超声波信号接口低电平2μs delayMicroseconds(2); digitalWrite(outputPin, HIGH); // 使发出发出超声波信号接口高电平10μs,这里是至少10μs delayMicroseconds(10); digitalWrite(outputPin, LOW); // 保持发出超声波信号接口低电平 int distance = pulseIn(inputPin, HIGH); // 读出脉冲时间 distance= distance/58; // 将脉冲时间转化为距离(单位:厘米) Serial.println(distance); //输出距离值 if (distance >=50) { //如果距离小于50厘米返回数据 return 50; }//如果距离小于50厘米小灯熄灭 else return distance; }
代码详解
“Trig”引脚控制超声波发出声波,对应
int outputPin=8;
“Echo”引脚反应接收到返回声波,对应int inputPin=7;
int inputPin=7; // 定义超声波信号接收接口 int outputPin=8; // 定义超声波信号发出接口
int getDistance()函数解析
超声波发出引脚“Trig”为高时对外发出超声波,为保证发出10μs声波,因此在发送之前需要将该引脚拉低,并给他一定反应时间。
digitalWrite(outputPin, LOW); // 使发出发出超声波信号接口低电平2μs delayMicroseconds(2);
之后发送10μs超声波
digitalWrite(outputPin, HIGH); // 使发出发出超声波信号接口高电平10μs,这里是至少10μs
声波发送之后禁止其继续发送,同时开始检测是否反射回来的声波
digitalWrite(outputPin, LOW); // 保持发出超声波信号接口低电平 int distance = pulseIn(inputPin, HIGH); // 读出脉冲时间
pulseIn()单位为微秒,声速344m/s,所以距离cm=344100/1000000pulseIn()/2约等于pulseIn()/58.0
distance= distance/58; // 将脉冲时间转化为距离(单位:厘米)
超声波模块工作受物体表面反射程度影响,并且在传播过程中信号强度容易衰减,因此该模块适用的检测距离有限,一般在50cm以内相对正确,而且我们在避障时不需要检测太远的距离,因此超过50cm以上的都按50cm计算if (distance >=50) { //如果距离小于50厘米返回数据 return 50; }//如果距离小于50厘米小灯熄灭 else return distance;
void avoidance()函数解析
小车前进过程中只检测前方距离障碍的距离,并且控制舵机,保持超声波模块位于正前方。
motorRun(FORWARD,200); myServo.write(90); dis[1]=getDistance(); //中间
当检测到小车前方距离障碍距离小于30cm时停车,检测两边距离。
motorRun(STOP,0);
控制舵机每次运动一个周期后都返回正前方位置。由于舵机运动需要一定的时间,因此在每转过一个角度的时候都延时
delay(15)
,供其运动。for (pos = 90; pos <= 150; pos += 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position }
当运动到最左边时检测小车左边距离障碍的距离
dis[2]=getDistance(); //左边
向右边运动,检测右边距离
for (pos = 150; pos >= 30; pos -= 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position if(pos==90) dis[1]=getDistance(); //中间 } dis[0]=getDistance(); //右边
将前边、左边、右边距离障碍的距离都检测结束之后,舵机回到最初位置。
for (pos = 30; pos <= 90; pos += 1) { myServo.write(pos); // tell servo to go to position in variable 'pos' delay(15); // waits 15ms for the servo to reach the position }
注意事项
1.舵机使用时要防止其堵转,因为点击堵转时电流会增大,很容易烧坏舵机。
2.舵机的红色电源线接入电压一般要大于等于其工作电压,供电不足会导致舵机不停自传。
3.Arduino 《Servo.h》库里提供的write()函数输出的PWM即为舵机专用的20ms为周期的PWM波,如果使用其他开发板或者函数的话,请务必保证输出方波周期为20ms,否则舵机不会受控制总结
这一篇讲解了舵机和超声波模块的使用方法,舵机在大家以后的开发生涯中应该会经常用到,因此舵机的使用规则(控制周期为20ms)请大家一定要记清楚,在舵机不受控制的时候建议大家可以购买一个舵机测试仪来测试舵机。
欢迎各位有兴趣的朋友加入Q群1:789127261 点评、交流
-
Arduino智能小车——测试篇
2017-08-21 00:53:45Arduino智能小车——测试篇 上一章讲解了智能小车的拼装,但是还没有让小车动起来,这章我们将继续拼装,使得小车可以动起来。 驱动模块安装 可能有些朋友会问到,驱动是干嘛的,为什么要驱动,小时候玩四...Arduino智能小车——测试篇
Arduino智能小车系列教程时空门:
- Arduino智能小车——拼装篇 点击跳转
- Arduino智能小车——测试篇 点击跳转
- Arduino智能小车——调速篇 点击跳转
- Arduino智能小车——超声波避障 点击跳转
- Arduino智能小车——蓝牙小车 点击跳转
- Arduino智能小车——循迹篇 点击跳转
- Arduino智能小车——小车测速 点击跳转
上一章讲解了智能小车的拼装,但是还没有让小车动起来,这章我们将继续拼装,使得小车可以动起来。
驱动模块安装
可能有些朋友会问到,驱动是干嘛的,为什么要驱动,小时候玩四驱车的时候直接装上电池小车就跑了,干嘛还要驱动模块。答案很简单,四驱车他只能朝着一个方向运动,而且永远都是以最大速度运行,我们所做的智能小车通常要控制小车电机的转速和运行方向,因此驱动是必不可少的模块。驱动模块的具体工作原理不在这里做详细的介绍,想了解的朋友可以自行查阅资料。
驱动模块用法简介
一般拿到一个模块之后都要去对应的官网找到它的资料包,查看其详细用法,在某宝上买的模块一般店家都有整理好的资料包,所以某宝也是一个很好的资料库,大家一定要合理运用哦~
在此我们选用的是L298N模块,该模块引脚分配如下:
**+12V:**该引脚接的电压是驱动模块所能输出给电机的最大电压,一般 直接接电池。12V是由L298N芯片所能接受最大电压而定,一般介入5~12V电压。在此我们接入的电压为两节18650串联的电压,即3.7+3.7=7.4V;
GND: 在该项目中GND即为电源的负极,同时要保证Arduino开发板,驱动模块等所有模块的GND连在一起才可以正常工作。在某些复杂的项目中还需要区分数字地和模拟地,在此不做详细介绍。
+5V: L298N模块(注意不是L298N芯片)内含稳压电路(将高电压转换为低电压的电路),在模块内部将"+12V"引脚输入的电压转化为可供开发板使用的+5V电压,一般将次输出接入到开发板为开发板供电。
L298N有两路输出,所以可以控制小车前进、后退、转弯,其中:
ENA: 代表第一路输出的电压大小。驱动模块输出电压越高,电机转速越快。
1.当其输入为0V的时候,驱动模块输出对第一路电机输出电压为0V;
2.当其输入为3.3V的时候,驱动模块对第一路电机输出电压为"+12V"引脚的输入电压。
3.由于ENA输入电压的高低控制驱动对电机的输出电压,因此当我们需要对小车运动速度进行控制的时候,一般通过PWM对"ENA"引脚进行控制。**IN1/IN2:**这两个引脚控制电机正反转方向。例:假如IN1输入高电平3.3V,IN2输入低电平0V,ENA为3.3V,电机正转,此时将IN1输入改为0V,IN2输入改为3.3V,其他条件不变,则电机将会反转。
**OUT1/OUT2:**这两个引脚分别接电机的两极。
**ENB,IN3/IN4,OUT3/OUT4引脚控制第二路输出,与上述ENB,IN3/IN4,OUT3/OUT4**功能相似。
驱动安装
将准备好的驱动模块固定在小车,将从地盘电机延长出来的导线分左右两边分开,左边两个电机中每一个电机的其中一根线OUT1,另外一个接OUT2。同理,右边两个电机中每一个电机的其中一根线OUT3,另外一个接OUT4,并用螺丝刀将拧蓝色接线柱上方的螺丝拧紧。
电池座固定
将电池固定在小车尾部,将电源的两根线链接到+12V和GND引脚,红色代表正极,接到+12V,黑色代表负极,接到GND(一般电路中默认红色为正,黑色为负),并拧紧螺丝固定。
Ardunio开发板安装
将Arduno板子用螺丝固定在小车中部,由于小车运动中也需要对开发板供电,此时我们用两根公对公的杜邦线为其供电,红色(也可以为其他颜色)杜邦线一边插入Arduino板的"5V"引脚,一边插到L298N驱动的"+5V"引脚,黑色(也可以为其他颜色)杜邦线一边插入Arduino板的"GND"引脚,一边插到L298N驱动的"GND"引脚。
为控制电机的正反转,此时我们需要拿四根公对母杜邦线,将L298N驱动的IN1/IN2/IN3/IN3引脚与Arduino板的4/5/6/7号引脚对应相连,最终拼装图如下:抱歉,接线有点乱,大家可以用扎带或者皮筋将其捆好固定
至此,我们的小车基本拼装完成,接下来就要开始写程序来控制小车运动咯,有没有很激动~
Ardunio程序编写
Arduino的程序编写一般使用Arduino IDE,该软件安装比较简单,大家可以自行安装,安装成功后打开IDE,在程序里写入下述代码:
#include <Servo.h> //定义五中运动状态 #define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4 //定义需要用到的引脚 int leftMotor1 = 4; int leftMotor2 = 5; int rightMotor1 = 6; int rightMotor2 = 7; void setup() { // put your setup code here, to run once: //设置控制电机的引脚为输出状态 pinMode(leftMotor1, OUTPUT); pinMode(leftMotor2, OUTPUT); pinMode(rightMotor1, OUTPUT); pinMode(rightMotor2, OUTPUT); } void loop() { // put your main code here, to run repeatedly: int cmd; for(cmd=0;cmd<5;cmd++)//依次执行向前、向后、向左、想有、停止四个运动状态 { motorRun(cmd); delay(2000);//每个命令执行2s } } //运动控制函数 void motorRun(int cmd) { switch(cmd){ case FORWARD: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case BACKWARD: digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case TURNLEFT: digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } }
代码详解
为方便代码的编写,提高代码的可读性,在此我们先定义出小车可能的运动状态
#define STOP 0 //停止 #define FORWARD 1 //前进 #define BACKWARD 2 //后退 #define TURNLEFT 3 //左转弯 #define TURNRIGHT 4 //右转弯
电机运动需要经过驱动模块驱动,而驱动模块的输出状态又取决去IN1/IN2/EN,IN3/IN4/ENB这两组引脚的控制。本实验只是简单控制电机的运动,不用控制电机的转速,因此ENA,ENB默认接入高电平(买过来模块的时候,你会发现这两个引脚都通过跳线帽和"+5V"的引脚相连,即输出最大电压),此时我们只需控制IN1/IN2,IN3/IN4两组引脚即可对小车的运动状态进行控制。因此我们在此定义以下四个引脚
//定义需要用到的引脚 int leftMotor1 = 4; int leftMotor2 = 5; int rightMotor1 = 6; int rightMotor2 = 7;
当电机的两个输入端加入的电压有电压差,且电压差满足一定条件时电机才会转动,为控制L298N驱动OUT1/OUT2,OUT3/OUT4两路输出,我们需要了解该模块的使用方法。
下面两个表格为L298N的输入输出对应关系,其中H:高电平,L:低电平,ENA、ENB均为高电平
输入 输出 IN1 IN2 OUT1 OUT1 H L H L L H L H
输入 输出 IN3 IN4 OUT3 OUT4 H L H L L H L H
由上述表格可以清晰看出控制小车运动时,只需要将同一边的两个引脚设置成不同的输出电压即可,例如让小车向前运动时左右两边的IN1和IN2可以设置为
digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH);
为了提高代码的执行效率,我们在此将小车的四种运动状态封装在函数里,方便调用。
void motorRun(int cmd) { switch(cmd){ case FORWARD: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case BACKWARD: digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case TURNLEFT: digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } }
至此,小车应该便可以成功运动起来咯,快为自己庆祝下吧。接下来我们将在此基础上进项进一步的开发,大家再接再厉哦
##调试中可能出现的问题问题1.
下载完程序,小车向前运动时,有些轮子向前,有些轮子运动方向相反:
将运动方向错误的电机的两根电源线在驱动上的接线位置对换。例如将本来接OUT1的电源线换到OUT2,将接OUT2的电源线换到OUT1。问题2.
代码没变,但是电机一会儿动一会不动:
这样的情况一般是由于接触不良造成的,检查你的驱动输出到电机的电源线是否有松动或者接触不良的情况。问题3.
向前运动时他向后,向后运动时他向前,向左运动时他向右:
解决方案1:按照 问题1 中那样的解决方案,一个一个换线
解决方案2:将void motorRun(int cmd)
函数中,引脚输出状态反过来,例如解决向前运动时他向后这种错误状态case FORWARD: //原来的代码 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break;
case FORWARD: //修改后的代码 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break;
欢迎各位有兴趣的朋友加入Q群1:789127261点评、交流
-
Arduino智能小车——拼装篇
2019-05-20 10:24:35Arduino智能小车——拼装篇简介Arduino是一款便捷灵活、方便上手的开源电子原型平台,比较适合刚接触硬件的入门级开发者学习。在我身边有很多初学者都陷入了这么一种困境,已经将Arduino官网的教程全部跑完,然而... -
Arduino智能小车——小车测速
2019-05-20 10:25:04Arduino智能小车——小车测速 可以用于测速的模块很多,比如加速度计、激光、超声波、编码器等等,由于我们对小车速度的测量精度要求不高,因此我们可以借助小车套件里面的码盘配上测速模块对其速度进行测量。... -
arduino智能小车避障程序
2021-04-01 16:43:20arduino智能小车避障程序 -
Arduino智能小车例程
2018-12-22 00:11:15共享学习Arduino智能小车例程, 有需要的朋友拿去参考吧。 -
arduino 智能小车程序
2016-06-23 21:37:45arduino 智能小车程序,可实现,前进,后退,左转,右转,碰见障碍物返回 -
Arduino智能小车——调速篇
2017-08-31 12:13:26Arduino小车——调速篇 在这一篇我们将对小车的行进速度进行调整,将驱动模块的作用发挥出来。首先大家要了解PWM这个概念。PWM 脉宽调制(PWM)基本原理:控制方式就是对逆变电路开关器件的通断进行控制,使输出... -
distance在函数 int_Arduino智能小车——超声波避障
2021-01-13 00:13:30Arduino智能小车——超声波避障Arduino智能小车系列教程时空门:Arduino智能小车——拼装篇 点击跳转Arduino智能小车——测试篇 点击跳转Arduino智能小车——调速篇 点击跳转Arduino智能小车——超声波避障 点击跳转... -
Arduino智能小车——组装篇
2021-01-06 06:19:27Arduino智能小车——拼装篇 简介 准备材料 拼装步骤 电机线的焊接 电机固定 安装背板支柱 安装背板 安装轮子 总结 简介 Arduino是一款便捷灵活、方便上手的开源电子原型平台,比较适合刚接触硬件的入门级开发者学习...