上次我们打造了一辆基于Arduino的循迹小车,并且加入了PID算法。
https://znzcqy.github.io/post/Nzx8AZmvk/

但是小车的表现却不尽如人意,于是我对代码进行了改进,并且不再使用L298N电机的ENA,ENB使能端,对线路进行了简化,直接采用Arduino的带PMW的引脚来对小车进行速度控制。电池方面,换成可充电的锂电池(3.7V X3),以保证小车的速度。在上次的赛道中,赛道总长度大约为5.14m,要实现在7s内跑完全程的目标,就必须要更高的速度。


推荐使用正版arduino开发板

下面是改进后的代码

#define righA_PIN 5
#define righB_PIN 3
#define leftA_PIN 10
#define leftB_PIN 9
#define leftA_track_PIN 2
#define leftB_track_PIN 4
#define middle_track_PIN 6
#define righA_track_PIN 7
#define righB_track_PIN 8
float Kp = 15 , Ki = 0, Kd = 200;                   //pid弯道参数参数
float error = 0, P = 0, I = 0, D = 0, PID_value = 0;//pid直道参数
float previous_error = 0, previous_I = 0;           //误差值
int sensor[5] = {0, 0, 0, 0, 0};                    //
//5个传感器数值的数组
static int initial_motor_speed = 200;                //初始速度
void read_sensor_values(void);                      //读取初值
void calc_pid(void);                                //计算pid
void motor_control(void);                           //电机控制
void motor_pinint( );     //引脚初始化
void setup()
{
  Serial.begin(9600); //串口波特率115200(PC端使用)
  track_pinint( );     //循迹引脚初始化
  motor_pinint();        //电机引脚初始化
}
void loop()
{
  read_sensor_values();         //循迹小车
  calc_pid();
  motor_control();
}
/*循迹模块引脚初始化*/
void track_pinint( )
{
  pinMode (leftA_track_PIN, INPUT); //设置引脚为输入引脚
  pinMode (leftB_track_PIN, INPUT); //设置引脚为输入引脚
  pinMode (middle_track_PIN, INPUT);//设置引脚为输入引脚
  pinMode (righA_track_PIN, INPUT); //设置引脚为输入引脚
  pinMode (righB_track_PIN, INPUT); //设置引脚为输入引脚
}

void motor_pinint( )
{
  pinMode (leftA_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (leftB_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (righA_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (righB_PIN, OUTPUT); //设置引脚为输出引脚
}
//速度设定范围(-255,255)
void motorsWrite(int speedL, int speedR)
{
  if (speedL > 0) {
    analogWrite(leftA_PIN, speedL);
    analogWrite(leftB_PIN, 0);
  } else {
    analogWrite(leftA_PIN, 0);
    analogWrite(leftB_PIN, -speedL);
  }
  if (speedR > 0) {
    analogWrite(righA_PIN, speedR);
    analogWrite(righB_PIN, 0);
  } else {
    analogWrite(righA_PIN, 0);
    analogWrite(righB_PIN, -speedR);
  }
}

void read_sensor_values()
{
  sensor[0] = digitalRead(leftA_track_PIN);
  sensor[1] = digitalRead(leftB_track_PIN);
  sensor[2] = digitalRead(middle_track_PIN);
  sensor[3] = digitalRead(righA_track_PIN);
  sensor[4] = digitalRead(righB_track_PIN);
  if (sensor[0] == 0 && sensor[1] != 0 && sensor[2] != 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = -4;//01111
  } else if (sensor[0] == 0 && sensor[1] == 0 && sensor[2] != 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = -3;//00111
  } else if (sensor[1] == 0 && sensor[0] != 0 && sensor[2] != 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = -2;//10111
  } else if (sensor[1] == 0 && sensor[0] != 0 && sensor[2] == 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = -1;//10011
  } else if (sensor[2] == 0 && sensor[1] != 0 && sensor[0] != 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = 0;//11011
  } else if (sensor[3] == 0 && sensor[1] != 0 && sensor[2] == 0 && sensor[0] != 0 && sensor[4] != 0) {
    error = 1;//11001
  } else if (sensor[3] == 0 && sensor[1] != 0 && sensor[2] != 0 && sensor[0] != 0 && sensor[4] != 0) {
    error = 2;//11101
  } else if (sensor[4] == 0 && sensor[1] != 0 && sensor[2] != 0 && sensor[3] == 0 && sensor[0] != 0) {
    error = 3;//11100
  } else if (sensor[4] == 0 && sensor[1] != 0 && sensor[2] != 0 && sensor[3] != 0 && sensor[0] != 0) {
    error = 4;//11110
  } else if (sensor[0] != 0 && sensor[1] != 0 && sensor[2] != 0 && sensor[3] != 0 && sensor[4] != 0) {
    error = 0;//11111
  }
}
void calc_pid()
{
  P = error;
  I = I + error;
  D = error - previous_error;
  PID_value = (Kp * P) + (Ki * I) + (Kd * D);
  previous_error = error;
}
void motor_control()
{
  int left_motor_speed ;
  int right_motor_speed ;
  left_motor_speed = initial_motor_speed + PID_value;
  right_motor_speed = initial_motor_speed - PID_value;
  if (left_motor_speed < -255) {
    left_motor_speed = -255;
  }
  if (left_motor_speed > 255) {
    left_motor_speed = 255;
  }
  if (right_motor_speed < -255) {
    right_motor_speed = -255;
  }
  if (right_motor_speed > 255) {
    right_motor_speed = 255;
  }
  motorsWrite(left_motor_speed, right_motor_speed);
}

最后再说明一下,PID参数需要自己调节,通常先把速度调到50,Ki,Kd先赋值为0,调节Kp,直到能循迹了,再把速度调大,调节Kd即可,Ki是积分常数,一般为0,如果小车不稳定,可以适当赋值。实测小车速度赋值为200时,跑完一圈大概5s。这时我们可以适当把速度再调高,挑战速度的极限,通常需要更换摩擦力更大的赛道地面。