关于arduino调PID的实际操作

首先说明:是可以用arduino控制带编码器的电机实现速度闭环,可行的,可行的,可行的。在实际应用中我用一块2560控制了4个电机匀速转动,实现麦轮或者全向轮的走直线,最后拿了省一等奖。

前提知识:外部中断引脚可以对高低电平的变化进行监测

而一般的带编码器的电机的编码器引脚输出的也就是高低电平

先看2560上的外部中断引脚有哪些:
图片摘自网络(图片摘自网络)
也就可以看到有6个引脚可以使用,分别是2、3、18、19、20、21
而编码器上有两个输出口输出矩形脉冲波,如果控制四个电机那么2560的端口就不够,于是简化一下,不使用正交解码,而是直接读取编码器一个接口的脉冲变化

初始化设置:

#define d_time 10
#define Kp 30
#define Ki 2.5
#define Kd 0                 //pid
int pulse1,pulse2,pulse3,pulse4;	//	全局变量,用于记录接收到的脉冲数
char direct1,direct2,direct3,direct4;
float error_sum1,error_sum2,error_sum3,error_sum4;
float error_last1,error_last2,error_last3,error_last4;
void setup()
{
pinMode(2,INPUT);pinMode(3,INPUT);
pinMode(18,INPUT);pinMode(19,INPUT);//将引脚设置为输入模式
attachInterrupt(digitalPinToInterrupt(2),READ1,CHANGE);      //车轮1编码器电平跳变沿进入中断函数READ1
attachInterrupt(digitalPinToInterrupt(3),READ2,CHANGE);      //车轮2进入中断函数READ2
attachInterrupt(digitalPinToInterrupt(18),READ3,CHANGE);      //车轮3进入中断函数READ3
attachInterrupt(digitalPinToInterrupt(19),READ4,CHANGE);      //车轮4进入中断函数READ4
   FlexiTimer2::set(100, control);// 设置定时器2中断每100ms进入一次中断函数control
   FlexiTimer2::start();          //使用中断使能
}

每个中断引脚有不同的中断号,digitalPinToInterrupt(pin) :取得引脚pin的中断号
关键语法借用太极创客的文章
在这里插入图片描述

关于中断

中断的具体就是让单片机放下正在运行的主函数比如loop中的函数,转去先执行一下中断函数中的代码

所以设置中断函数,名字自己随便取
我这里命名为READ1、2、3、4
中断函数中这样写(中断函数要放在初始化函数之后)

//pulse是全局变量,初始化的时候有设置
void READ1()
{
  pulse1++;
}
void READ2()
{
  pulse2++;
}
void READ3()
{
  pulse3++;
}
void READ4()
{
  pulse4++;
}

上面是外部中断函数

下面写定时器中断函数的内容

void control()
{
   int PWM1,PWM2,PWM3,PWM4;
   PWM1 = pid(pulse1,sudu1,1);PWM2 = pid(pulse2,sudu2,2);PWM3 = pid(pulse3,sudu3,3);PWM4 = pid(pulse4,sudu4,4);
   decide1(PWM1);decide2(PWM2);decide3(PWM3);decide4(PWM4);
   pulse1 = pulse2 = pulse3 = pulse4 = 0;     //计数清零
}

decide函数就是判断一下电机正转还是反转或者是停转

void decide1(int abc)
{
  if(direct1 == 'w')//正转
      go1(abc);
  if(direct1 == 's')
      back1(abc);   //反转
  if(direct1 == 'p')//停转
      down1();
 }

关于PID

pid的具体含义就不在这里赘述了,这里直接贴代码(有些不规范,将就用)

int pid(int Pulse,float target_speed,int chelun)//位置式PID
{
    int pwm;	
    float now_speed,error_speed;
    now_speed=Pulse/(0.748*d_time);			//将脉冲转换为速度
    error_speed = target_speed-now_speed;	//目标速度与当前速度的差距
    switch(chelun)							//这里其实可以对不同的电机采用不同的参数,这样鲁棒性会更好
    {case 1:
      error_sum1 += error_speed;
      pwm = error_speed*Kp+error_sum1*Ki+(error_speed-error_last1)*Kd;//参数的定义在初始化那里,使用的宏定义
      error_last1 = error_speed;
      //Serial.println(now_speed);
      break;
     case 2:
      error_sum2 += error_speed;			//累计误差,由于是累计误差,所以每次起步之前要先清零,不然直接满转
      pwm = error_speed*Kp+error_sum2*Ki+(error_speed-error_last2)*Kd;
      error_last2 = error_speed;
      //Serial.print("车轮2:");Serial.println(now_speed);
      break;
     case 3:
      error_sum3 += error_speed;
      pwm = error_speed*Kp+error_sum3*Ki+(error_speed-error_last3)*Kd;
      error_last3 = error_speed;
      //Serial.print("车轮3:");Serial.println(now_speed);
      break;
     case 4:
      error_sum4 += error_speed;
      pwm = error_speed*Kp+error_sum4*Ki+(error_speed-error_last4)*Kd;
      error_last4 = error_speed;
      //Serial.print("车轮4:");Serial.println(now_speed);
      break;
      }
    if(pwm>170)pwm = 170;       //这里可以丢进上面的switch判断,来仔细的限制车轮的最大输出来防止过冲
    if(pwm<0) pwm = 0;          //限幅作用
    return pwm;
}

——————————2024/08/04更新——————————————
简单整理上传了代码在码云,需要的可以查看
https://gitee.com/dreamer_home/arduino-car/tree/master

水平有限,欢迎指正。转载请注明出处!*

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值