查看: 8673|回复: 46

基于麦克纳姆轮的PID实例学习--机甲大师PID控制

[复制链接]

该用户从未签到

发表于 2020-2-25 15:36 | 显示全部楼层 |阅读模式
本帖最后由 wzzzq 于 2020-10-6 19:55 编辑

一 认识pid

首先要了解PID算法是什么。网上很多帖子要么就是只有理论,要么就没讲清楚,这次我们来通过一个实例来学习pid。
https://blog.csdn.net/qq_36554582/article/details/82721453
https://blog.csdn.net/qq_25352981/article/details/81007075
可以先看看这两篇文章了解了pid的基本原理,社区里也有一些好文,先理解一下概念。

二 调试,安装编码器

微信图片_20200223155657.jpg 微信图片_20200223155709.jpg 微信图片_20200223155713.jpg
编码器有读取电机转速的作用,是组成pid控制的必要部分,编码器上有6个口,如图接线读取转速的主流方法有两种:pulsein读取脉冲的时间,或为外部中断读取单位时间触发霍尔传感器的次数.
两种方法各有优劣:

pulsein的原理是计时,把它的源码扒出来看后,我发现pulsein需要延时来达到测量目的,会阻塞程序运行.而且时间是一个标量,无法测量转向.
但pulsein也有优点,它代码简单,不需要中断,通常中断会带来各种神奇的问题.
而外部中断的优缺点都十分突出.外部中断不会阻塞程序运行,而且可以通过比较AB相触发的相位差来测量转向.
其缺点上文也提到了,会影响计时器,从而导致pulsein精度降低很多,进一步导致传统的PWM接收机信号不能被正确读取.

那么接下来就讲讲两种读取的方法
1.pulsein计时读取
可以看到有AB两相,输出的占空比可直接用arduino中的pulseIn读取。其实我们用一相就够了,因为正转还是反转我们不关心,而且我们也读取不到,我们只要知道转速。
这里注意转速越慢,值越大,因为相同路程,速度越大,时间越少,而pulseIn输出的是脉冲的长度,单位微秒。
微信图片_20200223160224.jpg
先写一段测试代码,让电机转起来,然后测转速
这段代码只接了A相
[mw_shl_code=arduino,true]void setup() {
  // put your setup code here, to run once:
  pinMode(22,INPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  analogWrite(3,255);
  digitalWrite(4,0);
  int a=pulseIn(22,HIGH);
  Serial.println(a);
  delay(1000);
}[/mw_shl_code] b.PNG
这是串口监视器读到A相的结果,268,264,254,263都是pwm输出满量程时的读数,看来这个电机不是很稳定,这也是引入pid的原因之一。
我发现这个量程不是很合适,于是映射到0~50。
那么就有朋友好奇B相输出是什么呢
其实跟A相差不多,他们差了一相
两相的作用是用来测正反的,我也不需要。
这是正反转pwm从50-255电机测速器输出的值
我们可以看到值是反的
2.外部中断
该方法需要四个外部中断的接口(对于N个需要调速的电机,就需要N个端口),这里就把很多控制板给刷掉了,一般常见的UNO,NANO均不符合此条件.而拥有6个外部中断的MEGA就可以使用.
首先讲讲原理.外部中断的本质是在loop运行时如果其条件被触发(如某个端口的电平变化),就会跳出主程序去运行外部中断指定的函数.这里如果检测到A相电平变化了,说明磁环上的一个磁极经过了霍尔传感器,让计数器++,然后我们有了总共的脉冲数,但我们要的是速度,也就是单位时间的脉冲数,于是我们就有了这样的思路:用一个t变量,t=系统时间,每过了30ms将in1赋值给M1S(速度变量),并将in1清零,用于下一个30ms的读取.
三 程序实现
这里以pulsein为例,外部中断的代码见
arduino pid麦克纳姆轮小车程序详解https://www.arduino.cn/forum.php ... &fromuid=176749
(出处: Arduino中文社区)
-----------------------正文----------------------------
[mw_shl_code=arduino,true]map(in1,620,210,0,50);[/mw_shl_code]
这句话实现了将转速并映射到0-50.
[mw_shl_code=arduino,true]in1=pulseIn(ma_in,HIGH,2000);[/mw_shl_code]
这句话读取了转速
然后看看pid核心代码(没d)
[mw_shl_code=arduino,true]float last1;
void M1PWM(int ref,int real)
{
  float error=0,PWMINC=0;
  error=ref-real;
  PWMINC=Kp*(error-last1);
  M1PWMOUT+=(PWMINC+Ki*error);
  if(M1PWMOUT<50)
  M1PWMOUT=50;
  if(M1PWMOUT>255)
  M1PWMOUT=255;
  last1=error;
}[/mw_shl_code]

其实pid的核心代码很短,也不难理解,主要是调用很麻烦
调用代码如下
[mw_shl_code=arduino,true] int M1S,M2S,M3S,M4S,ref,in1,in2,in3,in4;
    ref=max(map(a,sa,2007,0,50),15);
    in1=pulseIn(ma_in,HIGH,2000);
    in2=pulseIn(mb_in,HIGH,2000);
    in3=pulseIn(mc_in,HIGH,2000);
    in4=pulseIn(md_in,HIGH,2000);
    if(in1==0)
    M1S=0;
    else
    M1S=map(in1,620,210,0,50);
    if(in2==0)
    M2S=0;
    else
    M2S=map(in2,620,210,0,50);
    if(in3==0)
    M3S=0;
    else
    M3S=map(in3,620,210,0,50);
    if(in4==0)
    M4S=0;
    else
    M4S=map(in4,620,210,0,50);     
    M1PWM(ref,M1S);
    M2PWM(ref,M2S);
    M3PWM(ref,M3S);
    M4PWM(ref,M4S);
    forward();
    printf("参考转速:%d ",ref);
    printf("前进,M1转速:%d  ",M1S);
    printf("前进,M2转速:%d  ",M2S);
    printf("前进,M3转速:%d  ",M3S);
    printf("前进,M4转速:%d\n",M4S);[/mw_shl_code]
这是前进的代码,也就是4个轮都正转。
其他都类似,见总代码
四 调试
d.PNG
实测0.几秒就能稳定,完全足够了
图是我不断变速的曲线,不是一个速度用了这么久
如果你觉的稳定的慢了,就把Kp弄大,反之亦然。当然kp太大了就会很飘。
调好kp后调ki,直到稳差较小。
--------------------------------------------------------------------------------------------------
总代码
--------------------------------------------------------------------------------------------------
游客,如果您要查看本帖隐藏内容请回复
最后还有个问题就是一开始四个电机的转速不一样,导致一开始会有一些角度偏差,希望有大佬能帮忙解决这个问题,谢谢。
如果有问题或建议欢迎回复
当然arduino自带了PID库,也 十分好用,程序详解中使用的就是自带的库.






签到天数: 6 天

[LV.2]偶尔看看I

发表于 2020-2-28 08:43 | 显示全部楼层
收藏了慢慢看

该用户从未签到

 楼主| 发表于 2020-2-28 09:03 | 显示全部楼层
本帖最后由 wzzzq 于 2020-7-31 17:53 编辑

谢谢观看

该用户从未签到

 楼主| 发表于 2020-2-28 09:05 | 显示全部楼层
还有个pid分段控制的帖子
PID进阶——分段PI
https://www.arduino.cn/forum.php ... &fromuid=176749
(出处: Arduino中文社区)

该用户从未签到

 楼主| 发表于 2020-4-16 13:57 | 显示全部楼层
经过一段时间的观察,还是arduino自带的PID库好使

该用户从未签到

发表于 2020-7-26 17:27 | 显示全部楼层
后面四个轮子转速一样吗

该用户从未签到

发表于 2020-7-26 17:29 | 显示全部楼层
用的硬件有哪些?我去买一份试试

该用户从未签到

发表于 2020-7-26 17:55 | 显示全部楼层
33号引脚和20号引脚用来干哈的?

该用户从未签到

 楼主| 发表于 2020-7-30 17:25 | 显示全部楼层
嘉哥呀呀呀 发表于 2020-7-26 17:27
后面四个轮子转速一样吗

用了pid调制能使四个电机转速基本一致,会比开环调制好很多,但也做不到绝对的完全一样,肉眼基本看不出太大的偏移

该用户从未签到

 楼主| 发表于 2020-7-30 17:26 | 显示全部楼层
嘉哥呀呀呀 发表于 2020-7-26 17:29
用的硬件有哪些?我去买一份试试

我会再发一个帖子具体的讲述的,敬请关注
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

arduino程序设计基础 blinker物联网解决方案

热门推荐

关于红外的求助
关于红外的求助
为什么红外发射出的跟我设定的不一样,如图,我两个板子一个发射,一个接收,我想要发
【Arduino】168种传感器模块系列实验(104)---MAX30102手腕心率
【Arduino】168种传感器模
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
[Arduino物联网开发实战5]云端历史数据存储与查看
[Arduino物联网开发实战5]
blinker提供了历史数据存储与图表查看数据的功能。 设备端开发 在blinker的设计下,设
程序卡着不动,最后打印Freeheap 25427
程序卡着不动,最后打印Fr
各位有见过这个错误吗,程序跑到这里卡着不动
通过定时器读取串口数据出错
通过定时器读取串口数据出
各位大佬好,本人通过树莓派和Arduino通信,一边接收Arduino传感器数据,一边给Arduin
Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
快速回复 返回顶部 返回列表