基于Arduino红外控制循迹小车

硬件:Arduino板子;电机驱动模块LM298(这个型号无所谓明白端口含义就行);红外传感模块(三个接口的,一个发射管一个接收管)几根杜邦线,四个普通的小的直流电机 首先明白这些硬件的端口情况

 

对于arduino上面的不同端口还有什么串口通信,此处程序利用高低电平控制不需要考虑特殊用途。

我在做的时候起初一下想到的PID控制直接就套用的写了下但是效果很糟糕,在很低的速度下可能能够完成,原因我没有确定应该是在时间上,最后还是选择的单纯的高低电平控制,两者的差别在于

PID控制程序只需要分为判断部分和PID算法部分,结构简单清晰,适合范围广

而使用高低电平逻辑控制则需要对传感器红外判断部分进行充分的划分对每种情况都需要直接做出判断而不存在计算,适合知道赛道情况的简单比赛。

//先这里就确定其他接口与MCU端口的对应关系!D为红外 IN 为驱动 EN使能
#define D1 A0   
#define D2 A1
#define D3 A2
#define D4 A3
#define IN1 2
#define IN2 4
#define IN3 5
#define IN4 7
#define ENA 3
#define ENB 6

#define LED_ON  digitalWrite(12, HIGH)
#define LED_OFF  digitalWrite(12, LOW)

#define SPEED_LINE 75           //直线速度
#define SPEED_ADJUST_LOW 0      //调整速度,低速一侧
#define SPEED_ADJUST_HIGH 80    //调整速度,高速一侧
#define SPEED_TURN_LOW -80      //转弯速度,低速一侧
#define SPEED_TURN_HIGH 100     //转弯速度,高速一侧
#define SPEED_CYCLE 85         //直角弯转圈速度
#define SPEED_TRIG 200          //制动速度

#define TIME_TRIG 35            //制动时间,单位ms
#define TIME_STOP 800           //静止时间,单位ms
#define TIME_ADJUST 1           //小弯调整时间,单位ms
#define TIME_TURN_MAX 2000      //转弯最大时间,单位ms
#define TIME_CYCLE_MAX 3500     //旋转最大时间,单位ms
#define TIME_LEAVE_MAX 1000     //离开黑线的最大时间,单位ms
#define TIME_DIRECT_ZERO 500    //判断V型弯方向最大时间差,单位ms

//速度误差值,以左轮为速度基准,小车跑直线时右轮速度误差,跑直线时实际设置的速度,offsset=L-R
//12个数组分别为-240,-200,-160,-120,-80,-40,40,80,120,160,200,240时的误差
static int motor_offset[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int t = 0; //延时计时时间
int speed_l = 0, speed_r = 0; //当前小车左右轮的设置速度
int temp_read = 0;//传感器状态临时变量
int direct;//V型弯方向,左为1,右
long int time_direct = 0;//上一次最左面或最右面传感器检测到黑线时间,用于计算V型弯时间差

int Read(void);
void Move(int L, int R);
void leave_line(void);
void turnv_l(void);
void turnv_r(void);

void setup() {
  // put your setup code here, to run once:
  //串口初始化
  Serial.begin(9600);
  Serial.println("start");

  //管脚初始化
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  //指示LED灯,可忽略
  LED_OFF;

  //当小车放在地面上时开始循迹
  while (Read() != 0);

}

void loop() {
  // put your main code here, to run repeatedly:
  switch (Read()) {

    //直行
    case 0:
      if (millis() - time_direct > TIME_DIRECT_ZERO)
        direct = 0;

      Move(SPEED_LINE, SPEED_LINE);
      break;

    //左转直角
    case 1110:
    case 1100:
      leave_line();

      //让小车静止下来,保证小车旋转时不受之前的轮子速度影响
      Move(0, 0);
      delay(TIME_STOP);

      //原地旋转
      Move(-SPEED_CYCLE, SPEED_CYCLE);
      temp_read = 0;
      t = 0;
      while (temp_read != 1 && temp_read != 100 && t++ < TIME_CYCLE_MAX) { //旋转直到只有左边第二个灯或最右边的灯检测到黑线或超时
        temp_read = Read();
        delay(1);
      }

      Move(0, 0);
      break;

    //右转直角
    case 11:
    case 111:
      leave_line();

      //让小车静止下来,保证小车旋转时不受之前的轮子速度影响
      Move(0, 0);
      delay(TIME_STOP);

      //原地旋转
      Move(SPEED_CYCLE, -SPEED_CYCLE);
      temp_read = 0;
      t = 0;
      while (temp_read != 1000 && temp_read != 10 && t++ < TIME_CYCLE_MAX) { //旋转直到只有右边第二个灯或最左边的灯检测到黑线或超时
        temp_read = Read();
        delay(1);
      }

      Move(0, 0);
      break;

    //左转大弯
    case 1000:
      //记录黑线方向,用于之后判断是否有V型弯
      time_direct = millis();
      direct = 1;

      leave_line();

      Move(SPEED_TURN_LOW, SPEED_TURN_HIGH);
      temp_read = 0;
      t = 0;
      while (temp_read != 1  && temp_read != 100 && temp_read != 101 && temp_read != 1010 && temp_read != 110 && t++ < TIME_TURN_MAX) { //旋转直到只有左边第二个灯或最右边的灯检测到黑线、超时或检测到V型弯时
        delay(1);
        temp_read = Read();
      }

      break;

    //右转大弯
    case 1:
      //记录黑线方向,用于之后判断是否有V型弯
      time_direct = millis();
      direct = 2;

      leave_line();

      Move(SPEED_TURN_HIGH, SPEED_TURN_LOW);
      temp_read = 0;
      t = 0;
      while (temp_read != 1000 && temp_read != 10  && temp_read != 101 && temp_read != 1010  && t++ < TIME_TURN_MAX) { //旋转直到只有右边第二个灯或最左边的灯检测到黑线、超时或检测到V型弯时
        delay(1);
        temp_read = Read();
      }
      break;

    //左转小弯调整
    case 100:
      //V型弯检测算法
      if ((millis() - time_direct) > TIME_DIRECT_ZERO)//清零黑线记录的方向
        direct = 0;
      else if (direct == 2) {//即在500s内最最右边和左边第二个先后检测到黑线,是V型弯
        leave_line();
        turnv_r();
      }

      //普通位置调整
      Move(SPEED_ADJUST_LOW, SPEED_ADJUST_HIGH);
      delay(TIME_ADJUST);
      break;

    //右转小弯调整
    case 10:
      //V型弯检测算法
      if ((millis() - time_direct) > TIME_DIRECT_ZERO)//清零黑线记录的方向
        direct = 0;
      else if (direct == 1) {//即在500s内最最左边和右边第二个先后检测到黑线,是V型弯
        leave_line();
        turnv_l();
      }

      //普通位置调整
      Move(SPEED_ADJUST_HIGH, SPEED_ADJUST_LOW);
      delay(TIME_ADJUST);
      break;

    //十字路口
    case 1001:
    case 1111:
      delay(1);
      direct = 0;
      break;

    //左转锐角弯
    case 1010:
      //记录黑线方向,备用
      time_direct = millis();
      direct = 1;

      leave_line();
      turnv_l();
      break;

    //右转锐角弯
    case 101:
      //记录黑线方向,备用
      time_direct = millis();
      direct = 2;

      leave_line();
      turnv_r();
      break;

    //锐角顶点或十字或不确定状态
    case 110:
      if (direct == 1) {//检测到了锐角顶点且之前检测锐角在左面
        leave_line();
        turnv_l();
      }
      else if (direct == 2) {//检测到了锐角顶点且之前检测锐角在右面
        leave_line();
        turnv_r();
      }
      else {//应该不是V型弯
        delay(1);
      }
      break;
    default:
      delay(1);
  }
}

/**
   函数功能:读取小车传感器状态
   入口参数:无
   返回参数:千位到个位分别表示四个传感器的状态,1111表示四路检测到,0表示未检测到黑线
  */
int Read(void) {
  return (digitalRead(D1) + digitalRead(D2) * 10 + digitalRead(D3) * 100 + digitalRead(D4) * 1000);
}

/**
   函数功能:设置小车的两个轮子转速
   入口参数:L左轮输出速度,范围0~255
   入口参数:R右轮输出速度,范围0~255
   返回参数:无
  */
void Move(int L, int R) {
  int area, remainder; //区间,余数

  //直行指示灯
  if (L == R)
    digitalWrite(13, HIGH);
  else
    digitalWrite(13, LOW);

  //记录设置速度,制动时使用
  speed_l = L;
  speed_r = R;

  //左轮方向设置
  if (L > 0) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  }
  else if (L < 0) {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }

  //右轮方向设置
  if (R > 0) {
    digitalWrite(IN3, HIGH);
    digitalWrite(IN4, LOW);
  }

  else {
    digitalWrite(IN3, LOW);
    digitalWrite(IN4, HIGH);
  }

  //速度计算
  area = R / 40;
  remainder = R % 40;

  if (R > 0 && R <= 40)
    R = R - motor_offset[6] * remainder / 40;
  else if (R > 40 && R < 240)
    R = R - (motor_offset[area + 6] + (motor_offset[area + 6 + 1] - motor_offset[area + 6]) * remainder / 40);
  else if (R >= 240)
    R = R - motor_offset[11];
  else if (R < 0 && R >= -40)
    R = R - motor_offset[5] * (-remainder) / 40;
  else if (R < -40 && R > -240)
    R = R - (motor_offset[area + 6] + (motor_offset[area + 6 - 1] - motor_offset[area + 6]) * (-remainder) / 40);
  else if (R <= -240)
    R = R - motor_offset[0];
  else
    R = 0;

  L = abs(L);
  R = abs(R);
  if (L > 0xff) L = 0xff;
  if (R > 0xff) R = 0xff;

  //速度设置
  analogWrite(ENA, L);
  analogWrite(ENB, R);
  //  analogWrite(ENA, 0);
  //  analogWrite(ENB, 0);


}

/**
   函数功能:让小车离开黑线
   入口参数:无
   返回参数:无
  */
void leave_line(void) {
  Move(SPEED_LINE, SPEED_LINE);
  t = 0;
  while (Read() != 0 && t++ < TIME_LEAVE_MAX) {
    delay(1);
  }

  if (speed_l > 0 && speed_r > 0)
    Move(-SPEED_TRIG, -SPEED_TRIG);
  if (speed_l > 0 && speed_r < 0)
    Move(SPEED_TRIG, -SPEED_TRIG);
  if (speed_l > 0 && speed_r < 0)
    Move(-SPEED_TRIG, SPEED_TRIG);
  delay(TIME_TRIG);

  Move(0, 0);
}

/**
   函数功能:左转V型弯
   入口参数:无
   返回参数:无
  */
void turnv_l(void) {
  LED_ON;
  Move(-SPEED_CYCLE, SPEED_CYCLE);
  t = 0;
  while (Read() != 1000 && t++ < TIME_CYCLE_MAX)
    delay(1);

  Move(SPEED_TURN_LOW, SPEED_TURN_HIGH);
  t = 0;
  while (Read() != 100 && t++ < TIME_TURN_MAX) //旋转直到只有左边第二个灯检测到黑线或超时
    delay(1);
  LED_OFF;
}

/**
   函数功能:右转V型弯
   入口参数:无
   返回参数:无
*/
void turnv_r(void) {
  LED_ON;
  Move(SPEED_CYCLE, -SPEED_CYCLE);
  t = 0;
  while (Read() != 1 && t++ < TIME_CYCLE_MAX)
    delay(1);

  Move(SPEED_TURN_HIGH, SPEED_TURN_LOW);
  t = 0;
  while (Read() != 10 && t++ < TIME_TURN_MAX) //旋转直到只有右边第二个灯检测到黑线或超时
    delay(1);
  LED_OFF;
}

 

 

 

  • 26
    点赞
  • 226
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: 好的,我可以回答这个问题。以下是基于 Arduino红外循迹小车程序的示例代码: ``` // 定义红外传感器引脚 #define IR_LEFT A #define IR_MIDDLE A1 #define IR_RIGHT A2 // 定义电机引脚 #define MOTOR_LEFT 5 #define MOTOR_RIGHT 6 void setup() { // 初始化串口通信 Serial.begin(960); // 设置红外传感器引脚为输入模式 pinMode(IR_LEFT, INPUT); pinMode(IR_MIDDLE, INPUT); pinMode(IR_RIGHT, INPUT); // 设置电机引脚为输出模式 pinMode(MOTOR_LEFT, OUTPUT); pinMode(MOTOR_RIGHT, OUTPUT); } void loop() { // 读取红外传感器的值 int ir_left = digitalRead(IR_LEFT); int ir_middle = digitalRead(IR_MIDDLE); int ir_right = digitalRead(IR_RIGHT); // 输出红外传感器的值 Serial.print(ir_left); Serial.print(ir_middle); Serial.println(ir_right); // 判断小应该往哪个方向移动 if (ir_left == LOW && ir_middle == LOW && ir_right == LOW) { // 直行 digitalWrite(MOTOR_LEFT, HIGH); digitalWrite(MOTOR_RIGHT, HIGH); } else if (ir_left == HIGH && ir_middle == LOW && ir_right == LOW) { // 左转 digitalWrite(MOTOR_LEFT, LOW); digitalWrite(MOTOR_RIGHT, HIGH); } else if (ir_left == LOW && ir_middle == LOW && ir_right == HIGH) { // 右转 digitalWrite(MOTOR_LEFT, HIGH); digitalWrite(MOTOR_RIGHT, LOW); } else if (ir_left == HIGH && ir_middle == LOW && ir_right == HIGH) { // 掉头 digitalWrite(MOTOR_LEFT, LOW); digitalWrite(MOTOR_RIGHT, HIGH); delay(500); digitalWrite(MOTOR_LEFT, HIGH); digitalWrite(MOTOR_RIGHT, LOW); delay(100); } else { // 停止 digitalWrite(MOTOR_LEFT, LOW); digitalWrite(MOTOR_RIGHT, LOW); } } ``` 这个程序可以让红外循迹小车根据红外传感器的值自动行驶,当小遇到障碍物时会自动避开。 ### 回答2: 基于Arduino红外循迹小车程序可以实现小根据地面上的红外线进行循迹移动。下面是一个简单的程序示例: 首先,需要连接红外传感器模块和电机驱动模块到Arduino板上。 1. 初始化引脚: ``` int leftMotorPin1 = 2; int leftMotorPin2 = 3; int rightMotorPin1 = 4; int rightMotorPin2 = 5; int leftIRSensorPin = A0; int rightIRSensorPin = A1; ``` 2. 设置引脚模式: ``` void setup() { pinMode(leftMotorPin1, OUTPUT); pinMode(leftMotorPin2, OUTPUT); pinMode(rightMotorPin1, OUTPUT); pinMode(rightMotorPin2, OUTPUT); pinMode(leftIRSensorPin, INPUT); pinMode(rightIRSensorPin, INPUT); } ``` 3. 定义小运动函数: ``` void moveForward() { digitalWrite(leftMotorPin1, HIGH); digitalWrite(leftMotorPin2, LOW); digitalWrite(rightMotorPin1, HIGH); digitalWrite(rightMotorPin2, LOW); } void moveBackward() { digitalWrite(leftMotorPin1, LOW); digitalWrite(leftMotorPin2, HIGH); digitalWrite(rightMotorPin1, LOW); digitalWrite(rightMotorPin2, HIGH); } void turnLeft() { digitalWrite(leftMotorPin1, LOW); digitalWrite(leftMotorPin2, HIGH); digitalWrite(rightMotorPin1, HIGH); digitalWrite(rightMotorPin2, LOW); } void turnRight() { digitalWrite(leftMotorPin1, HIGH); digitalWrite(leftMotorPin2, LOW); digitalWrite(rightMotorPin1, LOW); digitalWrite(rightMotorPin2, HIGH); } void stopMoving() { digitalWrite(leftMotorPin1, LOW); digitalWrite(leftMotorPin2, LOW); digitalWrite(rightMotorPin1, LOW); digitalWrite(rightMotorPin2, LOW); } ``` 4. 实现循迹功能: ``` void loop() { int leftIRValue = digitalRead(leftIRSensorPin); int rightIRValue = digitalRead(rightIRSensorPin); if (leftIRValue == HIGH && rightIRValue == HIGH) { moveForward(); } else if (leftIRValue == LOW && rightIRValue == HIGH) { turnRight(); } else if (leftIRValue == HIGH && rightIRValue == LOW) { turnLeft(); } else if (leftIRValue == LOW && rightIRValue == LOW) { moveBackward(); } delay(100); } ``` 这个程序在循迹小车上使用了两个红外传感器,根据感测到的红外线信号来决定小的运动方向。根据具体的电机驱动模块引脚连接情况,可以适当调整程序中的引脚定义和运动函数。 ### 回答3: 基于Arduino红外循迹小车程序可以实现小根据红外传感器的信号来自动识别黑线并按照线路行驶。下面是一个简单的红外循迹小车程序示例: 1. 首先,我们需要连接红外传感器和电机到Arduino板上。 2. 在程序中,我们需要定义引脚的接口和各个传感器的位置。例如,我们可以定义左侧红外传感器接收信号的引脚为A0,右侧红外传感器接收信号的引脚为A1。 3. 接下来,我们需要设置Arduino作为输入,配置引脚模式。在setup()函数中,使用pinMode()函数将A0和A1引脚设置为输入模式。 4. 在loop()函数中,我们需要编写代码来读取红外传感器的值。使用analogRead()函数获取红外传感器引脚的值,并将其存储到相应的变量中。例如,使用变量leftSensor保存A0引脚的值,使用变量rightSensor保存A1引脚的值。 5. 接下来,我们需要编写代码来判断小如何行驶。通过比较左侧和右侧红外传感器的值,判断小是否离开黑线。如果左侧和右侧传感器都检测到黑线,则小应该直行;如果只有左侧传感器检测到黑线,则小应该向右转;如果只有右侧传感器检测到黑线,则小应该向左转。 6. 根据判断结果,我们可以使用digitalWrite()函数控制电机引脚的电平来驱动小。例如,如果小要直行,则设置左右电机的引脚为HIGH;如果小要向右转,则设置左电机引脚为HIGH,右电机引脚为LOW(或者设置一个较小的PWM值);如果小要向左转,则设置左电机引脚为LOW,右电机引脚为HIGH(或者设置一个较小的PWM值)。 7. 最后,可以根据需要添加其他的功能,如遇到障碍物停止、加速减速等等。 这是一个简单的红外循迹小车程序示例,你可以根据你的具体需求进行修改和扩展。记得在编写代码之前,先安装好红外传感器和电机,并确保连接正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值