有关两种PID调参的经验分享——位置式与增量式
PID是一种用于精确控制的控制算法,通过对比例P,积分I,微分D参数调整以达到最佳的控制效果。
需要注意的是,并不是每一次都三个参数一起出场,有时只需要其中一个或两个就可以达到很好的控制效果。
PID分为位置式与增量式,各有优点,稍后将分别介绍,此处先分别展示,两种PID的公式:
-
位置式
outk 表示 k 时刻的输出值;
ek 表示 k 时刻偏差量;
ej 表示 j 时刻的偏差量,这一部分表示自系统开始运行后的误差的累积;
ek-1显然表示 k 时刻上一时刻的误差。 -
增量式
此处的ek-2表示上上时刻的误差量
位置式PID
参数说明
首先来说 P,他带给系统一个基础的运行能力,使得系统可以往预期的方向发展;这个参数需要适中,如果过大,则会超出目标,并且来回震荡。
积分 I 则起到了补足 P 的作用,他对小幅度的偏差具有明显的调节作用;如果 I 过小则系统存在小幅度偏差时不能回中,过大则会超出目标,且系统表现迟钝。
微分 D 主要起到了限制作用,阻止系统超出预期;过小时会超出目标,过大时则会高频抖动,并且对误差敏感。
调试方法
先调整比例 P,给予系统基础的运行能力,当调整到开始震荡时,减小一点,然后调整积分 I。
积分 I 应当限制他的幅度,这个幅度选定需要根据系统的回复能力决定,可以先自行带入一个设置的阈值,计算后与代表系统回复能力的数值比较,然后得出一个适中的 I,在此基础上进行调整,有时这个值调整完成后就可以发现系统已经可以达到预期,此时可以在根据需要调整微分 D。
微分 D 一般调整到震荡后在减小一些唯宜。
当然,对于 I 与 D 的调整顺序并不是固定的,也可以使 P 过冲,然后调整 D 来限制,之后微调 I 来补足。
当 I 充足后可适当减小P,D 充足后可适当增加 P,此处的增加与减小都不是大踏步行进,而是细微调整。
代码与案例展示
s16 palstance_i_max = 100; // 角速度环积分限幅
float fly_dt = 0.003; // 时间
void roll_palstance_c(float target_p_roll, float gyroX)
{
static float last_offset_rp = 0; // 上一次的偏差量
static s16 palstance_i_rp = 0; // 积分
float palstance_offset = gyroX + target_p_roll; // 偏差量
palstance_i_rp += palstance_offset * fly_dt;
if (palstance_i_rp > palstance_i_max)
{
palstance_i_rp = palstance_i_max;
}
else if (palstance_i_rp < -palstance_i_max)
{
palstance_i_rp = -palstance_i_max;
}
roll_pid_out = (palstance_offset * p_p + palstance_i_rp * p_i + (palstance_offset - last_offset_rp) * p_d / fly_dt);
// roll_pid_out = limit(roll_pid_out, 200, -200);
last_offset_rp = palstance_offset;
}
这是一个四轴无人机上角速度环调整的例子,
- palstance_offset 即为误差,palstance_i_rp 是误差的积分
- 我为积分限幅为100,因为我发现当达到这个值时就有比较强的回复力
- 公式中的 fly_dt 表示每 3ms 调整一次,实际并不需要加这个 fly_dt,只有在每次进入计算的时间不等时才需要,此处是为了公式的完整性
增量式PID
参数说明
从公式中增量式的 P 对应着位置式的 D,而 I 又对应着 P。
此处 P 过大会引起系统敏感,高频震荡,过小又不能达到目标。
I 则起到了补足的作用。
调试方法
首先调整 I,给定一个参数后观察是否会收敛,之后尝试增大参数,观察收敛速度,速度加快则增大,速度减慢则减小,当调整到有收敛趋势,且收敛速度适中时,加入参数 P。
P 的作用是一直震荡,给定一个参数后观察震荡幅度是否减小,同理,减小增大,增大减小。
需要注意的是,此处的 I 与 P 不是一劳永逸的,想要或好的效果需要不断调整两个参数,例如当观察到调整 P 的效果不明显时,可以适当减小 I 之后再次调整 P。
- 对于增量式PID中的 D 来讲,我个人调试时发现作用不大,不管设置的多么不合理的值都不会产生明显的变化。
代码与案例展示
void servo_horizontal_trun(uint16 target_h, uint16 h_x)
{
error_h = h_x - target_h; //本次误差
static sint16 last_error_h = 0; //上一次的误差
static sint16 last_last_error_h = 0; //上上次的误差
static float last_duty_h = SERVO_MID_H; //上一次的输出
static float temp_h = SERVO_MID_H; //这里是将期望值设为初值
if (h_x != 0)
{
offset_intergral_h += error_h;
if (offset_intergral_h > horizontal_intergral_max)
{
offset_intergral_h = horizontal_intergral_max;
}
else if (offset_intergral_h < -horizontal_intergral_max)
{
offset_intergral_h = -horizontal_intergral_max;
}
temp_h -= (p_h * (error_h - last_error_h) + i_h * error_h + d_h * (error_h - 2 * last_error_h + last_last_error_h));
}
else
{
temp_h = SERVO_MID_H;
}
//这部分与pid无关,只是对结果的进一步处理
//===================================================
if (abs(error_h) <= 3)
{
offset_intergral_h = 0;
}
if (temp_h > SERVO_H_MAX)
{
temp_h = SERVO_H_MAX;
}
else if (temp_h < SERVO_H_MIN)
{
temp_h = SERVO_H_MIN;
}
//===================================================
last_duty_h = temp_h;
duty_h = temp_h;
pwm_set_duty(servoP_H, duty_h);
last_last_error_h = last_error_h;
last_error_h = error_h;
}
后记
设置参数与变量时,数据类型很重要,使用浮点型可以保留更多精度,最好在计算时都使用浮点型,我曾因为想当然的认为输出是整型,而错误的使用整型存储变量,导致丢失的精度,从而做了一整天的无用功。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!