Arduino小车学习与研究
前期准备
概念学习
单片机
一台能够工作的计算机要有这样几个部份构成:中央处理单元CPU(进行运算、控制)、随机存储器RAM(数据存储)、存储器ROM(程序存储)、输入/输出设备I/O(串行口、并行输出口等)。在个人计算机(PC)上这些部份被分成若干块芯片,安装在一个被称之为主板的印刷线路板上。而在单片机中,这些部份全部被做到一块集成电路芯片中了,所以就称为单片(单芯片)机,而且有一些单片机中除了上述部份外,还集成了其它部份如模拟量/数字量转换(A/D)和数字量/模拟量转换(D/A)等。
设备检查及安装
产品名称:
亚博智能 Arduino入门学习套件(arduino UNO R3开发套件)
具体的安装流程
- 开包装,找到光碟(内置教程,相当于说明书)
- 安装光盘并将光盘资料全部复制到电脑上
- 安装驱动及开发环境驱动安装
驱动安装:
arduino IDE:
LabView 下载地址:
安装小车基本部件检查产品清单
配件安装:
-电机
-电池盒
-万向轮
-拓展面包板
-舵机云台(先单独安装不与底板连接)
-arduino uno开发板安装
小车硬件调试
- 通电调试
-电池安装完成后按下开关,灯亮即正常。
-根据提示给充电电池充电,避免过放电现象。 - 电机模块调试:
-说明:电机正反转时只能接一个信号,比始IN1 IN2 只能接一个,不能同时接两个。
-要电机转动时要先接入EN1/EN2 使能信号。
IN1接到VCC(正5V 电源)(右电机反转信号线,高电平有效)
IN2接到VCC(正5V 电源)(右电机正转信号线,高电平有效)
IN3接到VCC(正5V 电源)(左电机正转信号线,高电平有效)
IN4接到VCC(正5V 电源)(左电机反转信号线,高电平有效)
- 光电传感器调试(左右)
-在实际使用中,由W1(W2)电位器,L4(L5)信号指示灯左(右)光电 传 感器共同组成避障模块,在调试中当L4信号灯没有接收到红外信号时 不亮(输出高电平1),当接到红外反馈信号后,指示灯亮起(输出低电 平0)。
-顺时针调节电位器是增加W1左光电信号检测的距离,逆时针调节电 位 器时减少检测距离(W2相同)。
- 光电传感器调试(底盘下方)
-调试时不要对着强光,建议在室内调试,环境光线对检测距离有比较大的影响
-在实际使用中,由W3(W4)电位器,L2(L3)信号指示灯左光电传感器组成黑白线识别模块。L2(L3)信号灯没有接接收到红外信号时会不亮(输出高电平1),当接到到红外反馈信号后,指示灯亮起(输出低电平0)。
-顺时针调节电位器是增加W3左光电信号强度调节检测距离,逆时针调节电位器时减少检测距离(w4相同)。
硬件平台研究::
- 要使Arduino小车跑起来,一个很重要的部分就是电动机,关于小车左右电动机所接通的电路板上的接口是——>左电机的前进驱动接8号口,对应的输入是IN1,后退驱动接9号口,对应的输入时IN2;右电机的前进驱动接10号口,对应的输入是IN3,后退驱动接11号口,对应的输入时IN4。当接好以上接口时,小车就具备了可以跑的硬件准备了。
- 在此硬件基础上,小车可以实现的功能就有前进后退停止,以及改变左右电动机的转速来实现小车的前后左右转弯等一系列基本的动作。
在满足基本的小车行进的基础上,我们要进行改进和完善,没错,就是添加一个按钮开关和蜂鸣器来实现一个意义上的按键扫描,也就是在按下按钮的时候蜂鸣器会响,停下响声之后小车才会开始动作。这一步的意义是在于,让小车避免一启动总开关就开始运动的情况,因为这种情况下很容易发生脱手无法控制的情况,对操控者和小车都有可能造成不必要的损伤。
- 在这个按钮和蜂鸣器的设置下,我们选择了将VCC电源拓展开来,利用面包板拓展原理,然后嫁接到按钮那里,而蜂鸣器我们选择了使用A3作为模拟信号输入口。这样就实现了通过键盘扫描来启动小车。
- 一台Arduino小车如果光是在地上跑,那么它跟普通的玩具电动车是没什么本质上的区别。但是,就因为它是一台智能小车,所以它必须具备躲避危险的能力,现在我们加入了红外线功能,目的就是让它能够通过底盘的红外探测仪对前方的障碍物进行探测并规避。因此定义——>右循迹传感器P3.2输入,对应OUT1位输出;左循迹传感器P3.3输入,对应OUT2位输出。
- 在完成以上操作之后,不要忘了调节SW1和SW2两电位器,设置一下Arduino小车的红外传感器对底部障碍物的传感距离。一个有趣的实验就是,小车可以在自己制作的黑线跑道上进行黑线寻迹运动。
- 做到以上这个步骤还是不能说是完整意义上的躲避障碍物,此时的Arduino小车只是完成了它的原理部分:红外传感器的探测能力。这个时候我们继续设置连线——>右红外传感器P3.5输入,对应OUT4输出;左红外传感器P3.4输入,对应OUT3位输出。
在此基础上我们可以对程序代码进行反向的修改,也就是可以在同样的硬件基础上实现规避障碍物的反向操作:跟随障碍物行进。
- 完成到这一步的硬件实现,小车已经是一个具有规避障碍功能的智能小车了。打个比方:Arduino小车现在具有了一双眼睛。那么有眼睛就会使人联想到,它是不是应该有眼睛。那么有眼睛就会使人联想到,它是不是应该有耳朵。没错,接下来的声波探测功能将会实现耳朵的功能。
- 超声波探测仪需要安装的是一个HC-SR04超声波模块以及一块液晶显示板来显示到障碍物的距离。我们此时将A1口作为模拟信号输入接到Echo回声脚,也就是P2.0;同样的,A0口作为模拟信号输入接到Trig触发脚,然后另一边接上P2.1。这样就实现了超声波探测仪的功能。
Arduino语言的学习
编程和基本函数研究
Arduino语言是建立在C/C++基础上的,其实也就是基础的C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化。
与C语言一致的语法就不多加赘述了,主要介绍一些C语言库中未定义的常量。
HIGH | LOW : 表示数字IO口的电平,HIGH 表示高电平(1),LOW 表示低电平(0)。 INPUT | OUTPUT
表示数字IO口的方向,INPUT 表示输入(高阻态),OUTPUT 表示输出(AVR能提供5V电压 40mA电流)。
Arduino库函数简介
ArduinoArduino,是一个基于开放原始码的软硬体平台,构建于开放原始码simple I/O介面版,并且具有使用类似Java,C语言的Processing/Wiring开发环境。我们这次的时间就是由一个基于单片机并且开放源码的硬件平台和一套为Arduino板编写程序的开发环境组成。
Arduino是一个能够用来感应和控制现实物理世界的一套工具。Arduino能通过各种各样的传感器来感知环境,通过控制灯光、马达和其他的装置来反馈、影响环境。板子上的微控制器可以通过Arduino的编程语言来编写程序,编译成二进制文件,烧录进微控制器。对Arduino的编程是利用 Arduino编程语言 (基于 Wiring)和Arduino开发环境(based on Processing)来实现的。
Arduino的编程语言就像似在对一个类似于物理的计算平台进行相应的连线,它基于处理多媒体的编程环境。
1.基本函数:
void setup():初始化变量,管脚模式,调用库函数等 。
void loop():连续执行函数内的语句 。
2.功能函数:
(1).数字I/O
pinMode(pin, mode):数字IO口输入输出模式定义函数,pin表示为0~13, mode表示为INPUT或OUTPUT。
digitalWrite(pin, value):数字IO口输出电平定义函数,pin表示为0~13,value表示为HIGH或LOW。比如定义HIGH可以驱动LED。
int digitalRead(pin):数字IO口读输入电平函数,pin表示为0~13,value表示为HIGH或LOW。比如可以读数字传感器。
(2).模拟 I/O
int analogRead(pin):模拟IO口读函数,pin表示为0~5(Arduino Diecimila为0~5,Arduino nano为0~7)。比如可以读模拟传感器(10位AD,0~5V表示为0~1023)。
analogWrite(pin, value)–PWM:数字IO口PWM输出函数,Arduino数字IO口标注了PWM的IO口可使用该函数,pin表示3, 5, 6, 9, 10,11,value表示为0~255。比如可用于电机PWM调速或音乐播放。
analogWrite函数常用与调整左右轮的pwm比率在板上都以符号标示。如:``6 `
(3).扩展 I/O
shiftOut(dataPin, clockPin, bitOrder, value):SPI外部IO扩展函数,通常使用带SPI接口的74HC595做8个IO扩展,dataPin为数据口,clockPin为时钟口,bitOrder为数据传输方向(MSBFIRST高位在前,LSBFIRST低位在前),value表示所要传送的数据(0~255),另外还需要一个IO口做74HC595的使能控制。
unsigned long pulseIn(pin, value):脉冲长度记录函数,返回时间参数(us),pin表示为0~13,value为HIGH或LOW。比如value为HIGH,那么当pin输入为高电平时,开始计时,当pin输入为低电平时,停止计时,然后返回该时间。
(4).时间函数
unsigned long millis():返回时间函数(单位ms),该函数是指,当程序运行就开始计时并返回记录的参数,该参数溢出大概需要50天时间。
delay(ms):延时函数(单位ms)。
delayMicroseconds(us):延时函数(单位us)。
(5).数学库函数
min(x, y):求最小值 。
max(x, y):求最大值 。
abs(x):计算绝对值 。
constrain(x, a, b):约束函数,下限a,上限b,x必须在ab之间才能返回。
map(value, fromLow, fromHigh, toLow, * toHigh):约束函数,value必须在fromLow与toLow之间和fromHigh与toHigh之间才能返回。
pow(base, exponent):开方函数,base的exponent次方。
sq(x):平方。
sqrt(x):开根号 。
三角函数:sin(rad) ,cos(rad) ,tan(rad) 。
(6).随机数函数
randomSeed(seed):随机数端口定义函数,seed表示读模拟口analogRead(pin)函数 。
long random(max):随机数函数,返回数据大于等于0,小于max。
long random(min,max):随机数函数,返回数据大于等于min,小于max。
(7).外部中断函数
attachInterrupt(interrupt, , mode):外部中断只能用到数字IO口2和3,interrupt表示中断口初始0或1,表示一个功能函数,mode:LOW低电平中断,CHANGE有变化就中断,RISING上升沿中断,FALLING下降沿中断。
detachInterrupt(interrupt):中断开关,interrupt=1 开,interrupt=0 关。
(8).中断使能函数
interrupts():使能中断。
noInterrupts():禁止中断。
(9).串口收发函数
Serial.begin(speed): 串口定义波特率函数,speed表示波特率,如9600,19200等。
int Serial.available():判断缓冲器状态。
int Serial.read():读串口并返回收到参数。
Serial.flush():清空缓冲器。
Serial.print(data):串口输出数据。
Serial.println(data):串口输出数据并带回车符。
扩展库的研究:
红外遥控库:
自主编程
基本扩展模块,代码如下:
#include <LiquidCrystal.h>
LiquidCrystal lcd(13,12,7,6,5,4,3);
int Echo = A1; // Echo回声脚(P2.0)
int Trig =A0; // Trig 触发脚(P2.1)
int Front_Distance = 0;
int Left_Distance = 0;
int Right_Distance = 0;
int Left_motor_back=8; //左电机后退(IN1)
int Left_motor_go=9; //左电机前进(IN2)
int Right_motor_go=10; // 右电机前进(IN3)
int Right_motor_back=11; // 右电机后退(IN4)
int key=A2;//定义按键 A2 接口
int beep=A3;//定义蜂鸣器 A3 接口
int servopin=2;//设置舵机驱动脚到数字口2
int myangle;//定义角度变量
int pulsewidth;//定义脉宽变量
int val;
void setup()
{
Serial.begin(9600); // 初始化串口
//初始化电机驱动IO为输出方式
pinMode(Left_motor_go,OUTPUT); // PIN 8 (PWM)
pinMode(Left_motor_back,OUTPUT); // PIN 9 (PWM)
pinMode(Right_motor_go,OUTPUT);// PIN 10 (PWM)
pinMode(Right_motor_back,OUTPUT);// PIN 11 (PWM)
pinMode(key,INPUT);//定义按键接口为输入接口
pinMode(beep,OUTPUT);
//初始化超声波引脚
pinMode(Echo, INPUT); // 定义超声波输入脚
pinMode(Trig, OUTPUT); // 定义超声波输出脚
lcd.begin(16,2); //初始化1602液晶工作 模式
//定义1602液晶显示范围为2行16列字符
pinMode(servopin,OUTPUT);//设定舵机接口为输出接口
}
void run() // 前进
{
digitalWrite(Right_motor_go,HIGH); // 右电机前进
digitalWrite(Right_motor_back,LOW);
analogWrite(Right_motor_go,92);//PWM比例0~255调速,左右轮差异略增减
analogWrite(Right_motor_back,0);
digitalWrite(Left_motor_go,HIGH); // 左电机前进
digitalWrite(Left_motor_back,LOW);
analogWrite(Left_motor_go,120);//PWM比例0~255调速,左右轮差异略增减
analogWrite(Left_motor_back,0);
}
void brake(int time) //刹车,停车
{
digitalWrite(Right_motor_go,LOW);
digitalWrite(Right_motor_back,LOW);
digitalWrite(Left_motor_go,LOW);
digitalWrite(Left_motor_back,LOW);
delay(time * 100);//执行时间,可以调整
}
void left(int time) //左转(左轮不动,右轮前进)
{
digitalWrite(Right_motor_go,HIGH); // 右电机前进
digitalWrite(Right_motor_back,LOW);
analogWrite(Right_motor_go,200);
analogWrite(Right_motor_back,0);//PWM比例0~255调速
digitalWrite(Left_motor_go,LOW); //左轮后退
digitalWrite(Left_motor_back,LOW);
analogWrite(Left_motor_go,0);
analogWrite(Left_motor_back,0);//PWM比例0~255调速
delay(time * 100); //执行时间,可以调整
}
void spin_left(int time) //左转(左轮后退,右轮前进)
{
digitalWrite(Right_motor_go,HIGH); // 右电机前进
digitalWrite(Right_motor_back,LOW);
analogWrite(Right_motor_go,150);
analogWrite(Right_motor_back,0);//PWM比例0~255调速
digitalWrite(Left_motor_go,LOW); //左轮后退
digitalWrite(Left_motor_back,HIGH);
analogWrite(Left_motor_go,0);
analogWrite(Left_motor_back,150);//PWM比例0~255调速
delay(time * 100); //执行时间,可以调整
}
void right(int time)
{
digitalWrite(Right_motor_go,LOW); //右电机后退
digitalWrite(Right_motor_back,LOW);
analogWrite(Right_motor_go,0);
analogWrite(Right_motor_back,0);//PWM比例0~255调速
digitalWrite(Left_motor_go,HIGH);//左电机前进
digitalWrite(Left_motor_back,LOW);
analogWrite(Left_motor_go,200);
analogWrite(Left_motor_back,0);//PWM比例0~255调速
delay(time * 100); //执行时间,可以调整
}
void spin_right(int time) //右转(右轮后退,左轮前进)
{
digitalWrite(Right_motor_go,LOW); //右电机后退
digitalWrite(Right_motor_back,HIGH);
analogWrite(Right_motor_go,0);
analogWrite(Right_motor_back,150);//PWM比例0~255调速
digitalWrite(Left_motor_go,HIGH);//左电机前进
digitalWrite(Left_motor_back,LOW);
analogWrite(Left_motor_go,150);
analogWrite(Left_motor_back,0);//PWM比例0~255调速
delay(time * 100); //执行时间,可以调整
}
void back(int time) //后退
{
digitalWrite(Right_motor_go,LOW); //右轮后退
digitalWrite(Right_motor_back,HIGH);
analogWrite(Right_motor_go,0);
analogWrite(Right_motor_back,200);//PWM比例0~255调速
digitalWrite(Left_motor_go,LOW); //左轮后退
digitalWrite(Left_motor_back,HIGH);
analogWrite(Left_motor_go,0);
analogWrite(Left_motor_back,200);//PWM比例0~255调速
delay(time * 100); //执行时间,可以调整
}
void keysacn()//按键扫描
{
int val;
val=digitalRead(key);//读取数字7 口电平值赋给val
while(!digitalRead(key))//当按键没被按下时,一直循环
{
val=digitalRead(key);//此句可省略,可让循环跑空
}
while(digitalRead(key))//当按键被按下时
{
delay(10); //延时10ms
val=digitalRead(key);//读取数字7 口电平值赋给val
if(val==HIGH) //第二次判断按键是否被按下
{
digitalWrite(beep,HIGH); //蜂鸣器响
while(!digitalRead(key)) //判断按键是否被松开
digitalWrite(beep,LOW); //蜂鸣器停止
}
else
digitalWrite(beep,LOW); //蜂鸣器停止
}
}
float Distance_test() // 量出前方距离
{
digitalWrite(Trig, LOW); // 给触发脚低电平2μs
delayMicroseconds(2);
digitalWrite(Trig, HIGH); // 给触发脚高电平10μs,这里至少是10μs
delayMicroseconds(10);
digitalWrite(Trig, LOW); // 持续给触发脚低电
float Fdistance = pulseIn(Echo, HIGH); // 读取高电平时间(单位:微秒)
Fdistance= Fdistance/58; //为什么除以58等于厘米, Y米=(X秒*344)/2
// X秒=( 2*Y米)/344 ==》X秒=0.0058*Y米 ==》厘米=微秒/58
//Serial.print("Distance:"); //输出距离(单位:厘米)
//Serial.println(Fdistance); //显示距离
//Distance = Fdistance;
return Fdistance;
}
void Distance_display(int Distance)//显示距离
{
if((2<Distance)&(Distance<400))
{
lcd.home(); //把光标移回左上角,即从头开始输出
lcd.print(" Distance: "); //显示
lcd.setCursor(6,2); //把光标定位在第2行,第6列
lcd.print(Distance); //显示距离
lcd.print("cm"); //显示
}
else
{
lcd.home(); //把光标移回左上角,即从头开始输出
lcd.print("!!! Out of range"); //显示
}
delay(250);
lcd.clear();
}
void servopulse(int servopin,int myangle)/*定义一个脉冲函数,用来模拟方式产生PWM值*/
{
pulsewidth=(myangle*11)+500;//将角度转化为500-2480 的脉宽值
digitalWrite(servopin,HIGH);//将舵机接口电平置高
delayMicroseconds(pulsewidth);//延时脉宽值的微秒数
digitalWrite(servopin,LOW);//将舵机接口电平置低
delay(20-pulsewidth/1000);//延时周期内剩余时间
}
void front_detection()
{
//此处循环次数减少,为了增加小车遇到障碍物的反应速度
for(int i=0;i<=5;i++) //产生PWM个数,等效延时以保证能转到响应角度
{
servopulse(servopin,90);//模拟产生PWM
}
Front_Distance = Distance_test();
}
void left_detection()
{
for(int i=0;i<=15;i++) //产生PWM个数,等效延时以保证能转到响应角度
{
servopulse(servopin,175);//模拟产生PWM
}
Left_Distance = Distance_test();
}
void right_detection()
{
for(int i=0;i<=15;i++) //产生PWM个数,等效延时以保证能转到响应角度
{
servopulse(servopin,5);//模拟产生PWM
}
Right_Distance = Distance_test();
}
//===========================================================
void loop()
{
keysacn(); //调用按键扫描函数
while(1)
{
front_detection();//测量前方距离
if(Front_Distance < 32)//当遇到障碍物时
{
back(2);//后退减速
brake(2);//停下来做测距
left_detection();//测量左边距障碍物距离
Distance_display(Left_Distance);//液晶屏显示距离
right_detection();//测量右边距障碍物距离
Distance_display(Right_Distance);//液晶屏显示距离
if((Left_Distance < 35 ) &&( Right_Distance < 35 ))//当左右两侧均有障碍物靠得比较近
spin_left(0.7);//旋转掉头
else if(Left_Distance > Right_Distance)//左边比右边空旷
{
left(3);//左转
brake(1);//刹车,稳定方向
}
else//右边比左边空旷
{
right(3);//右转
brake(1);//刹车,稳定方向
}
}
else
{
run(); //无障碍物,直行
}
}
}