基于Arduino的智能避障小车

   *这篇文章是作者的本科毕业设计,属于作者的原创文章,禁止随意转载,仅供大家学习参考。*

基于Arduino的避障小车现在已经有了很多的帖子和教程,但本篇文章为避障小车添加了更多的实用的功能,比如:拍照、人体感应、蜂鸣器响应、蓝牙控制、自动模式和手动模式之间的切换。这篇文章的避障小车使用的是超声波避障模块。

下面介绍一下这篇文章使用到的一些模块:

  • Arduino MEGA2560 开发板
  • MEGA2560传感器扩展板
  • L298N驱动板
  • 直流电机
  • HC-SR04超声波传感器
  • AM412人体红外传感器
  • 有源蜂鸣器模块
  • SG90舵机
  • VC0706串口相机
  • TF卡读写储存模块
  • HC-05蓝牙模块

基于Arduino的智能避障小车_第1张图片

下面介绍这篇文章中各个模块之间的接线关系

MEGA2560传感器扩展板直接插接在2560开发板上。2560的扩展板的每个数字接口和模拟接口旁边都有独立的VCC和GND接口,这也是为什么需要用到扩展板的原因之一。有关2560扩展板的详情请参考MEGA2560传感器扩展板

基于Arduino的智能避障小车_第2张图片

基于Arduino的智能避障小车_第3张图片

超声波传感器:我一共用了三个超声波传感器,分别放在小车前面的舵机云台上、小车左方、小车右方,舵机云台由舵机控制,可以让云台上的超声波传感器探测更大的角度。HC-SR04超声波传感器一共有4个引脚,分别是VCC、GND、Trig、Echo。HC-SR04超声波传感器的具体用法请参考SR04超声波模块,SR04超声波传感器在Arduino中有官方的库文件,只需要调用即可,不需要自己定义函数去计时换算成距离。超声波传感器的GND连Arduino的GND,VCC连VCC就不多说了(随便哪一个口的VCC、GND都可以接,推荐接用到的那个口的GND、VCC)。基于Arduino的智能避障小车_第4张图片

SR04 Arduino
前Trig D4
前Echo D5
左Trig D9
左Echo D8
右Trig D16
右Echo D17

D是数字量接口,A是模拟量接口。大部分传感器或者模块都使用数字量接口,也有一部分传感器使用模拟量接口,这篇文章中模拟量接口只用到了一个,就是人体红外传感器使用的模拟量接口。

舵机:舵机控制的是小车前方的云台,云台上放了一个超声波传感器,通过开发板写角度,改变舵机的角度。SG90跟大部分舵机一样是三个接口,分别是S、VCC、GND,S是控制接口也称信号接口,大部分的传感器或者模块都是这三个接口。舵机使用方法非常简单,同样的拥有官方库文件,使用时只需要调用库函数就行了。例如:myservo.write(val);定义一个整型变量“val”通过改变val的值,再用这个函数,就可以改变舵机的角度。角度当然也可以一度一度地改变,只需要用一个循环语句,让舵机每变一度再延时几毫秒就可以了,这种一度一度地改变的方法更符合使用实际。

舵机 Arduino
S D2

舵机的三根线通常是是连在一起的杜邦线,一般认线的颜色就可以了,黄色的是信号线,红色的是VCC,黑色的是GND,竖着一排插在数字量接口那里,线的排列顺序刚好也和扩展板上接口的排列顺序一一对应。

基于Arduino的智能避障小车_第5张图片

人体红外传感器:AM412人体红外传感器是普通HC-SR501人体红外传感器的升级版,模块体积更小,但少了两个可调节电位器,正常使用来说,那两个可调节的电位器可以去掉。AM412人体红外传感器一共有三个引脚,OUT、VCC、GND。人体红外传感器的具体使用方法请参考人体红外传感器
基于Arduino的智能避障小车_第6张图片

人体红外传感器 Arduino
OUT A0

人体红外传感器是模拟信号,所以要连扩展板的模拟量接口。

蜂鸣器:蜂鸣器可以和人体红外传感器一起使用,当有人经过人体红外传感器时,蜂鸣器就会响应。蜂鸣器有三个引脚,S、VCC、GND。这里的蜂鸣器是低电平响应。

蜂鸣器 Arduino
S D14

蓝牙模块:蓝牙模块是与手机相连接的,手机上下载好相应的APP后(我使用的APP是“蓝牙串口”),可以发送数据给蓝牙模块,开发板接收到蓝牙模块的串口数据后执行相应的命令。蓝牙模块买到手后需要经过调试才能正常使用,一般使用USB转串口模块,将蓝牙模块插在USB转串口上,或者用开发板直接连蓝牙调试,这里调试一般为AT指令调试(使用开发板直连蓝牙模块调试时,不能将蓝牙接在D0和D1引脚上,其它数字引脚都可以,只需在程序中声明就行,因为D0、D1是开发板默认的与电脑USB的串口通讯接口,与电脑连接时不能被占用,否则电脑不能通过USB与开发板之间进行读写)。调试的主要内容是:①更改蓝牙模块的蓝牙名称和连接密码。②将蓝牙设置成从机模式(蓝牙分主机模式和从机模式,主机模式蓝牙开机自动扫描连接上次连接成功的手机或其他设备,从机模式开机后等待被连接)。③设置蓝牙波特率为9600(我习惯波特率都用9600)。

基于Arduino的智能避障小车_第7张图片

蓝牙模块具体的操作办法请参考HC-05蓝牙模块
在这篇文章中,我将手机发送给蓝牙的数据定义为整型变量,也就是数字的形式,比如改变手动自动模式或者执行前进命令都是不同的数字,这样在编程的时候比较方便。
HC-05蓝牙模块有很多个引脚,但是一般用到的就4个,分别是:RXD、TXD、VCC、GND,一个接收数据,一个发送数据,一个接电源、一个接地。当蓝牙模块调试好后,与开发板之间的连接方法如下表所示,但为了不占用与电脑之间的硬串口,每次开发板与电脑相连时,需要把蓝牙RXD、TXD的两根线拔掉(或者你直接定义另外两个数字量引脚也可以,这里用的是通用的蓝牙引脚D0、D1)。

HC-05蓝牙 Arduino
TXD D0
RXD D1

串口相机:这篇文章使用的是VC0706串口相机,之所以使用这个相机是因为网络上有这个相机的第三方库文件,使用起来比较方便。但有个问题是,网络上下载的库文件中的例程,不能直接使用,直接使用的话会出现拍的照片只有一半或一少半显示出来,能完全显示出照片的教程在VC0706使用教程
这个教程中是将VC0706弄成了一个延时摄影(延时摄影是自动每隔几秒拍照一张,再后期通过电脑合成视频,视频看上去就是加快了速度)的工具,如果只需要VC0706执行拍一张照,可以将程序中的循环语句删掉,或者使用我后面文章中的程序。串口相机买来后基本不需要调试可以直接使用,网络上找到的相关资料都是收费的,所以我也就没调试。VC0706串口相机也有很多的引脚,但是有用的也只有TX、RX、VCC、GND。

基于Arduino的智能避障小车_第8张图片

VC0706 Arduino
TX D10
RX D13

SD卡模块:SD卡模块也就是TF读写储存模块是用来储存串口相机拍摄的照片的。SD卡模块上插入一个SD卡,接在开发板上就可以直接使用了。

基于Arduino的智能避障小车_第9张图片
SD卡模块使用到的引脚有:CS(有的也称SS,这是SD卡的片选引脚,也就是开发板选择把数据存在这张卡里)、MOSI、MISO、SCK、VCC、GND。SD卡模块比较特殊在UNO和MEGA2560开发板上都有固定的引脚(且两个板子默认的引脚都不同),当然也可以自己定义引脚,这里我比较倾向于使用默认的引脚。SD卡模块在UNO上使用的教程请参考SD卡模块UNO
下表是SD卡模块在MEGA2560开发板上的引脚接线。

SD卡模块 Arduino
CS/SS D53
MOSI D51
MISO D50
SCK D52

驱动模块:驱动模块我选用的是L298N双桥驱动板,带动直流电机。

基于Arduino的智能避障小车_第10张图片
左边轮子的直流电机连接L298N的OUT1、OUT2。
右边轮子的直流电机连接L298N的OUT3、OUT4。
其中L298N和开发板相连的几个引脚当中,ENA和ENB必须要连有PWM功能的数字量引脚,因为直流电机调速要用到PWM脉宽调制。
在驱动板内部,OUT1是与IN1相连的、OUT2与IN2相连,以此类推。ENA是左边电机的调速引脚,ENB是右边电机的调速引脚。

L298N Arduino
ENA D12(PWM)
IN1 D32
IN2 D33
IN3 D34
IN4 D35
ENB D11(PWM)

另外驱动板因为电流很大,需要独立提供电源,我用了12V的电源供电,且驱动板的电源不能和开发板的电源引脚相连,但可以共地。

我将蓝牙的串口数据定义为整型变量,当手机发送0时,小车停止;当手机发送1时,小车前进;当手机发送2时,小车倒退;当手机发送3时,小车拍照;当小车发送6时,小车的蜂鸣器响应;当手机发送4时,小车切换成手动模式(小车开机默认为手动模式);当手机发送5时,小车切换成自动模式。自动模式下,小车将执行自动避障程序,直到接收到蓝牙的停止命令。

下图是小车完整的程序框图:

基于Arduino的智能避障小车_第11张图片
如下图是小车自动模式下的程序框图:

基于Arduino的智能避障小车_第12张图片
下面是这篇文章的完整程序,程序段中已经注明了注释,若还有不懂的也可以回复问我,我随缘解答。

#include //调用软串口库函数
#include //调用VC0706相机库函数
#include //调用SD卡库函数
#include //调用舵机库函数
#include //调用超声波传感器库函数(函数调用完成)
#include 



SoftwareSerial BT(0, 1);//定义蓝牙的TX和RX引脚(蓝牙模块)

#define chipSelect 53//定义SD卡的片选引脚(摄像头、SD卡模块)
SoftwareSerial cameraconnection = SoftwareSerial(10,13);
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection);//(摄像头、SD卡模块初始化前的操作)

#define Buzzerpin 14//定义蜂鸣器的引脚
#define PIR_sensor A0//定义人体红外传感器的引脚(红外、蜂鸣器模块)

#define TRIG_PIN 5//定义前方超声波传感器控制端
#define ECHO_PIN 4//定义前方超声波传感器接收端
#define TRIGright_PIN 16//定义右方超声波传感器控制端
#define ECHOright_PIN 17//定义右方超声波传感器接收端
#define TRIGleft_PIN 9//定义左方超声波传感器控制端
#define ECHOleft_PIN 8//定义左方超声波传感器接收端
#define motor1pin1 32//定义IN1引脚
#define motor1pin2 33//定义IN2引脚
#define motor1pwm 12//定义ENA引脚
#define motor2pin1 34//定义IN3引脚
#define motor2pin2 35//定义IN4引脚
#define motor2pwm 11//定义ENB引脚(避障、驱动模块)(各模块引脚定义完成)



Servo myservo;//创建一个舵机控制对象
int djangle=90;//用该变量储存舵机角度位置
SR04 sr04=SR04(ECHO_PIN,TRIG_PIN);//定义前方超声波传感器
SR04 sr04r=SR04(ECHOright_PIN,TRIGright_PIN);//定义右方超声波传感器
SR04 sr04l=SR04(ECHOleft_PIN,TRIGleft_PIN);//定义左方超声波传感器
long forward;//储存舵机在90°时障碍物的距离
long leftfw;//储存舵机在135°(左前方)时障碍物的距离
long rightfw;//储存舵机在45°(右前方)时障碍物的距离
long left;//储存左方超声波传感器数值
long right;//储存右方超声波传感器数值
int l;//定义左电机速度变量
int r;//定义右电机速度变量
int n;//计数
int m;//计数
void motor(int motorpin1,int motorpin2,int motorpwm,int val)//定义一个电机转动函数
{
pinMode(motorpin1,OUTPUT);//输出第一个引脚
pinMode(motorpin2,OUTPUT);//输出第二个引脚
digitalWrite(motorpin1,1);//将第一个引脚抬高
digitalWrite(motorpin2,0);//将第二个引脚置低
analogWrite(motorpwm,val);//给EN引脚设定模拟值,设定转速
}//(避障、驱动模块初始化前的操作)


int rentiv=0;//定义人体红外传感器获取到的电位值(红外、蜂鸣器模块初始化前的操作)(初始化前的操作完成)



int opmode;//储存小车运行模式(手动和自动):4手动、5自动
int cmd;//储存小车在手动模式下的运行指令:0停止、1前进、2倒退、3拍照、6响铃
int blcmd;//暂存蓝牙指令


void setup()
{
  opmode=4;//设置为手动模式
  Serial.begin(9600);//设置串口通讯波特率(软串口)
  BT.begin(9600);//设置蓝牙串口波特率(蓝牙模块)
  myservo.attach(2);//定义舵机的信号引脚
  myservo.write(djangle);//初始化舵机角度(避障、驱动模块)
  
  pinMode(PIR_sensor,INPUT);//将A0引脚置为输入
  pinMode(Buzzerpin,OUTPUT);//将14引脚置为输出(红外、蜂鸣器模块)
  digitalWrite(Buzzerpin,HIGH);

  delay(10000);//延迟10秒等待蓝牙启动并连上手机
  Serial.println("已成功连接上小车");//连上蓝牙后通知手机

  pinMode(53, OUTPUT);
  if (!SD.begin(chipSelect))
  {
    Serial.println("SD卡异常");
    return;
  }
  if (cam.begin())
  {
    Serial.println("相机已准备好");
  }
  else
  {
    Serial.println("相机异常");
    return;
  }
  cam.setImageSize(VC0706_640x480);
  Serial.println("照片尺寸为640x480");
  Serial.println("现在是手动模式");//向手机输出“现在是手动模式”
}//(各模块初始化完成)




void loop()
{
  while(Serial.available())
  {
    blcmd=Serial.read();
    if(blcmd=='0')
    {
      Serial.println("接收到停止命令");
      cmd=blcmd;
    }
    if(blcmd=='1')
    {
      Serial.println("接收到前进命令");
      cmd=blcmd;
    }
    if(blcmd=='2')
    {
      Serial.println("接收到倒退命令");
      cmd=blcmd;
    }
    if(blcmd=='3')
    {
      Serial.println("接收到拍照命令");
      cmd=blcmd;
    }
    if(blcmd=='4')
    {
      Serial.println("切换成手动模式");
      opmode=blcmd;
    }
    if(blcmd=='5')
    {
      Serial.println("切换成自动模式");
      opmode=blcmd;
      cmd=10;
    }
    if(blcmd=='6')
    {
      Serial.println("接收到响铃命令");
      cmd=blcmd;
    }
  }
  if((cmd=='0')&&(opmode=='4'))
  {
    l=0;
    r=0;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);
    Serial.println("小车已停止");
  }
  if((cmd=='0')&&(opmode=='5'))
  {
    l=0;
    r=0;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);
    Serial.println("小车已停止");
  }
  if((cmd=='1')&&(opmode=='4'))
  {
    l=120;
    r=120;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);
    Serial.println("小车正在前进");
  }
  if((cmd=='2')&&(opmode=='4'))
  {
    l=100;
    r=100;
    motor(motor1pin2,motor1pin1,motor1pwm,l);
    motor(motor2pin2,motor2pin1,motor2pwm,r);
    Serial.println("小车正在倒退");
  }
  if((cmd=='3')&&(opmode=='4'))
  {
    l=0;
    r=0;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);
    cmd=10;
    Serial.println("小车正在拍照");
  delay(20);
  if (! cam.takePicture()) 
    Serial.println("拍照失败!");
  else 
    Serial.println("拍照完成!");
    char filename[13];
    strcpy(filename, "IMAGE000.JPG");
    for (int i = 0; i < 1000; i++)
    {
      filename[5] = '0' + i/100;
      filename[6] = '0' + (i%100)/10;
      filename[7] = '0' + (i%100)%10;
      if (! SD.exists(filename))
      {
        break;
      }
    }
    File imgFile = SD.open(filename, FILE_WRITE);
    uint16_t jpglen = cam.frameLength();
    Serial.print(jpglen, DEC);
    Serial.println(" byte image");

    Serial.print("储存照片到");
    Serial.print(filename);
    while (jpglen > 0)
    {
    uint8_t *buffer;
    uint8_t bytesToRead = min(32, jpglen);
    buffer = cam.readPicture(bytesToRead);
    imgFile.write(buffer, bytesToRead);
    jpglen -= bytesToRead;
    }
    imgFile.close();
    Serial.println("...完成!");
    cam.begin();
  }
  if((cmd=='6')&&(opmode=='4'))
  {
    l=0;
    r=0;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);
    Serial.println("小车正在响铃");
    digitalWrite(Buzzerpin,LOW);//发声音
    delay(1000);
    digitalWrite(Buzzerpin,HIGH);//不发声音
    delay(1000);
  }
  if(opmode=='5')
  {
    rentiv=analogRead(PIR_sensor);//读取A0口的电压值并赋值到rentiv
    Serial.println(rentiv);//串口发送rentiv的值
    if(rentiv>600)
    {
      Serial.println("there is somebody!");
      digitalWrite(Buzzerpin,LOW);//发声音
    delay(1000);
    digitalWrite(Buzzerpin,HIGH);//不发声音
    delay(1000);
    Serial.println("正在拍照...");
  delay(20);
  if (! cam.takePicture()) 
    Serial.println("拍照失败!");
  else 
    Serial.println("拍照完成!");
    char filename[13];
    strcpy(filename, "IMAGE000.JPG");
    for (int i = 0; i < 1000; i++)
    {
      filename[5] = '0' + i/100;
      filename[6] = '0' + (i%100)/10;
      filename[7] = '0' + (i%100)%10;
      if (! SD.exists(filename))
      {
        break;
      }
    }
    File imgFile = SD.open(filename, FILE_WRITE);
    uint16_t jpglen = cam.frameLength();
    Serial.print(jpglen, DEC);
    Serial.println(" byte image");

    Serial.print("储存照片到");
    Serial.print(filename);
    while (jpglen > 0)
    {
    uint8_t *buffer;
    uint8_t bytesToRead = min(32, jpglen);
    buffer = cam.readPicture(bytesToRead);
    imgFile.write(buffer, bytesToRead);
    jpglen -= bytesToRead;
    }
    imgFile.close();
    Serial.println("...储存完成!");
    cam.begin();
    rentiv=0;
    }
  forward=sr04.Distance();//获取前方障碍物距离
  left=sr04l.Distance();//获取左方障碍物距离
  right=sr04r.Distance();//获取右方障碍物距离
  if(forward>30)//当前方障碍物距离大于30cm时
  {
    if((left>25)&&(right>25))//当左、右方障碍物距离大于25cm时
    {
      l=120;
      r=120;
      motor(motor1pin1,motor1pin2,motor1pwm,l);
      motor(motor2pin1,motor2pin2,motor2pwm,r);//直行
      delay(800);
    }

    if((left>25)&&(right<25))//左边比右边宽敞
    {
      l=90;
      r=120;
      motor(motor1pin1,motor1pin2,motor1pwm,l);
      motor(motor2pin1,motor2pin2,motor2pwm,r);//左转
      delay(800);
    }

    if((left<25)&&(right>25))//右边比左边宽敞
    {
      l=120;
      r=90;
      motor(motor1pin1,motor1pin2,motor1pwm,l);
      motor(motor2pin1,motor2pin2,motor2pwm,r);//右转
      delay(800);
    }

    if((left<25)&&(right<25))//当左、右障碍物距离小于25cm时
    {
      if(left>right)//如果左边比右边宽敞
      {
        l=70;
        r=80;
        motor(motor1pin1,motor1pin2,motor1pwm,l);
        motor(motor2pin1,motor2pin2,motor2pwm,r);//左转
        delay(800);
      }
      else//如果右边比左边宽敞
      {
        l=80;
        r=70;
        motor(motor1pin1,motor1pin2,motor1pwm,l);
        motor(motor2pin1,motor2pin2,motor2pwm,r);//右转
        delay(800);
      }
    }
  }
  if((forward<30)&&(forward>15))//当前方障碍物距离在15cm到30cm
  {
    l=0;
    r=0;
    motor(motor1pin1,motor1pin2,motor1pwm,l);
    motor(motor2pin1,motor2pin2,motor2pwm,r);//让小车停下来
    djangle=135;
    myservo.write(djangle);//让舵机转到135°
    delay(250);
    for(n=0;n<2;n++)
    {
      leftfw=sr04.Distance();//左前方获取数据
      delay(100);
    }
    djangle=45;
    myservo.write(djangle);//让舵机转到45°
    delay(400);
    for(n=0;n<2;n++)
    {
      right=sr04.Distance();//右前方获取数据
      delay(100);
    }
    djangle=90;
    myservo.write(djangle);//舵机归中
    delay(250);
    
    if(leftfw>rightfw)//如果左前方比右前方宽敞
    {
      l=0;
      r=80;
      motor(motor1pin1,motor1pin2,motor1pwm,l);
      motor(motor2pin1,motor2pin2,motor2pwm,r);//左转
      delay(500);
    }
    else//右边比左边宽敞
    {
      l=80;
      r=0;
      motor(motor1pin1,motor1pin2,motor1pwm,l);
      motor(motor2pin1,motor2pin2,motor2pwm,r);//右转
      delay(500);
    }
  }
  if(forward<15)
  {
    l=100;
    r=100;
    motor(motor1pin2,motor1pin1,motor1pwm,l);
    motor(motor2pin2,motor2pin1,motor2pwm,r);//倒车
    delay(500);
  }
  }
}

你可能感兴趣的:(Arduino)