4、电流环PI控制器设计:连续域到离散域的映射,抗积分饱和实现

电流环是FOC控制中最内层的环,也是决定动态响应速度的关键。说白了,电流环PI控制器设计得好不好,直接决定了电机能不能跑得稳、跑得快。我刚开始做FOC时,在连续域里调得挺顺的参数,一搬到MCU上就各种不对劲。后来才明白——连续域到离散域的映射,不是简单抄个公式就完事的。

4.1 连续域PI控制器的基本形式

先回顾一下连续域里PI控制器的标准形式:

u(t) = Kp * e(t) + Ki * ∫e(t)dt

其中:

  • Kp:比例增益,决定响应速度
  • Ki:积分增益,消除稳态误差
  • e(t):电流误差(给定值 - 反馈值)

在FOC电流环里,我们通常对d轴和q轴分别做PI控制。d轴控制励磁分量,q轴控制转矩分量。两轴的PI参数可以独立整定,但结构是一样的。

关键点:电流环的带宽通常设置为速度环的5~10倍。我一般先把电流环带宽定在1~2kHz,再根据实际响应微调。

4.2 离散化方法的选择

MCU只能处理离散信号,所以必须把连续域的PI控制器映射到离散域。常用的方法有三种:

方法 公式(积分项) 特点 适用场景
前向欧拉 u[k] = u[k-1] + Ts * e[k] 实现简单,精度一般 采样率远高于带宽时可用
后向欧拉 u[k] = u[k-1] + Ts * e[k] 数值稳定性好 大多数工程场景的首选
双线性变换(Tustin) u[k] = u[k-1] + (Ts/2)*(e[k] + e[k-1]) 精度最高,无频率失真 对相位精度要求高的场合

我个人习惯用后向欧拉法。为什么?因为它在MCU上实现简单,数值稳定性好,而且对于电流环这种高采样率的场景(通常10~20kHz),三种方法的差异其实不大。我在一个项目里试过双线性变换,效果确实好一点,但代码量多了不少,调试也麻烦。后来我就一直用后向欧拉了。

4.3 离散域PI控制器的实现

用后向欧拉法离散化后,PI控制器的递推公式长这样:

// 位置式PI(直接计算输出)
u[k] = Kp * e[k] + Ki * Ts * Σe[k]

// 增量式PI(计算输出变化量)
Δu[k] = Kp * (e[k] - e[k-1]) + Ki * Ts * e[k]
u[k] = u[k-1] + Δu[k]

在FOC电流环里,我推荐用增量式PI。原因有两个:

  • 增量式只计算变化量,不容易出现大幅跳变
  • 抗积分饱和实现起来更自然

小技巧:实际代码里,我会把Ki * Ts预先算好,存成一个系数。这样每次中断里只需要做乘法和加法,省掉一次除法。GD32的FPU虽然快,但能省则省嘛。

4.4 抗积分饱和的实现

积分饱和是个老生常谈的问题,但在FOC电流环里特别要命。你想想看,电机启动时电流误差很大,积分项一路狂飙。等电流追上给定值时,积分项已经积了一大堆,导致输出超调,甚至震荡。

我遇到过最夸张的一次,积分饱和让电机在启动时直接过流保护跳了。查了半天才发现是积分项没处理好。

常用的抗积分饱和方法有几种:

方法 原理 实现复杂度 效果
积分限幅 直接限制积分项的上下限 低 一般,可能仍会饱和
积分冻结 输出饱和时停止积分 低 较好,常用
反向计算 饱和时重新计算积分项 中 好,但需注意稳定性
条件积分 误差大时减小积分增益 高 最好,但调参复杂

我在工程里最常用的是积分冻结加积分限幅的组合。代码实现如下:

// 增量式PI + 抗积分饱和
float pi_current_loop(float error, float *integral, float Kp, float Ki, float Ts, float limit) {
    float output;
    float delta_u;

    // 计算增量
    delta_u = Kp * (error - prev_error) + Ki * Ts * error;
    prev_error = error;

    // 计算输出
    output = prev_output + delta_u;

    // 输出限幅
    if (output > limit) {
        output = limit;
        // 积分冻结:不更新积分项
    } else if (output < -limit) {
        output = -limit;
        // 积分冻结
    } else {
        // 正常更新积分项
        *integral += Ki * Ts * error;
        // 积分限幅
        if (*integral > INTEGRAL_LIMIT) *integral = INTEGRAL_LIMIT;
        if (*integral < -INTEGRAL_LIMIT) *integral = -INTEGRAL_LIMIT;
    }

    prev_output = output;
    return output;
}

注意:积分限幅的阈值INTEGRAL_LIMIT不能随便设。我一般取输出限幅的50%~80%。设太小了积分没效果,设太大了等于没限幅。具体值得根据实际调试来定。

4.5 连续域到离散域的映射流程

把整个设计流程串起来,大概是这样的:

  1. 确定电流环带宽:根据电机参数和系统要求,定出目标带宽(比如1kHz)
  2. 计算连续域PI参数:用极点配置或经验公式算出Kp和Ki
  3. 选择离散化方法:推荐后向欧拉法
  4. 离散化PI参数:把Kp和Ki映射到离散域
  5. 实现抗积分饱和:选择合适的方法并编码
  6. 调试验证:用示波器或DA输出看电流响应

下面这张图把整个映射关系画清楚了:

连续域到离散域映射流程 连续域PI u(t) = Kp·e(t) + Ki·∫e(t)dt 离散化方法 后向欧拉 / Tustin 离散域PI 增量式/位置式 抗积分饱和 积分冻结 + 积分限幅 代码实现 GD32 FOC中断服务函数 关键参数 • 采样周期Ts:50~100μs • 带宽:1~2kHz • 积分限幅:50%~80%输出限幅 避坑提醒 • 积分冻结要判断输出限幅 • Ki·Ts预计算,避免除法 • 注意变量类型,用float

4.6 工程部署中的注意事项

把PI控制器部署到GD32上时,有几个坑我得提醒你:

  • 变量类型:电流环的误差和输出都用float,别用int。GD32有FPU,浮点运算不慢。
  • 中断优先级:电流环中断优先级要设得最高。我一般设成0(最高),速度环设成1。
  • 执行时间:电流环中断服务函数要在采样周期内跑完。20kHz采样就是50μs,代码要精简。
  • 参数整定:先在连续域调好参数,再离散化。别直接在MCU上盲调。

经验之谈:我曾经在一个项目里把电流环中断优先级设错了,导致电流环被其他中断打断,电流波形全是毛刺。查了两天才发现是优先级的问题。嗯,从那以后我每次建工程第一件事就是检查中断优先级配置。

好了,这一章的内容就这些。连续域到离散域的映射是FOC工程化的基础,抗积分饱和则是保证系统稳定性的关键。把这两块吃透了,电流环的调试就会顺手很多。