【arduino】三路pid循迹小车

这是个很久之前的项目,没有保存照片,故使用文字+图片说明

先提前说明,想用pid就需要足够的精度,数字信号很难满足,故选用模拟信号,51系列建议放弃,没有自带模拟接口,只能外接转换器,非常麻烦

pid的好处就是跑的非常稳

以下是正文

首先是传感器部分,使用三个带模拟信号的红外传感器(大部分都是带的,d引脚输出数字,a引脚输出模拟),并排放置,如图差不多就行了,传感器之间可以留有一定间隙,根据个人调整。

每个传感器都是模拟量,也就是越黑值就越大,

 然后我们把最左边的值减去最右边的值,就能得到另一个函数图像,大概这样,画的有点丑

  再融入中间的传感器,把左右偏转,得出这样一个图像,可能传感器本身误差比较大,跟图像有所区别

直接贴代码,这部分是难点,主要是对数据的处理,实际环境变化传感器出现的误差会很难受,可以选择较好的红外传感器或者灰度传感器

int GetTraceDate()
{
  int Data_Out;//定义数据输出变量
  int Left_AD,Right_AD,Mid_AD;//定义左右中传感器AD值变量
  
  Left_AD=analogRead(A0);
  Mid_AD=analogRead(A1);
  Right_AD=analogRead(A2);
//左值减去右值
  Data_Out=(Left_AD-Right_AD+D_AD_VALUE);//D_AD_VALUE表示居中时误差,把车放在最中间显示的值
//底下这段是对图像进行处理,使得更接近理论以及运行过程的bug,自行调整即可
  if(Left_AD > _H && Mid_AD > _H && Right_AD > _H) 
    stop = 1;
  else 
    stop = 0;
  if(Mid_AD>200)
  {
    flag = 0;
  }
  if((Mid_AD <= LEFT_THERSH) && (Data_Out < -20))
  { //left
    Data_Out= (2*LEFT_MAX-Data_Out);
    flag--;
  } 
  else if((Mid_AD <= RIGHT_THERSH) && (Data_Out > -10))
  { //right
    Data_Out= (2*RIGHT_MAX-Data_Out);
    flag++;
  }
  if(Left_AD < _L && Mid_AD < _L && Right_AD < _L)
  {
       Serial.print("---");
       if(flag<0)
       {//left
          Data_Out = LEFT_MAX*2;
       }
       else
       {//right
          Data_Out = RIGHT_MAX*2;
       }
  }
// Data_Out是最重要的返回值,表示车子位于线的方向以及距离,不然无法使用pid公式
  return Data_Out;
}

 车身就没什么好讲的了,使用了一个l298n带电机,好处是可以一次带两个,图片为网图(侵权联系删除),接线方法大概就这样,使用差速转向成本低,快捷,缺点是车身容易摆动

 这里贴上完整代码

#define LEFT_MAX -680
#define RIGHT_MAX 330
#define LEFT_THERSH 35
#define RIGHT_THERSH 37
#define D_AD_VALUE -8
#define _L 45
#define _H 500

#define leftA_PIN 3
#define leftB_PIN 5
#define righA_PIN 6
#define righB_PIN 9

float Kp = 0.05, Ki = 0, Kd = 0;                    //pid弯道参数参数 
float error = 0, P = 0, I = 0, D = 0, PID_value = 0;//pid直道参数 
float previous_error = 0, previous_I = 0;           //误差值
int initial_motor_speed = 100;                //初始速度
//unsigned int PWM_L, PWM_R, T = 0;
int flag = 0;
int stop = 0;
void motor_pinint( )
{
  pinMode (leftA_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (leftB_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (righA_PIN, OUTPUT); //设置引脚为输出引脚
  pinMode (righB_PIN, OUTPUT); //设置引脚为输出引脚
}
//获取循迹传感器输出函数
int GetTraceDate()
{
  int Data_Out;//定义数据输出变量
  int Left_AD,Right_AD,Mid_AD;//定义左右中传感器AD值变量
  
  Left_AD=analogRead(A0);
  Mid_AD=analogRead(A1);
  Right_AD=analogRead(A2);
  Data_Out=(Left_AD-Right_AD+D_AD_VALUE);
  Serial.print(Data_Out);
  Serial.print("   ");
  if(Left_AD > _H && Mid_AD > _H && Right_AD > _H) 
    stop = 1;
  else 
    stop = 0;
  if(Mid_AD>200)
  {
    flag = 0;
  }
  if((Mid_AD <= LEFT_THERSH) && (Data_Out < -20))
  { //left
    Data_Out= (2*LEFT_MAX-Data_Out);
    flag--;
  } 
  else if((Mid_AD <= RIGHT_THERSH) && (Data_Out > -10))
  { //right
    Data_Out= (2*RIGHT_MAX-Data_Out);
    flag++;
  }
  if(Left_AD < _L && Mid_AD < _L && Right_AD < _L)
  {
       Serial.print("---");
       if(flag<0)
       {//left
          Data_Out = LEFT_MAX*2;
       }
       else
       {//right
          Data_Out = RIGHT_MAX*2;
       }
  }
  return Data_Out;
}

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  = initial_motor_speed - PID_value;
  int right_motor_speed = initial_motor_speed + PID_value;
  if(left_motor_speed < 1)
    left_motor_speed = 1;
  if(right_motor_speed < 1)
    left_motor_speed = 1;
  
  analogWrite(leftA_PIN, left_motor_speed);
  analogWrite(leftB_PIN, 0);
  analogWrite(righA_PIN, right_motor_speed);
  analogWrite(righB_PIN, 0);
  
  Serial.print("move_A: ");
  Serial.print(left_motor_speed, OCT);
  Serial.print(" move_B: ");
  Serial.print(right_motor_speed, OCT);
  Serial.print(" error: ");
  Serial.print(error, OCT);
  Serial.print(" PID_value: ");
  Serial.print(PID_value);
  Serial.println();
}
void _stop()
{
  analogWrite(leftA_PIN, 0);
  analogWrite(leftB_PIN, 0);
  analogWrite(righA_PIN, 0);
  analogWrite(righB_PIN, 0);
}
void setup() {
  Serial.begin(9600);
  motor_pinint();
}

void loop() {
  error = GetTraceDate();
  if(!stop) 
  {
    calc_pid();
    motor_control();
  }
  else 
  {
    Serial.println("stop");
    _stop();
  }
//  Serial.print(GetTraceDate());
//  Serial.print("   ");
//  Serial.print(analogRead(A0));
//  Serial.print("   ");
//  Serial.print(analogRead(A1));
//  Serial.print("   ");
//  Serial.print(analogRead(A2));
//  Serial.print("   ");
//  Serial.print(flag);
//  Serial.println();
  
}

代码照搬大概率是用不了的,因为需要根据车子进行实际修改,赛道也有影响

  • 15
    点赞
  • 222
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是一段基于Arduino控制器的寻线模块程序,其中包含三个传感器: ``` //定义三个传感器引脚 int sensor1 = 2; int sensor2 = 3; int sensor3 = 4; //定义电机引脚 int leftMotor = 5; int rightMotor = 6; //定义最大和最小阈值 int maxThreshold = 800; int minThreshold = 300; void setup() { //初始化串口 Serial.begin(9600); //设置传感器引脚为输入模式 pinMode(sensor1, INPUT); pinMode(sensor2, INPUT); pinMode(sensor3, INPUT); //设置电机引脚为输出模式 pinMode(leftMotor, OUTPUT); pinMode(rightMotor, OUTPUT); } void loop() { //读取传感器值 int value1 = analogRead(sensor1); int value2 = analogRead(sensor2); int value3 = analogRead(sensor3); //调试用,输出传感器值 Serial.print(value1); Serial.print(" "); Serial.print(value2); Serial.print(" "); Serial.println(value3); //判断黑线位置 if (value1 > maxThreshold && value2 > maxThreshold && value3 > maxThreshold) { //没有检测到黑线 digitalWrite(leftMotor, LOW); digitalWrite(rightMotor, LOW); } else if (value1 > minThreshold && value2 > minThreshold && value3 > minThreshold) { //在黑线上 digitalWrite(leftMotor, HIGH); digitalWrite(rightMotor, HIGH); } else if (value1 > minThreshold && value2 < minThreshold && value3 < minThreshold) { //偏左 digitalWrite(leftMotor, LOW); digitalWrite(rightMotor, HIGH); } else if (value1 < minThreshold && value2 < minThreshold && value3 > minThreshold) { //偏右 digitalWrite(leftMotor, HIGH); digitalWrite(rightMotor, LOW); } } ``` 该程序使用三个光电传感器来检测黑线位置,当三个传感器都没有检测到黑线时,电机停止运行;当三个传感器都检测到黑线时,电机以相同的速度运行;当中间传感器检测到黑线时,电机继续运行;当左边传感器检测到黑线时,右边电机停止运行,左边电机继续运行;当右边传感器检测到黑线时,左边电机停止运行,右边电机继续运行。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值