目录
小车由于在自身重力或摩擦力等因素影响下,实际转速会小于输入转速,导致里程计误差较大。在较小速度情况下可能会出现堵转情况,导致小车在导航过程中输入小幅度角度转动的时候,无法及时调整进而撞到障碍物,因此需要给电机加PID速度环闭环控制。
一、位置式PID和增量式PID
PID有位置式和增量式,两者各有优缺点。
位置式PID的输出直接对应系统输出,响应速度快,适用于需要快速响应的系统,但缺点是容易出现积分饱和:当惯性系统达到最大输出值时,由于系统还未达到目标位置,error一直存在,如果没有积分限幅积分项会不断累加,一旦开始反向变化,系统需要一定时间从饱和区退出,会导致系统超调或震荡。
增量式PID不需要做累加,控制量增量的确定仅与最近三次偏差采样值有关,计算误差对控制量计算的影响较小,当输出达到最大值时,增量自动归零,避免持续饱和,适用于需要平滑控制的系统,如步进电机。缺点是由于输出的是控制量的增量,响应不如位置式PID,更难直接用于控制一些需要精确位置调整的系统
二、问题分析
我们先采用位置式PID来进行调试,下面给出代码
double PID::compute_positional(double setpoint, double measured_value, short outLimit, short IntegralLimit) {
outLimit = config_outLimit;
IntegralLimit = config_IntegralLimit;
double error = setpoint - measured_value;
// 当目标值为零时,重置积分项
if (setpoint == 0.0) {
integral_pos = 0.0;
}
double proportional = kp * error;
integral_pos += ki * error;
double derivative = kd * (error - prev_error_pos);
// 积分限幅
if (integral_pos > IntegralLimit)
integral_pos = IntegralLimit;
else if (integral_pos < -IntegralLimit)
integral_pos = -IntegralLimit;
double output_unclamped = proportional + integral_pos - derivative;
double output = output_unclamped;
// ROS_INFO("integral_pos=%.1f,output=%.1f",integral_pos,output);
// 输出限幅
if (output > outLimit) output = outLimit;
else if (output < -outLimit) output = -outLimit;
// 抗积分饱和:输出被限幅时调整积分项
if (output_unclamped != output) {
integral_pos = output - proportional - derivative;
}
prev_error_pos = error;
return output;
}
小车最大pwm为900,最大线速度0.3m/s,先给小车一个KP=2000,KI和KP都为0,输出限幅和积分限幅为900,目标速度为0.2,输出左右两轮速度波形图如下:
小车速度始终达不到0.2,这里有个矛盾的地方:如果小车速度达到0.2了,则偏差值error为0,那么KP*error为0,output也为0,那么速度为0,速度为0那么error值又不为0,就会有速度输出,形成矛盾,为此我记录了几组数据,得出以下公式:实际速度=f+(目标速度-f)/2,f为小车克服摩擦力的速度,当f为0时,小车实际速度为目标速度一半,此时加入KI,先给个较小值20
可以看到小车速度逐渐到达0.2,分析后得知:小车电机速度环的KP可以帮助小车预先接近目标速度,再此基础上,KI开始进行积分,从积分开始到目标速度的那块红色阴影面积即为最终的pwm值,如下图,当达到目标速度时KP和KI稳定在目标速度进行调节使小车稳定在目标速度
三、参数调节
个人调参经验:KP<=最大output/最大误差。KI影响增长到目标转速的快慢,但太快会过冲,我对小车的要求使快速到达目标转速,建议大家对着波形调试,将KI调到速度波形可快速到达目标转速,或稍微过冲。KD我个人不建议加,因为小车电机要快速达到目标转速,但KD会抑制速度过快增长,所以KD给0。
KP=2000,KI=200效果:
位置式和增量式效果对比,上面是位置式,下面是增量式,相同参数情况下:
个人觉得差别不大,但增量式在参数较小的情况下变化更平滑,KP=200,KI=20:
增量式PID代码:
double PID::compute_incremental(double setpoint, double measured_value, short outLimit) {
// 当目标速度为0时,重置历史值
if (setpoint == 0.0) {
prev_output_inc = 0.0;
prev_error_inc1 = 0.0;
prev_error_inc2 = 0.0;
}
double error = setpoint - measured_value;
double output = prev_output_inc + kp * (error - prev_error_inc1) +
ki * error + kd * (error - 2 * prev_error_inc1 + prev_error_inc2);
prev_error_inc2 = prev_error_inc1;
prev_error_inc1 = error;
prev_output_inc = output;
// 输出限幅
if (output > outLimit) output = outLimit;
else if (output < -outLimit) output = -outLimit;
return output;
}