ROS机器人底盘(6)-电机PID
通过PID对电机转速进行闭环控制。
1.PID控制的原理
在工程实际中,PID控制是应用最为广泛控制方式。PID为比例、积分、微分的缩写。当被控制的系统的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象﹐或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量来对系统进行控制。
比例(P)控制
比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。
积分(I)控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。
微分(D)控制
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。 自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。
2.实际模型
对于3WD全向小车,在对整体运动进行解算后得到各个轮子的期望转速Vd,以这个转速作为PID控制的输入,输出为控制电机的PWM值,反馈则为通过编码器反馈值计算出的实际速度V。闭环系统如下:
PID模型
在实际操作中,由于是通过程序进行PID的计算,无法达到连续的积分和微分计算,所以用离散处理。给定PID控制频率,周期性地进行PID控制计算。
主要参数:
pid_interval 控制周期 ms
kp 比例系数
ki 积分系数
kd 微分系数
ko 统一放大缩小比例
3 具体实现
30ms控制周期,C代码如下:
pid.h
#ifndef PIBOT_PID_H_
#define PIBOT_PID_H_
class PID{
public:
PID(float* input, float* feedback, float kp, float ki, float kd, unsigned short max_output);
short compute(float interval);
void clear();
private:
float kp, ki, kd;
unsigned short max_output;
float* input;
float* feedback;
float error;
float integra;
float derivative;
float previous_error;
};
#endif
pid.cpp
#include "pid.h"
#include "board.h"
PID::PID(float* _input, float* _feedback, float _kp, float _ki, float _kd, unsigned short _max_output)
:input(_input), feedback(_feedback), kp(_kp), ki(_ki), kd(_kd), max_output(_max_output){
clear();
}
void PID::clear(){
error = integra = derivative = previous_error =0;
}
short PID::compute(float interval){
error = *input - *feedback;
integra = integra + error*interval;
derivative = (error - previous_error) / interval;
previous_error = error;
if (ki != 0)
if (integra < -max_output/ki)
{
//printf("integra clear\r\n");
integra = -max_output/ki;
}
if (integra > max_output/ki)
{
//printf("integra clear\r\n");
integra = max_output/ki;
}
float val = error*kp + integra*ki + derivative*kd;
if (val < -max_output)
val = -max_output+1;
else if (val > max_output)
val = max_output-1;
return val;
}