硬件: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;
}