简介这份资源面向电力电子与嵌入式控制方向的学习者聚焦功率因数校正PFC电路的PID控制实现解决从Simulink仿真到C语言离散化代码落地的衔接问题。包内共5个文件包含2个C源文件、1个TLC模板、1个TXT说明和1个SLX仿真模型压缩包约35KB体量轻巧但覆盖了模型搭建、控制器离散化与代码生成的关键环节。其中SLX文件对应PFC的Simulink仿真模型C文件与TLC模板则体现PID控制器的离散化实现思路便于读者对照仿真波形理解数字控制逻辑。目前已有2358人学习下载适合正在做PFC项目、需要把理论控制算法转化为可运行代码的初学者与工程师参考也可作为课程设计或自学电力电子数字控制的辅助材料。1. PFC 的 PID 控制为什么离散化实现才是真正卡住你的那一步很多做电源的工程师第一次接触 PFC 的 PID 控制都会觉得理论没什么难的——不就是电压外环、电流内环两个 PI 调节器嘛。但真正把代码烧进 DSP 或者 MCU 里跑起来才发现事情完全不是那么回事明明仿真里波形漂亮得很实际一上电要么震荡要么响应慢得像蜗牛要么轻载时电流波形直接畸变。问题往往不出在 PID 参数本身而是出在离散化这一步。PFC 的 PID 控制用 C 语言离散化实现核心要解决的就是把连续域的控制器变成数字芯片能一个周期一个周期执行的差分方程。这里面涉及采样周期怎么定、积分项怎么抗饱和、微分项要不要加、定点数还是浮点数、中断里怎么保证实时性。适合正在做数字 PFC 的嵌入式工程师、电力电子方向的学生以及从模拟控制转数字控制的从业者。这篇笔记就按我实际做过的路径把选型、代码、参数和踩过的坑一次讲清楚。2. 从连续域到差分方程PFC 双环 PID 的离散化推导与选型2.1 为什么 PFC 通常用 PI 而不是完整 PID先把这个选型问题说清楚因为很多新手一上来就写完整 PID结果微分项把开关噪声放大到没法看。PFC 的电流环和电压环本质上都是对直流量或者低频包络做调节被控对象的带宽远低于开关频率。电流环要跟踪的是整流后的正弦半波频率是两倍工频也就是 100Hz 或者 120Hz电压环要跟踪的是输出电压的直流分量带宽通常只有十几赫兹。微分项的作用是预测误差变化趋势但它对高频噪声极其敏感。PFC 的采样信号里天然带着开关纹波就算加了硬件滤波ADC 采回来还是有残余的高频分量。微分项一作用这些噪声直接被放大占空比就会抖得厉害。所以工程上 PFC 的电压环和电流环基本都用 PI微分项要么不加要么加一个极弱的、带重滤波的微分。我一般会这样处理电流环纯 PI电压环纯 PI如果确实需要改善动态响应就在电压环输出上做前馈补偿而不是硬加 D 项。这个选择直接决定了后面离散化代码的结构。2.2 位置式 PI 和增量式 PI 的离散化公式连续域的 PI 控制器传递函数是G(s) Kp Ki/s把它离散化最常用的有两种形式位置式和增量式。位置式 PI 的差分方程是u(k) Kp * e(k) Ki * Ts * sum(e(j)) u0其中 Ts 是采样周期sum 是从 0 到 k 的误差累加u0 是初始输出。这种形式直接算出控制量的绝对值适合输出直接对应占空比的情况。增量式 PI 的差分方程是delta_u(k) Kp * (e(k) - e(k-1)) Ki * Ts * e(k) u(k) u(k-1) delta_u(k)增量式只算控制量的变化量天然带积分限幅而且不需要累加历史误差数值稳定性更好。在 PFC 里电流环我倾向用增量式因为电流给定是正弦变化的增量式对给定突变更平滑电压环用位置式因为电压环输出是电流环的幅值给定需要绝对量。这里有个关键参数Ts。PFC 的开关频率一般在 50kHz 到 100kHz但控制环的执行频率不一定等于开关频率。电流环通常每个开关周期执行一次Ts 就是 10us 到 20us电压环因为带宽低可以每几个开关周期执行一次Ts 可以是 100us 甚至 1ms。Ts 选得越小离散化越接近连续域但计算负担越重。我一般让电流环 Ts 等于开关周期电压环 Ts 等于 10 倍开关周期。2.3 离散化方法的选择前向差分、后向差分还是双线性从 s 域到 z 域有三种常用映射前向差分、后向差分、双线性变换Tustin。前向差分s (z-1)/Ts离散化后可能出现不稳定特别是 Ts 较大时。后向差分s (z-1)/(zTs)稳定性好但频率畸变较大。双线性变换s (2/Ts)(z-1)/(z1)频率响应匹配最好但计算量稍大。对于 PFC 的 PI 控制器我实测下来后向差分最省事稳定性也够。因为 PI 控制器本身没有高频谐振点频率畸变在低频段可以忽略。具体到代码后向差分离散化后的位置式 PI 就是u(k) u(k-1) Kp*(e(k)-e(k-1)) KiTse(k)这其实和增量式形式一样。所以实际写代码时增量式和后向差分离散化的位置式在数学上是等价的区别只在于你怎么组织变量。下面这张表对比三种方法在 PFC 场景下的表现离散化方法稳定性频率畸变计算量PFC 适用性前向差分差小小不推荐后向差分好中小推荐双线性好小中可用选型结论PFC 的 PI 控制器用后向差分代码简单稳定性有保障。2.4 定点数与浮点数的取舍PFC 控制芯片常见的有 TI 的 C2000 系列、STM32 的 G4 系列、还有各种专用 PFC 控制器。C2000 有浮点单元STM32 G4 也有但有些低成本 MCU 只有定点。浮点数的好处是开发快不用担心溢出和精度损失参数直接写 Kp、Ki 的物理值。定点数的好处是速度快、成本低但需要做 Q 格式定标积分累加容易溢出。我一般这样决策如果芯片有 FPU直接用 float代码可读性好调试方便。如果没有 FPU用 Q15 或者 Q12 定点但积分项要用 32 位甚至 64 位累加器防止溢出。下面代码示例我用 float因为现在主流 PFC 芯片基本都有 FPU定点版本后面会讲怎么改。3. 用 C 语言把 PFC 双环 PI 写成可复现的代码3.1 数据结构定义与初始化先把 PI 控制器的结构体定义好这样电压环和电流环可以复用同一套代码。#include stdint.h typedef struct { float Kp; // 比例增益 float Ki; // 积分增益 float Ts; // 采样周期单位秒 float out_max; // 输出上限 float out_min; // 输出下限 float integral; // 积分累加项 float err_prev; // 上一次误差 float out; // 当前输出 uint8_t mode; // 0位置式1增量式 } PI_Handle; void PI_Init(PI_Handle *h, float Kp, float Ki, float Ts, float out_min, float out_max, uint8_t mode) { h-Kp Kp; h-Ki Ki; h-Ts Ts; h-out_min out_min; h-out_max out_max; h-integral 0.0f; h-err_prev 0.0f; h-out 0.0f; h-mode mode; }这段代码定义了 PI 控制器的核心参数。Kp 和 Ki 是连续域的增益Ts 是离散化用的采样周期out_max 和 out_min 用来做输出限幅。integral 是积分累加器err_prev 保存上一次误差用于增量式计算。mode 用来切换位置式和增量式方便同一个结构体服务两个环。初始化函数把积分和误差清零输出置零。注意 out_min 和 out_max 要根据实际占空比范围来设比如电流环输出对应占空比范围是 0 到 1电压环输出对应电流幅值给定范围要根据额定功率和采样电阻来算。3.2 位置式 PI 的 C 实现与抗积分饱和位置式 PI 适合电压环因为电压环输出是绝对量。但位置式最大的坑就是积分饱和当输出已经到限幅值误差还在同一方向累积积分项会越加越大等误差反向时积分项要很久才能退回来导致响应迟钝。抗积分饱和的常用做法是当输出达到限幅时停止积分累加或者只累加让输出回退的方向。float PI_Compute_Position(PI_Handle *h, float ref, float fdb) { float err ref - fdb; float out_new; // 积分累加 h-integral h-Ki * h-Ts * err; // 计算未限幅输出 out_new h-Kp * err h-integral; // 抗积分饱和输出限幅时回退积分 if (out_new h-out_max) { out_new h-out_max; h-integral - h-Ki * h-Ts * err; // 撤销本次积分 } else if (out_new h-out_min) { out_new h-out_min; h-integral - h-Ki * h-Ts * err; } h-err_prev err; h-out out_new; return out_new; }这段代码的关键在抗积分饱和那几行。当计算出的输出超过上限先把输出钳位到上限然后把刚才加进去的积分量减掉。这样积分项不会在限幅期间继续累积误差反向时能立刻响应。参数说明Ki * Ts 是离散化后的积分系数如果 Ki 是连续域增益Ts 是采样周期这个乘积就是每个周期积分项的增加量。Kp 直接乘当前误差。out_max 和 out_min 根据实际执行器范围设定比如占空比 0.05 到 0.95。有个细节积分项回退时减的是 Ki * Ts * err而不是把整个 integral 清零。这样只撤销本次累加保留之前的积分量过渡更平滑。3.3 增量式 PI 的 C 实现与输出限幅增量式 PI 适合电流环因为电流给定是正弦增量式对给定变化更平滑。增量式天然不需要单独的抗积分饱和因为输出本身就是累加出来的只要对最终输出限幅就行。float PI_Compute_Increment(PI_Handle *h, float ref, float fdb) { float err ref - fdb; float delta_u; float out_new; // 增量式计算 delta_u h-Kp * (err - h-err_prev) h-Ki * h-Ts * err; // 累加到输出 out_new h-out delta_u; // 输出限幅 if (out_new h-out_max) { out_new h-out_max; } else if (out_new h-out_min) { out_new h-out_min; } h-err_prev err; h-out out_new; return out_new; }增量式的核心是 delta_u 的计算Kp 乘误差变化量加上 Ki * Ts 乘当前误差。然后累加到上一次输出上。输出限幅直接钳位不需要回退积分因为增量式没有独立的积分累加器输出本身就是历史累加的结果。参数说明Kp 和 Ki 的物理意义和位置式一样但增量式对 Kp 更敏感因为 Kp 乘的是误差变化量噪声会被放大。实际调试时增量式的 Kp 一般比位置式小一些。out_max 和 out_min 对应占空比范围电流环输出直接给 PWM 比较寄存器。注意一点增量式在启动时 out 初值是 0如果 out_min 设的是 0.05那启动瞬间输出会被钳到 0.05这是正常的软启动行为。如果希望从 0 开始out_min 设 0 就行。3.4 在中断里调用 PI 的时序与优先级PFC 控制代码通常放在 PWM 中断里执行电流环每个开关周期跑一次电压环降频跑。下面是一个典型的中断服务函数框架。volatile uint32_t tick_cnt 0; PI_Handle vloop; // 电压环 PI_Handle iloop; // 电流环 // PWM 周期中断假设 50kHz每 20us 触发一次 void PWM_ISR(void) { float v_ref, v_fdb, i_ref, i_fdb, duty; // 读取 ADC输出电压、电感电流、输入电压 v_fdb ADC_Read_Vout(); i_fdb ADC_Read_Iind(); // 电压环每 10 次中断执行一次即 5kHz tick_cnt; if (tick_cnt 10) { tick_cnt 0; v_ref VOUT_TARGET; i_ref PI_Compute_Position(vloop, v_ref, v_fdb); // i_ref 是电流环幅值给定需要乘正弦模板 } // 电流环每个周期执行 i_ref i_ref * sin_template; // sin_template 来自锁相环 duty PI_Compute_Increment(iloop, i_ref, i_fdb); // 写占空比 PWM_SetDuty(duty); // 清中断标志 PWM_ClearFlag(); }这段代码展示了双环的时序关系。电压环降频执行每 10 个 PWM 周期跑一次算出的 i_ref 是电流幅值给定。电流环每个周期跑把 i_ref 乘上正弦模板得到瞬时电流给定再和采样电流做 PI输出占空比。参数说明tick_cnt 是降频计数器10 对应 5kHz 电压环。sin_template 来自锁相环范围是 0 到 1 的半波正弦。PWM_SetDuty 把占空比写到比较寄存器注意占空比范围要和 PI 输出的 out_min/out_max 对应。中断优先级方面PWM 中断要设最高优先级ADC 采样完成中断次之。如果 ADC 采样和 PWM 中断有竞争建议在 PWM 中断里直接读 ADC 结果寄存器而不是等 ADC 中断这样时序更确定。4. PFC 的 PID 控制离散化实现避坑5 个血泪教训4.1 积分项溢出导致输出满占空比现象上电后占空比直接冲到最大输出电压失控功率器件发热严重。原因位置式 PI 的积分累加器是 float但如果在定点 MCU 上用 Q15 格式积分项累加多次后溢出变成一个很大的负数或者正数输出直接饱和。解决积分累加器用 32 位或者 64 位变量不要用 16 位。如果用定点积分项单独用 Q12 格式的 32 位变量输出时再移位。浮点的话一般不会溢出但也要加输出限幅兜底。4.2 采样周期和实际中断周期不一致现象仿真调好的参数实际跑起来响应偏慢或者震荡怎么调都不对。原因代码里 Ts 写的是 20us但实际中断因为其他任务阻塞执行周期变成了 25us 甚至 30us。离散化参数和实际周期不匹配等效于 Ki 变小、Kp 不变相位裕度变化。解决用示波器或者定时器翻转 IO 口实测中断执行周期。Ts 要用实测值不要用理论值。如果中断周期抖动大考虑把 PI 计算放到高优先级中断或者用定时器触发 ADC 和 PI 计算保证周期恒定。4.3 电流环给定没有乘正弦模板现象电流波形不是正弦而是方波或者畸变严重THD 超标。原因电压环输出的是电流幅值给定是个直流量。如果直接把这个直流量给电流环做给定电流环会试图把电感电流调成直流完全偏离 PFC 的工作原理。解决电压环输出必须乘以锁相环生成的正弦模板得到瞬时电流给定。正弦模板要和输入电压同相位幅值归一化到 0 到 1。锁相环的相位精度直接影响电流波形质量建议用二阶广义积分器做锁相。4.4 增量式 PI 的 Kp 给太大导致占空比抖动现象占空比在稳态时高频抖动电流波形毛刺多EMI 变差。原因增量式 PI 的 Kp 乘的是误差变化量误差里包含开关纹波和 ADC 噪声。Kp 越大噪声放大越严重占空比抖动越厉害。解决增量式的 Kp 要比位置式小一般先按位置式 Kp 的 1/5 到 1/10 来试。同时给电流采样加硬件 RC 滤波ADC 采样时刻对准电感电流的中点避开开关瞬间。如果还抖在 PI 输出后加一个一阶低通滤波截止频率设在开关频率的 1/10 左右。4.5 电压环和电流环带宽不匹配现象负载突变时输出电压恢复慢或者电流环响应太快导致电压环震荡。原因电压环带宽通常只有十几赫兹电流环带宽可以到几kHz。如果电压环带宽设得太高接近电流环带宽两个环会互相干扰系统震荡。如果电压环带宽太低负载突变时恢复时间太长。解决电压环带宽设在电流环带宽的 1/10 到 1/20。具体做法是先调电流环让电流环能快速跟踪正弦给定带宽做到 2kHz 以上。然后调电压环Kp 和 Ki 从小到大加观察负载突变时的恢复时间一般做到 10Hz 到 20Hz 带宽就够了。电压环输出要加限幅防止电流给定超过额定值。5. 进阶技巧用抗饱和补偿和参数自整定把 PFC 动态响应再提一档前面讲的都是基础实现如果想让 PFC 在负载突变和输入电压波动时表现更好还有两个进阶方向可以试。第一个是抗饱和补偿的改进。基础做法是输出限幅时撤销本次积分但更平滑的做法是采用反计算抗饱和把限幅前后的差值乘一个补偿系数反馈到积分项上。代码上就是在限幅后加一行float anti_windup_gain 0.5f; float sat_err out_new - out_unlimited; h-integral anti_windup_gain * sat_err * h-Ts;这样积分项不会突变过渡更平滑。anti_windup_gain 一般取 0.1 到 1.0越大回退越快但太大可能引起振荡。第二个是参数自整定。PFC 的负载范围很宽轻载和满载的最优参数不一样。可以在固件里存两组参数根据输出功率切换。功率检测可以用输出电压乘输出电流的平均值每 100ms 算一次做平滑滤波后查表选参数。切换时用斜坡过渡避免参数突变引起扰动。验证方法很简单用电子负载做 10% 到 90% 的阶跃看输出电压的恢复时间和过冲。好的 PFC 应该在 20ms 内恢复到 ±2% 以内过冲不超过 5%。如果达不到先检查电流环跟踪是否到位再调电压环参数。我自己踩过最大的坑是忽略采样时刻。电感电流在开关周期内是三角波采样点如果落在开关管开通瞬间采到的是尖峰落在关断瞬间采到的是谷值。正确做法是在三角波的中点采样也就是 PWM 计数器过零或者峰值时触发 ADC。这个细节调好后电流波形 THD 能直接降一半。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?