欢迎光临
我们一直在努力

倒立摆的串级控制:让你的倒立摆屹立不倒

# 倒立摆串级控制:速度环如何给直立环“改目标”

两轮倒立摆有两个目标:摆杆不能倒,小车也不能一直向前或向后漂。直立环负责前者,速度环负责后者。

但这两个环不能各自算一份 PWM 再相加,否则它们会争抢电机。本文讨论的串级结构是:

```text

速度环 PI(外环,10 ms)

    -> 输出目标角度偏移

    -> 作为直立环的目标

直立环 PD(内环,5 ms)

    -> 输出两轮共用的平衡 PWM

```

关键词是“串行”:速度环的输出进入直立环,成为它的一部分目标,而不是在内环之外另加一个电机指令。

## 1. 为什么采用串级

倒立摆主要通过改变车轮加速度,把支撑点移动到重心下方。摆杆角度变化快,因此内环必须用更短周期纠正倾倒;平均速度变化慢,因此由外环慢慢修正。

```text

速度太快 -> 外环让目标倾角向后移 -> 直立环驱动车轮减速

速度太慢 -> 外环让目标倾角向前移 -> 直立环驱动车轮加速

```

外环只改变“应该倾斜多少”,真正的电机输出仍由直立环统一计算。这样外环动作较慢、幅度较小,不会直接破坏摆杆的快速平衡。

## 2. 三个量不要混淆

```c

float ang;          /* 摆杆当前实际角度 */

float s_ang_offset; /* 速度环给出的目标角度偏移 */

float out;          /* 直立环输出的平衡 PWM */

```

三者关系是:

```text

实测角度 ang 与目标角度 s_ang_offset 比较

    -> 误差 ang – s_ang_offset

    -> 直立环计算出 out

```

`s_ang_offset` 不是摆杆当前偏离了多少度,而是速度环希望把平衡点向前或向后移动多少度。

例如:

```text

s_ang_offset = 0°  -> 希望摆杆平衡在 0°

s_ang_offset = 2°  -> 希望摆杆平衡在 2°

```

## 3. 控制周期

当前工程以 5 ms 为基本控制周期:

```text

直立环:5 ms,200 Hz

速度环:10 ms,100 Hz

```

速度环在每两次直立环计算中执行一次:

```c

s_speed_div++;

if (s_speed_div >= 2)

{

    s_speed_div = 0;

    s_ang_offset = PID_Calc(&s_pid, s_target, s_speed);

}

```

内环必须更快,因为它直接处理摆杆倾倒;外环更慢,因为它只负责平均速度的长期漂移。

## 4. 外环:速度 PI 输出目标角度

编码器每 5 ms 读取一次增量,左右轮增量取平均,得到车辆平均速度:

```c

int dl = Enc_ReadLeftDelta();

int dr = Enc_ReadRightDelta();

s_speed = (dl + dr) * 0.5f;

```

因此 `s_speed` 的单位是:

```text

编码器计数 / 5 ms

```

目标速度 `s_target` 必须使用完全相同的单位。若目标速度给的是 m/s,应先换算:

```c

s_target = speed_mps * ENC_COUNT_PER_METER * CTRL_DT;

```

外环每 10 ms 计算一次:

```c

s_ang_offset = PID_Calc(&s_pid, s_target, s_speed);

```

这里选择的符号约定是:

```text

速度误差 = 目标速度 – 实测速度

外环输出 = 目标角度偏移

```

当前外环输出限制为 `±4°`,积分项最多贡献约 `±1.2°`:

```c

PID_Init(&s_pid, SPEED_KP, SPEED_KI, SPEED_KD, 4.0f);

pid.integral_max = 0.3f * out_max;

```

这两个限制很重要。外环一旦要求摆杆长时间大角度倾斜,直立环会先失去稳定性,速度也就无从修正。

## 5. 内环:直立 PD 跟踪外环目标

直立环的核心只有一行:

```c

float out = (ang – s_ang_offset) * s_bal_kp

          + rate * s_bal_kd;

```

写成公式是:

```text

角度误差 = ang – s_ang_offset

直立输出 = Kp × 角度误差 + Kd × rate

```

当前参数:

```text

Kp = 600

Kd = 35

```

这里的 `ang` 和 `s_ang_offset` 都是角度,单位一致。`rate` 是摆杆角速度,当前从角度差分得到:

```text

rate = (angle[k] – angle[k-1]) / 0.005

```

角度差分噪声较大,所以角速度的平滑和滤波会直接影响 Kd 的可用范围。

直立环通常只用 PD:

1. 直立姿态需要快速响应,积分会引入滞后;

2. 零速度下的静态角度偏差可交给外环修正;

3. D 项提供阻尼,减少摆杆来回摆。

## 6. 完整的串行主链

省略测速底层、显示和转向混合后,核心循环可以简化为:

```c

void Control_Loop(void)

{

    Angle_Update();

    int dl = Enc_ReadLeftDelta();

    int dr = Enc_ReadRightDelta();

    s_speed = (dl + dr) * 0.5f;

    float ang  = Angle_Get();

    float rate = Angle_GetRate();

    /* 外环:10 ms 执行一次,输出目标角度 */

    s_speed_div++;

    if (s_speed_div >= 2)

    {

        s_speed_div = 0;

        s_ang_offset = PID_Calc(&s_pid, s_target, s_speed);

    }

    /* 内环:5 ms 执行一次,输出平衡 PWM */

    float out = (ang – s_ang_offset) * s_bal_kp

              + rate * s_bal_kd;

    if (out >  7200.0f) out =  7200.0f;

    if (out < -7200.0f) out = -7200.0f;

    Motor_Left((int)out);

    Motor_Right((int)out);

}

```

数据流可以概括为:

```text

编码器 -> 平均速度 -> 速度 PI -> 目标角度

                                  ↓

电位器 -> 当前角度 -> 直立 PD -> 平衡 PWM

                         ↑

                       角速度

```

速度环和直立环共享“角度”这条通道,这正是串级连接。

## 7. 一个数值例子

假设小车比目标速度更快,速度环因此给出:

```text

s_ang_offset = -1°

```

这表示直立环暂时把平衡目标改成向后倾斜 1°,以帮助小车减速。

某一时刻摆杆状态为:

```text

ang  = +2°

rate = +10°/s

```

先算角度误差:

```text

ang – s_ang_offset = 2° – (-1°) = 3°

```

再算直立输出:

```text

out = 600 × 3 + 35 × 10

    = 1800 + 350

    = 2150

```

如果直接比较 `ang=2°` 和 0°,会得到 1200;串级结构下得到 2150。多出的输出不是速度环直接加的 PWM,而是目标角度改变后,直立环重新计算的结果。

## 8. 正确与错误的连接方式

常见错误是并联:

```c

out = angle_pid(ang) + speed_pid(speed);

```

这里两个控制器都在直接争抢电机。速度环可能为了追速度持续增加 PWM,而直立环同时试图消除倾角,两个目标相互拉扯。

当前的串级结构是:

```c

s_ang_offset = speed_pid(s_target, s_speed);

out = angle_pid(ang, s_ang_offset, rate);

```

判断内环目标是否正确,可检查:

```text

s_ang_offset = 0 时,直立环希望平衡在 0°

s_ang_offset 为正时,直立环希望平衡在正角度

s_ang_offset 为负时,直立环希望平衡在负角度

```

若不满足,先检查 `ang – s_ang_offset` 的符号和角度方向,不要急着调参数。

## 9. 调参顺序

### 第一步:方向和单位

确认角度增大方向、电机前进方向、编码器计数方向和速度误差符号一致。方向错误时,增大任何 PID 参数都无法得到稳定控制。

把外环暂时旁路,令 `s_ang_offset=0`,先让直立环单独工作。

### 第二步:内环 Kp

Kd 先设为零,逐步增加 Kp:

– 太小:摆杆倾倒时车轮追不上;

– 合适:能来回修正,但可能持续摆动;

– 太大:高频抖动、电机嗡鸣或 PWM 快速饱和。

### 第三步:内环 Kd

加入 Kd 抑制摆动:

– 太小:摆杆或车头反复摆动;

– 合适:回正过程更利落;

– 太大:角度噪声被放大,电机高频抖动。

Kd 调不上去时,先检查角度噪声和角速度质量。

### 第四步:外环 Kp

恢复速度环,先令 `Ki=0`。逐渐增加速度 Kp:

– 太小:被推一下后长时间漂移;

– 合适:小车能缓慢回到目标速度;

– 太大:为了追速度产生明显的前后摆动。

外环输出必须保持在 `±4°` 内。

### 第五步:外环 Ki

最后从小值开始增加 Ki,消除摩擦和左右轮差异造成的长期偏差。若积分一开启就把目标角度推向限幅,优先检查积分符号、目标速度单位和积分限幅。

## 10. 常见现象及排查

| 现象 | 优先检查 |

|—|—|

| 内环高频抖动 | 角度噪声、Kd、内环 Kp、机械间隙 |

| 前后低频摆动 | 外环 Kp 过大,或外环周期过短 |

| 始终向一个方向漂 | 测速方向、速度误差符号、外环积分 |

| 外环长期卡在 `±4°` | 内环无法实现目标角、参数比例或方向错误 |

| 速度几乎为零但目标角明显偏移 | 积分残留、积分限幅或速度单位错误 |

| 直立稳定但速度不受控 | 外环未执行、反馈符号错误或目标速度不一致 |

## 11. 小结

倒立摆串级控制的关键不是再写一个 PID,而是明确两个环的职责:

```text

外环慢:控制平均速度,输出目标倾角

内环快:控制摆杆姿态,输出电机 PWM

```

速度环通过改变目标角度,间接让直立环带着小车加速或减速。只要保持这条串行数据链、符号约定和输出限幅一致,直立与速度就可以分别调试,也能避免两个控制器直接争抢电机。

##12.实际效果演示视频

倒立摆小车

赞(0)
未经允许不得转载:171主机测评 » 倒立摆的串级控制:让你的倒立摆屹立不倒
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址