第13课 自动避障智能车#

(1)项目介绍:#

在上课程中,我们制作了一个跟随智能车。实际上,利用同样的电子元件,同样的接线方法,我们只需要更改一个测试代码就可以将跟随智能车变为避障智能车。

(2)流程图:#

避障智能车具体逻辑如下表格。

检测

左边障碍物距离

distance_l(单位:cm)

distance_l(单位:cm)

检测

右边障碍物距离

distance_r(单位:cm)

distance_r(单位:cm)

检测

中间障碍物距离

distance(单位:cm)

distance(单位:cm)

条件

条件

条件

状态

0<distance<20

0<distance<20

distance_l > distance_r 如果左边大于右边

向左转

0<distance<20

0<distance<20

distance_l<=distance_r 如果左边不大于右边

向右转

distance>=20

distance>=20

前进

前进

使用的电子元件,接线方法和课程四一样,更换测试代码,运行,确保智能车能够实 现理想中的功能。

(3)接线图:超声波模块+电机+舵机#

接线注意:A、B两电机分别对应的连接电机驱动扩展板上的接口A和接口B;超声波传感器模块的V引脚至V,T(Trig)引脚至数字12(S),E(Echo)引脚至数字13(S),G引脚至G;电源接到BAT接口,舵机S接D10。

(4)测试代码#

示例代码 1(KE0165_13.ino):

/*
  keyes 4WD 多功能智能车
  课程 13
  超声波避障机器人
  http://www.keyes-robot.com
*/
#include <Servo.h>

Servo myservo;                      // 舵机对象

// 数组,用于存储点阵图案数据,可自行计算或使用取模工具获得
unsigned char FRONT[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char LEFT[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x00};
unsigned char RIGHT[] = {0x00, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char STOP01[] = {0x2E, 0x2A, 0x3A, 0x00, 0x02, 0x3E, 0x02, 0x00, 0x3E, 0x22, 0x3E, 0x00, 0x3E, 0x0A, 0x0E, 0x00};
unsigned char CLEAR[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define SCL_PIN    A5    // 时钟引脚 A5
#define SDA_PIN    A4    // 数据引脚 A4
#define TRIG_PIN   12    // 超声波触发引脚 D12
#define ECHO_PIN   13    // 超声波回声引脚 D13
#define MA_PIN     2     // 电机M3,M4方向控制引脚 D2
#define PWMA_PIN   6     // 电机M3,M4速度控制引脚 D6
#define MB_PIN     4     // 电机M1,M2方向控制引脚 D4
#define PWMB_PIN   5     // 电机M1,M2速度控制引脚 D5

int distance = 0;
int distanceL = 0;
int distanceR = 0;

/* 功能:初始化设置 */
void setup() {
  Serial.begin(9600);               // 初始化串口,波特率9600
  myservo.attach(10);               // 绑定舵机引脚10
  pinMode(ECHO_PIN, INPUT);         // 设置回声引脚为输入
  pinMode(TRIG_PIN, OUTPUT);        // 设置触发引脚为输出
  pinMode(MA_PIN, OUTPUT);          // 设置电机方向引脚为输出
  pinMode(PWMA_PIN, OUTPUT);        // 设置电机速度引脚为输出
  pinMode(MB_PIN, OUTPUT);          // 设置电机方向引脚为输出
  pinMode(PWMB_PIN, OUTPUT);        // 设置电机速度引脚为输出
  pinMode(SCL_PIN, OUTPUT);         // 设置时钟引脚为输出
  pinMode(SDA_PIN, OUTPUT);         // 设置数据引脚为输出
  matrixDisplay(CLEAR);             // 点阵屏清屏
  myservo.write(90);                // 舵机初始角度90度
  delay(500);
}

/* 功能:主循环 */
void loop() {
  distance = getDistance();         // 获取前方距离

  if (distance > 0 && distance < 20) { // 距离小于20且大于0时避障
    stopCar();                      // 停车
    matrixDisplay(STOP01);          // 显示停止图案
    delay(100);
    myservo.write(180);             // 舵机转到180度检测左侧距离
    delay(500);
    distanceL = getDistance();      // 获取左侧距离
    delay(100);
    myservo.write(0);               // 舵机转到0度检测右侧距离
    delay(500);
    distanceR = getDistance();      // 获取右侧距离
    delay(100);
    if (distanceL > distanceR) {    // 左侧距离大于右侧,左转
      turnLeft();
      matrixDisplay(LEFT);           // 显示左转图案
      delay(1000);
      myservo.write(90);             // 舵机回中
      matrixDisplay(FRONT);          // 显示前进图案
    }
    else {                         // 右侧距离大于左侧,右转
      turnRight();
      matrixDisplay(RIGHT);          // 显示右转图案
      delay(1000);
      myservo.write(90);             // 舵机回中
      matrixDisplay(FRONT);          // 显示前进图案
    }
  }
  else {                           // 距离大于等于20,继续前进
    advance();
    matrixDisplay(FRONT);           // 显示前进图案
  }
}

/* 功能:获取超声波距离(单位:厘米) */
int getDistance() {
  int dist = 0;
  digitalWrite(TRIG_PIN, LOW);     // 触发引脚低电平2微秒
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);    // 触发引脚高电平10微秒
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);     // 触发引脚低电平
  dist = pulseIn(ECHO_PIN, HIGH) / 58; // 计算距离
  Serial.println(dist);             // 输出距离值
  return dist;
}

/* 功能:小车前进 */
void advance() {
  digitalWrite(MA_PIN, HIGH);      // 电机A正转
  analogWrite(PWMA_PIN, 180);      // 电机A速度180
  digitalWrite(MB_PIN, HIGH);      // 电机B正转
  analogWrite(PWMB_PIN, 180);      // 电机B速度180
}

/* 功能:小车后退 */
void back() {
  digitalWrite(MA_PIN, LOW);       // 电机A反转
  analogWrite(PWMA_PIN, 180);      // 电机A速度180
  digitalWrite(MB_PIN, LOW);       // 电机B反转
  analogWrite(PWMB_PIN, 180);      // 电机B速度180
}

/* 功能:小车左转 */
void turnLeft() {
  digitalWrite(MA_PIN, HIGH);      // 电机A正转
  analogWrite(PWMA_PIN, 180);      // 电机A速度180
  digitalWrite(MB_PIN, LOW);       // 电机B反转
  analogWrite(PWMB_PIN, 180);      // 电机B速度180
}

/* 功能:小车右转 */
void turnRight() {
  digitalWrite(MA_PIN, LOW);       // 电机A反转
  analogWrite(PWMA_PIN, 180);      // 电机A速度180
  digitalWrite(MB_PIN, HIGH);      // 电机B正转
  analogWrite(PWMB_PIN, 180);      // 电机B速度180
}

/* 功能:小车停止 */
void stopCar() {
  analogWrite(PWMA_PIN, 0);        // 电机A速度0
  analogWrite(PWMB_PIN, 0);        // 电机B速度0
}

/* 功能:点阵屏显示图案 */
void matrixDisplay(unsigned char matrixValue[]) {
  IICStart();                      // 开始传输
  IICSend(0xc0);                  // 选择点阵地址
  for (int i = 0; i < 16; i++) {  // 发送16字节图案数据
    IICSend(matrixValue[i]);
  }
  IICEnd();                       // 结束传输
  IICStart();
  IICSend(0x8A);                 // 显示控制,脉宽4/16
  IICEnd();
}

/* 功能:IIC开始信号 */
void IICStart() {
  digitalWrite(SCL_PIN, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_PIN, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_PIN, LOW);
  delayMicroseconds(3);
}

/* 功能:IIC发送一个字节 */
void IICSend(unsigned char sendData) {
  for (char i = 0; i < 8; i++) {  // 发送8位数据
    digitalWrite(SCL_PIN, LOW);   // 时钟拉低,准备改变数据线状态
    delayMicroseconds(3);
    if (sendData & 0x01) {        // 判断最低位
      digitalWrite(SDA_PIN, HIGH);
    }
    else {
      digitalWrite(SDA_PIN, LOW);
    }
    delayMicroseconds(3);
    digitalWrite(SCL_PIN, HIGH);  // 时钟拉高,数据有效
    delayMicroseconds(3);
    sendData = sendData >> 1;     // 右移一位,准备发送下一位
  }
}

/* 功能:IIC结束信号 */
void IICEnd() {
  digitalWrite(SCL_PIN, LOW);
  delayMicroseconds(3);
  digitalWrite(SDA_PIN, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_PIN, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_PIN, HIGH);
  delayMicroseconds(3);
}

(5)测试结果#

将驱动扩展板堆叠在UNO Plus板上,上传好代码,按照课程三接线图接线,将拨码开关拨至ON端后,智能车能够自动避开障碍物行走。