Arduino中文社区

标题: 适合新手的详细arduino超声波避障小车教程 [打印本页]

作者: GT610    时间: 2017-1-8 22:26
标题: 适合新手的详细arduino超声波避障小车教程
本帖的所有代码开源。本人强迫症,所以我的代码会很好看哦。代码都是自己写的,所以非常熟悉,不懂得地方都可以问我。用到的材料有
1
arduino UNO r3
2 l298n
电机驱动板
3
小车底盘一个,带有万向轮一个,减速驱动轮两个
4
超声波模块
5 led
6 100
欧姆限流电阻两个,其实不用也行,一个也可以。
7
杜邦线若干,扎线若干
8 7.2V
电源
9
舵机一个

先上源代码
#include <Servo.h>
int motorL1=6; //
左边轮子
int motorL2=5;
int motorR1=9; //
右边轮子
int motorR2=11;
int ledL=7; //
转弯灯
int ledR=8;
int ledB=19; //
倒车灯
int ledG=13; //
大灯
Servo s; //
超声波转向舵机
int trig=4; //
发射信号
int echo=2; //
接收信号
unsigned int S; //
距离存储
void setup() {
Serial.begin(9600); //
设置波特率
pinMode(trig,OUTPUT); //
设置引脚模式
pinMode(echo,INPUT);
pinMode(motorL1,OUTPUT);
pinMode(motorL2,OUTPUT);
pinMode(motorR1,OUTPUT);
pinMode(motorR2,OUTPUT);
pinMode(ledL,OUTPUT);
pinMode(ledR,OUTPUT);
pinMode(ledB,OUTPUT);
pinMode(ledG,OUTPUT);
pinMode(12,OUTPUT);
s.attach(3); //
定义舵机所用引脚
s.write(90); //
初始化舵机角度
tone(12,800,500);
delay(2000); //
开机延时
}


void loop() { //
主函数
s.write(90); //
舵机中位
range(); //
执行测距函数
if(S<10){ //
判断障碍物距离,距离太近
back(); //
后退
delay(300); //
后退时间
}
if(S<=40&&S>10){ //
距离中等
turn(); //
运行转向判断函数
}
if(S>40){ //
距离充足
line(); //
运行直行函数
}
}


void turn(){ //
判断转向函数
lull(); //
停止所用电机
s.write(170); //
舵机转到170度既左边(角度与安装方式有关)
delay(500); //
留时间给舵机转向
range(); //
运行测距函数
s.write(90); //
测距完成,舵机回到中位
delay(600); //
留时间给舵机转向
if (S>30) {L();} //
判断左边障碍物距离,如果距离充足,运行左转
else {
s.write(10); //
否则,舵机转动到10度,测右边距离
delay(600);
range(); //
测距
s.write(90); //
中位
delay(600);
if(S>30){ R();
} //
右转
else{ back(); //
判断右边距离,距离充足右转否则后退
int x=random(1); //
产生一个01的随机数
if (x=0){R();}
else{L();} //
判断随机数
} //
否则后退,并随机转向
}
}


void range(){ //
测距函数
digitalWrite(trig,LOW); //
测距
delayMicroseconds(2); //
延时2微秒
digitalWrite(trig,HIGH);
delayMicroseconds(20);
digitalWrite(trig,LOW);
int distance = pulseIn(echo,HIGH); //
读取高电平时间
distance = distance/58; //
按照公式计算
S = distance; //
把值赋给S
Serial.println(S); //
向串口发送S的值,可以在显示器上显示距离
if (S<30){
tone(12,800,50);
delay(50); //
延时
}
}


void line(){
ledoff(); //
关闭所有LED
ledg(); //
运行大灯函数
digitalWrite(motorR1,HIGH); //
启动所有电机向前
digitalWrite(motorL1,HIGH);
digitalWrite(motorR2,LOW);
digitalWrite(motorL2,LOW);
}

void L(){
ledoff;
digitalWrite(motorL1,LOW);
digitalWrite(motorR2,LOW);
analogWrite(motorL2,100);
analogWrite(motorR1,100);
for (int x=0;x<5;x++){ //
执行5
ledl(); //
右边LED函数
}
lull(); //
暂停所有电机
}
void R(){
ledoff();
digitalWrite(motorL2,LOW);
digitalWrite(motorR1,LOW);
analogWrite(motorL1,100);
analogWrite(motorR2,100);
for (int x=0;x<5;x++){
ledr();
}
lull();
}


void back(){ //
后退函数
ledoff();
ledb();
digitalWrite(motorL1,LOW);
digitalWrite(motorR1,LOW);
analogWrite(motorL2,100);
analogWrite(motorR2,100);
ledb();
}

void lull(){
digitalWrite(motorL1,LOW);
digitalWrite(motorL2,LOW);
digitalWrite(motorR1,LOW);
digitalWrite(motorR2,LOW);
}


void ledl(){
digitalWrite(ledL,HIGH);
delay(50);
digitalWrite(ledL,LOW);
delay(50);
}


void ledr(){
digitalWrite(ledR,HIGH);
delay(50);
digitalWrite(ledR,LOW);
delay(50);
}


void ledb(){
digitalWrite(ledB,HIGH);
}


void ledg(){
digitalWrite(ledG,HIGH);
}


void ledoff(){
digitalWrite(ledL,LOW);
digitalWrite(ledL,LOW);
digitalWrite(ledL,LOW);
digitalWrite(ledL,LOW);
}

小车整体图,为了方便接线用了拓展板一块,忘记写了

超声波测距传感器安装在舵盘上。电阻当扎线用

正面图。前面有三个led

俯视图

电源采用插接式,懒得安装开关了

电源充电口

电机与电容,电容据说可以滤掉噪音


电池特写


首先来看定义变量,需要定义用于驱动小车前进的轮子,考虑到调速所以用了pwm的引脚;4个引脚用来控制电机。在定义4LED,这个可有可无,有的话让小车更酷更好玩。这里我安装了转向灯和倒车灯和大灯。转向灯可以闪烁,大灯和倒车灯用的白光LED,看个人喜好了。在定义一个舵机,因为我们需要舵机来控制超声波传感器的方向。还有定义超声波传感器的引脚,超声波传感器需要一个发射一个接受。关于超声波传感器的使用我是模仿网上的代码写的,由于需要频繁使用测距函数,所以定义一个S来存储距离。


在来看setup函数。首先设置波特率,目的是为了调试小车是能够在电脑上显示超声波传感器的距离。在设置各种引脚的模式,电机LED都是输出模式。接下来给我们的舵机设置引脚,选择具有pwm的引脚,我们把舵机引脚设置为3,在初始化一下舵机的角度为90度,也就是正前方。继续来一个蜂鸣器叫一下,tone函数的12代表蜂鸣器接在12引脚上,800代表频率,500代表叫的时间,如果不加500将会一直响,注意,这里蜂鸣器响的时间不是延时,执行蜂鸣器响程序会继续运行,程序不会延时的。这一点注意。接下来所以LED点亮一下,有全部熄灭。这个目的制造开机的效果,通电以后LED亮起,蜂鸣器响一下,给人开机了的信号。就行电脑一样。
继续跟新完吧,在不跟新我都快忘了。继续来看主函数。

首先让舵机回到90度,然后运行测距函数,如果距离小于10,执行后退函数也就是图中的back函数,在arduino里你可以定义很多个函数,这是和C语言一样的,使用一个与内建函数不同的名称,不如这里的back,不能使用系统里面的函数不如loop函数系统已经有了就不能再用,调用函数的格式是函数的名称后面带一个括号,其实这个括号就是告诉arduino这是一个函数,然后加上分号就行了。调用函数就行使用延时一样,只是这里把需要执行的东西写在了后面,这样做的目的是让排版更加的简洁,提高程序的可读性,同时可以方便开发者修改程序,因为这个函数比如测距我们要使用很多次,如果不这样做的话出来问题的到处找测距的代码然后修改。使用了函数以后如果测距出了问题我们可以直接定位到测距函数然后就能修改了,修改以后所有用到测距的地方都变了。说完了函数继续看主函数,运行完back后我加了一个300的延时,这样小车有一个停顿,仿佛似在思考。如果距离中等运行转向这些注释都有很容易理解。大于40则直行。这里的测距,返回,转向,直行都没有直接写出具体的代码,下一楼叫大家写这些函数。
主函数主要是用来做编程思路的,个人建议写的越精简越好,具体的执行通过调用函数的方式执行

继续转向函数,顺带一张灵魂接线示意图。


运行转向函数的开始先运行lull暂停函数,停止所有的电机。转向函数的思路是先检测左边要有没有障碍物,没有就左转,有的话检测右边,右边没有就往右边转,如果右边也有的话就后退,后退一点距离后随即转向。这里的逻辑的好处是舵机运转的时间短,小车反应快,但是就是每一次都几乎往一个地方转向。可以将左边和右边的距离比较,往开阔的一方运行。
此处调用了lull函数,测距函数,左转函数和右转函数。下一楼介绍着几个函数。


这次来介绍测距函数,测距函数格式是固定的,这部分可以去网上查找超声波测距传感器的使用方法,依葫芦画瓢就行了就不多写了。这里与网上不同的是我们使用了调用函数的方式来使用测距,所以不能直接在其他函数中使用距离distance,因为变量distance是我们在测距函数中定义的,而我定义了一个全局变量S来存储distance的值,在其他函数中直接使用S的值就可以了。每次使用测距函数以后都会把新的距离值distance存储到S。这里还有一个串口函数serialprintln,这个函数的功能是把S的值发送到串口监视器上,串口监视器就在arduinoIDE中,使用串口监视器就可以在电脑上显示出S的值,方便检测运行情况。tone函数也是我加上去的,我在arduino上接了一个蜂鸣器,这里运行测距函数是如果距离小于30是蜂鸣器会响。tone函数的用法是第一个变量为arduino的引脚好,这里是12,第二个变量为频率,第三个为响的时间长度。注意,蜂鸣器响的时间长度不是延时,响了以后程序会继续运行的。

接下来是具体的执行了,line函数直行。我们用的是298,这个驱动板很方便就能让电机动起来。我们给motorRL1 1分别给高电平,2 2给低电平,电机就会转动了,如果反向与预期不同调换接线就行了,调换输入或输出的接线其中一个都是可以的。很简单已经结束了



再来看看左转和右转函数吧,这个就很简单了,让一个轮子动一个不动或反方向动都可以实现转向的。这里用了一个for循环来当延时,如果你不打算安装LED可以直接将for循环换成delay就行了。我使用了LED灯来让小车更酷所以用了一个for循环。for循环的使用也简单,定义一x并给一个初始值,然后是条件,然后是x的运算,这里x0,条件是小于5,如果小于5就加1,并且执行后面的函数一次。这里可以执行5次,x小于5以后结束,这里的具体是亮灯,我们让转向时那个方向的小灯闪烁几下,模拟转向灯。

最后把后退,暂停,LED函数一起讲解一下。这三个函数都是很简单的。后退函数用了analogwrite函数,这是arduinopwm输出功能,模拟一个05v的电压。analogwrite的事用方法是第一个变量是引脚,第二个是输出的电压大小,对应的值是0-255,0的时候就是0,255的时候是5v,使用模拟输出可以得到一个较低的电压,这样小车就跑的慢。暂停函数就是所有的都输出0,电机就不会运行了。LED函数是arduino的入门第一课,看图就知道了吧。
完结。


作者: GT610    时间: 2017-1-8 22:27
http://tieba.baidu.com/p/4921638280  原贴地址,这里发图片好麻烦。建议看原贴
作者: GT610    时间: 2017-1-8 22:29
<iframe height=498 width=510 src='http://player.youku.com/embed/XMTg5MjU4ODkwNA==' frameborder=0 'allowfullscreen'></iframe>视频
作者: outlimiter    时间: 2017-1-9 06:33
Save, and Thanks!
作者: markmarkmark    时间: 2017-1-9 08:00
good                  
作者: 完美幸    时间: 2017-1-10 23:55
不错哦

作者: DawnFly    时间: 2017-1-17 00:14
学习,感谢
作者: (TANG)    时间: 2017-1-26 23:53
好贴,学习一下。。。。。。。。
作者: weiking    时间: 2017-2-8 23:51
学习了。谢谢!
作者: Void杰    时间: 2017-3-15 22:39
问下舵机的pwm是接3吗?
作者: wangshihong    时间: 2017-3-16 00:18
好帖,谢谢楼主
作者: 峥小陈    时间: 2017-4-30 00:21
很不错,借鉴一下
作者: whengfu    时间: 2017-7-3 18:27
很不错,第一次接触小车,遇到这么齐的教程,受益了,谢谢楼主
作者: kedijixian    时间: 2017-8-15 21:13
学习了,很好,本人新手,请问我的arduino是4轮子的,这代码怎么改?
作者: 汇研    时间: 2017-8-18 17:28
wow,这个好有意思!
作者: 汇研    时间: 2017-8-18 17:33
学习到了,谢谢楼主分享
作者: panda_Aron    时间: 2017-8-31 10:10
请问如何能够实现小车能够锁定障碍然后就绕着障碍走?
作者: p巧巧    时间: 2017-12-10 10:50
请问有图吗?
作者: a1792098040    时间: 2018-1-11 08:50
12号接口是干嘛的
作者: hjr001    时间: 2018-1-11 08:55
好好学习
作者: enjoyjubilee    时间: 2018-1-16 16:40
想问下楼主电机驱动板和小车这些零件在哪里买?淘宝吗
作者: 鸿鹄重工    时间: 2018-1-20 23:40
很详细呢,只是图好像挂了
作者: 黑冥河    时间: 2018-4-4 23:21
学习学习
作者: 黑冥河    时间: 2018-4-4 23:47
路过
作者: pangpangpc    时间: 2018-4-5 05:15
让舵机回到90度,然后运行测距函数,如果距离小于10,执行后退函数也就是图中的back函数,在arduino里你可以定义很多个函数,这是和C语言一样的,使
作者: inacai2008    时间: 2018-4-21 10:41
您好,我套用了你的代码修改,但无法后退;我使用的是openjumper的L298motordriver板;定义针脚为:int motorL1=5; //左边轮子
int motorL2=4;
int motorR1=6; //右边轮子
int motorR2=7;
int ledL=13; //转弯灯
int ledR=12;
int ledB=8; //倒车灯
int ledG=9; //大灯
Servo s; //超声波转向舵机
int trig=3; //发射信号
int echo=2; //接收信号
unsigned int S; //距离存储
void setup() {
Serial.begin(9600); //设置波特率
pinMode(trig,OUTPUT); //设置引脚模式
pinMode(echo,INPUT);
pinMode(motorL1,OUTPUT);
pinMode(motorL2,OUTPUT);
pinMode(motorR1,OUTPUT);
pinMode(motorR2,OUTPUT);
pinMode(ledL,OUTPUT);
pinMode(ledR,OUTPUT);
pinMode(ledB,OUTPUT);
pinMode(ledG,OUTPUT);
pinMode(11,OUTPUT);
s.attach(10); //定义舵机所用引脚
s.write(90); //初始化舵机角度
tone(11,800,500);
delay(2000); //开机延时
}
作者: kefeng1001    时间: 2018-4-26 13:32
验证时报错:'Servo' does not name a type,怎么回事?
作者: kefeng1001    时间: 2018-4-26 13:37
又仔细看了下,漏掉了第一排的语句#include <Servo.h>,加上去就好了
作者: xnfeng    时间: 2018-5-27 18:42
怎么看不到图
作者: luo983    时间: 2018-6-8 10:35
好,我顶顶顶
作者: purple    时间: 2018-9-16 20:08
我根据自己的需要改了一下程序~~~ 可以帮我验证下是哪里错了吗? L298N的out1和out2接口连接的电机无法运转。
#include <Servo.h>
int in1=7; //左边轮子
int in2=6;
int ENA=10;
int in3=9; //右边轮子
int in4=8;
int ENB=5;
Servo s; //超声波转向舵机
int trig=4; //发射信号
int echo=3; //接收信号
unsigned int S; //距离存储
void setup() {
Serial.begin(9600); //设置波特率
pinMode(trig,OUTPUT); //设置引脚模式
pinMode(echo,INPUT);
pinMode(in1,OUTPUT);
pinMode(in2,OUTPUT);
pinMode(in3,OUTPUT);
pinMode(in4,OUTPUT);
pinMode(ENA,OUTPUT);
pinMode(ENB,OUTPUT);

s.attach(2); //定义舵机所用引脚
s.write(90); //初始化舵机角度
tone(12,800,500);
delay(2000); //开机延时m
}

void loop() { //主函数
s.write(90); //舵机中位
range(); //执行测距函数
if(S<10){ //判断障碍物距离,距离太近
back(); //后退
delay(300); //后退时间
}
if(S<=40&&S>10){ //距离中等
turn(); //运行转向判断函数
}
if(S>40){ //距离充足
line(); //运行直行函数
}
}


void turn(){ //判断转向函数
lull(); //停止所用电机
s.write(170); //舵机转到170度既左边(角度与安装方式有关)
delay(500); //留时间给舵机转向
range(); //运行测距函数
s.write(90); //测距完成,舵机回到中位
delay(600); //留时间给舵机转向
if (S>30) {L();} //判断左边障碍物距离,如果距离充足,运行左转
else {
s.write(10); //否则,舵机转动到10度,测右边距离
delay(600);
range(); //测距
s.write(90); //中位
delay(600);
if(S>30){ R();
} //右转
else{ back(); //判断右边距离,距离充足右转否则后退
int x=random(1); //产生一个0到1的随机数
if (x=0){R();}
else{L();} //判断随机数
} //否则后退,并随机转向
}
}


void range(){ //测距函数
digitalWrite(trig,LOW); //测距
delayMicroseconds(2); //延时2微秒
digitalWrite(trig,HIGH);
delayMicroseconds(20);
digitalWrite(trig,LOW);
int distance = pulseIn(echo,HIGH); //读取高电平时间
distance = distance/58; //按照公式计算
S = distance; //把值赋给S
Serial.println(S); //向串口发送S的值,可以在显示器上显示距离
if (S<30){
tone(12,800,50);
delay(50); //延时
}
}


void line(){

digitalWrite(in1,1);
digitalWrite(in2,0);
analogWrite(ENA,240);
digitalWrite(in3,1);
digitalWrite(in4,0);
analogWrite(ENB,240);
}

void L(){

digitalWrite(in1,0);
digitalWrite(in2,1);
analogWrite(ENA,150);
digitalWrite(in3,1);
digitalWrite(in4,0);
analogWrite(ENB,150);


lull(); //暂停所有电机
}
void R(){

digitalWrite(in1,1);
digitalWrite(in2,0);
analogWrite(ENA,150);
digitalWrite(in3,0);
digitalWrite(in4,1);
analogWrite(ENB,150);

lull();
}


void back(){ //后退函数

digitalWrite(in1,0);
digitalWrite(in2,1);
analogWrite(ENA,150);
digitalWrite(in3,0);
digitalWrite(in4,1);
analogWrite(ENB,150);

}

void lull(){
digitalWrite(in1,0);
digitalWrite(in2,0);
analogWrite(ENA,0);
digitalWrite(in3,0);
digitalWrite(in4,0);
analogWrite(ENB,0);
}

作者: 明娃子    时间: 2018-9-17 01:07
经典教程  
作者: 明娃子    时间: 2018-9-17 01:07
多谢分享  
作者: biscuit    时间: 2019-10-30 10:23
你好,你现在还在线吗?我想问问关于这小车的问题
作者: zk1719079789    时间: 2019-11-17 10:39
厉害厉害,
作者: ____lamer    时间: 2019-11-18 16:14
你的图挂了~ 看不到图很了~
作者: 四十岁的新手    时间: 2019-11-21 18:58
很不错。也开视频了。开始的时候duang的一下把我乐坏了。不过我是新手。这个贴纸很适合我学习

作者: ctezsw    时间: 2019-11-22 14:03
有详细组装过程吗
作者: jamesbone    时间: 2019-11-22 16:42
学习了!!
作者: tigerzq    时间: 2021-4-18 08:47
给全套图呀!
作者: yangminglin    时间: 2021-5-4 20:52
够详细,赞一个,就是图片看不见
作者: Divinity    时间: 2021-6-11 11:01
求图求图,谢谢





欢迎光临 Arduino中文社区 (https://www.arduino.cn/) Powered by Discuz! X3.4