尧图精选

PFC的PID控制C语言离散化实现:电压外环与电流内环实战指南

🕒 发布时间:2026/9/3 19:48:46 📁 来源:尧图网络
简介这是一份面向电力电子与嵌入式开发者的 PFC 仿真及 C 语言实现资料包围绕功率因数校正中的 PID 控制展开。资料先给出 Simulink 下的 APFC 仿真模型演示如何通过 PID 控制器将功率因数调整到接近 1再将控制器离散化并用 C 语言代码复现同等控制效果帮助读者贯通从仿真到代码实现的链路。资源包为 rar 压缩格式仅 35KB共 5 个文件。其中 .slx 为 Simulink 模型两个 .c 文件分别承担主控逻辑与包装接口.tlc 用于代码生成配置.txt 为参考说明结构清晰方便按需查看。目前已有 2348 人学习下载。读者可对照模型理解 PID 参数整定与功率因数校正流程也可借助 C 代码掌握离散化落地方法对电力电子课程设计或电源控制入门有实际参考价值。 做了不少电力电子控制项目PFC基本是绕不开的一环。最近在整理旧资料时翻到一个之前写的“PFC的PID控制_C语言离散化实现”的工程包索性把里面的设计思路和代码细节重新梳理一遍。这个包里的内容并不复杂核心就是三相/单相PFC电路里最常用的电压外环、电流内环双闭环控制以及PID控制器在C语言里的离散化落地。对于刚接触数字电源或者想在MCU上实现PFC控制的朋友来说这套代码和思路可以直接“抄作业”比从零啃理论要快得多。需要先说明的是这篇博文主要围绕“用C语言在MCU上实现PFC的PID控制”展开涉及的理论推导不会太深但关键公式、参数选择逻辑和代码实现都会给到。硬件平台以常见的Boost型PFC为例控制芯片可以是STM32、DSP或者任何带ADC和PWM外设的MCU代码逻辑是通用的。1. PFC的PID控制为什么非用双闭环不可1.1 单电压环为什么不够很多初学者第一反应是PFC不就是把输出电压稳住吗那直接采输出电压、跑一个电压PID、输出占空比不就行了实测下来这个方案根本跑不起来原因在于Boost型PFC有个天然矛盾既要稳住输出电压又要让输入电流跟随输入电压的波形变化从而把功率因数拉高。如果只有一个电压环控制器唯一能调节的是占空比。而Boost电路的占空比同时决定了输出电压和输入电流。当你为了稳住电压去增减占空比时输入电流随之变化但这种变化和输入电压的瞬时值没有协调关系电流波形就会畸变功率因数依然很低。更麻烦的是输入电压是正弦波每半个周期内瞬时值从0到峰值再回到0单电压环根本没法跟踪这种快速变化所以必须再加一个内环去“强制”输入电流跟随正弦基准。1.2 电压外环电流内环的职责划分双闭环的结构很清晰电压外环的输出作为电流内环的基准电流内环的输出直接决定PWM占空比。电压外环采样输出电压和给定电压比较经过PID计算后输出一个值。这个值代表“当前负载下输入电流应该有多大”。负载加重输出电压跌落电压环输出增大电流环基准相应升高输入电流增大从而补充能量。电流内环采样电感电流或者开关管电流和电压环给出的电流基准比较经过PID计算后输出占空比直接控制开关管通断。这里有个关键细节电流环的基准不是纯直流而是“电压环输出 × 输入电压瞬时值的归一化波形”。换句话说电流基准的形状必须和输入电压一致这样才能让输入电流也呈正弦。实际工程里通常用输入电压前馈来做这件事也就是先采样输入电压归一化后乘上电压环输出得到正弦电流基准。这套方案在业内叫“平均电流模式控制”是最主流、最稳定的PFC控制方式。1.3 平均电流模式控制最实用的PFC实现方案平均电流模式的原理可以这样理解在一个开关周期内让电感电流的平均值等于给定基准。它对比峰值电流模式有两个明显优势一是抗噪声能力强因为采样的是平均电流而非开关尖峰二是占空比可以超过50%不会出现次谐波振荡问题尤其适合宽电压输入的PFC场景。具体到实现上电流内环的PID参数通常需要比较高的比例增益和积分增益因为要保证电流能快速跟随正弦基准变化在过零附近尤其关键。还有一个实际问题电流基准是正弦信号频率是工频的2倍100Hz或120Hz但电流环的响应速度要远快于这个频率一般电流环带宽做到几kHz这样才能让实际电流紧紧“咬住”基准不产生明显相位滞后。2. 离散化之前的功课连续域设计到底要定哪些数2.1 从“模拟控制器”到“每周期算一次”以前模拟PFC控制器用运放搭PIDRC参数决定增益和积分时间常数。现在做数字控制PID变成了一段C语言代码每隔一个开关周期执行一次。这个过程中最核心的问题就是离散化把连续域的传递函数变成差分方程让MCU用加减乘除算出来。离散化的标准做法是双线性变换Tustin变换把拉普拉斯域映射到z域。不过在实际工程里如果控制频率足够高比如开关频率50kHz控制频率25kHz以上直接用后向欧拉法化简也是够用的误差在可接受范围内。关键是每一步的物理意义要清楚比例项是当前误差积分项是误差的累加微分项是误差的变化速率。2.2 截止频率与采样频率这两组数决定了控制的成败很多人拿到PID就开始调参调了半天系统振荡。其实参数不是拍脑袋定的先算清楚以下三个值再微调就容易多了。电压环截止频率一般取10~20Hz。理由是输出电压有100Hz工频纹波如果电压环带宽太高会把100Hz纹波放大导致电流基准畸变输入电流谐波增加。取10~20Hz电压环响应慢但正好能过滤掉工频纹波。电流环截止频率取开关频率的1/10到1/20。比如开关频率50kHz电流环带宽做到2.5k~5kHz。电流环要快才能跟住正弦基准。采样频率和PWM频率一致或者用PWM中点触发采样保证采到的是电流平均值。电压环设计口诀电压环要“慢而稳”电流环要“快而准”。这句话虽然朴素但直接决定了参数整定的方向。2.3 位置式PID还是增量式PIDPFC的电流环和电压环都适合用位置式PID而不是增量式原因在于积分项的作用方式不同。位置式PID的输出直接对应一个物理量电压环输出对应电流基准幅值电流环输出对应占空比。如果改成增量式输出的变化量还需要外部累加得到最终占空比这反而多了一层积分和嵌入式实现里常见的限幅逻辑配合起来更麻烦。位置式PID的隐患是积分饱和但在做了抗饱和处理之后它依然是PFC控制里最直观、最好调的选择。具体的抗饱和写法在第三节代码里会给到。3. 用C语言把PID写进MCU核心代码拆解3.1 整体软件框架ADC触发、PWM更新、控制环执行先捋一下整个控制流程在一个PWM周期内是怎么串联起来的。以单相Boost PFC为例开关频率设50kHz控制频率同频每个周期执行一次完整控制PWM定时器到达中点时触发ADC采样采输入电压、输出电压、电感电流三路信号。ADC转换完成后触发中断在中断服务函数里跑电压环和电流环。电流环输出经过限幅后更新PWM比较寄存器。PWM在下一个周期按新占空比输出。这个流程里最关键的地方在于ADC采样必须用PWM中点触发而不是随便在主循环里定时采样。因为电感电流在开关管导通和关断时有纹波只有在中点采样才能采到平均电流。否则电流环拿到的信号带大量纹波噪声PID输出会被这噪声带着跑。3.2 电压环的离散化实现先定义PID结构体这是最基础的版本后面加抗饱和逻辑typedef struct { float Kp; float Ki; float Kd; float integral; // 积分累加值 float prev_error; // 上一次误差用于微分项 float output_max; // 输出上限 float output_min; // 输出下限 } PID_TypeDef;接下来是PID计算函数采用位置式PIDfloat PID_Calculate(PID_TypeDef *pid, float ref, float feedback) { float error ref - feedback; float output; // 比例项 float P_out pid-Kp * error; // 积分项 pid-integral error; float I_out pid-Ki * pid-integral; // 微分项使用一阶低通滤波防止高频噪声放大 float D_out pid-Kd * (error - pid-prev_error); output P_out I_out D_out; pid-prev_error error; // 输出限幅 if (output pid-output_max) output pid-output_max; if (output pid-output_min) output pid-output_min; return output; }在电压环里调用这个函数时ref是目标电压比如390Vfeedback是ADC采样到的实际输出电压。输出电压是分压电阻ADC采到的数字量所以注意ref也要换算到同一个量纲。电压环输出是电流基准的幅值注意这个值不能为负——PFC是单向的电流方向不会反转所以输出下限设为0。电压环执行频率其实可以降频。很多成熟的方案里电压环每10个工频周期跑一次就够了比如50kHz的控制频率下电压环每250次执行一次相当于200Hz电流环每次都执行。这样可以减少不必要的计算量同时也天然降低了电压环带宽。3.3 电流环的离散化实现电流环每次PWM中断都要跑所以代码要精简。核心逻辑是先根据输入电压波形生成正弦基准再和采到的电感电流比较跑PID得到占空比。正弦基准的生成有两种做法一是实时采样输入电压的绝对值归一化二是用锁相环得到相位查正弦表。工程上第一种就够用且简单很多。// 电流环控制period是PWM周期计数值 uint32_t CurrentLoop(PID_TypeDef *pid, float input_voltage, float inductor_current, float current_amplitude) { // 计算正弦基准输入电压瞬时值归一化 float input_voltage_norm; float current_ref; float duty; // input_voltage_max是输入电压峰值用软件记录或固定参数 input_voltage_norm input_voltage / INPUT_VOLTAGE_PEAK; // 电流基准 电压环输出 × 电压归一化值 current_ref current_amplitude * input_voltage_norm; // 调用PID计算占空比 duty PID_Calculate(pid, current_ref, inductor_current); // 占空比限幅不能超过95%留死区时间给MOSFET if (duty 0.95f) duty 0.95f; if (duty 0.0f) duty 0.0f; // 换算为PWM比较寄存器值 return (uint32_t)(duty * PWM_PERIOD); }电流环PID的Kp和Ki和电压环完全不同。以实测过的一个平台为例电感量1mH、开关频率50kHz、输出电压390V电流环Kp大约需要0.05~0.2、Ki大约0.001~0.01这个量级具体取决于电流采样系数和PWM分辨率的换算关系。关键是要理解电流环的响应速度要快到能跟上正弦基准尤其过零附近斜率很大如果Kp太小电流会在过零处“塌陷”THD指标直接恶化。3.4 积分饱和处理必须加的防呆逻辑位置式PID最经典的坑就是积分饱和。在下桥臂输出电压、电感电流失控等场景中如果输出电压一直低于给定值电压环的积分项会一直累加输出达到上限。等输出电压恢复正常时积分项早已“刹不住车”输出电压会出现很大超调严重时触发过压保护甚至炸管。常用的抗饱和方法有钳位法、退积分法和条件积分法。工程上我用得最多的是退积分法实现简单且效果稳定当PID输出超过限幅值时积分不累加或者直接把积分项拉回限幅对应的值。float PID_Calculate_AntiWindup(PID_TypeDef *pid, float ref, float feedback) { float error ref - feedback; float output; float P_out pid-Kp * error; // 先计算不考虑积分时的输出 output P_out pid-integral pid-Kd * (error - pid-prev_error); // 输出限幅 if (output pid-output_max) { output pid-output_max; // 退积分把积分拉回当前限幅下的合理值 pid-integral output - P_out - pid-Kd * (error - pid-prev_error); } else if (output pid-output_min) { output pid-output_min; pid-integral output - P_out - pid-Kd * (error - pid-prev_error); } else { // 正常范围才累加积分 pid-integral error; } pid-prev_error error; return output; }这个写法里的积分项用的是“积分值”而非“积分累加和”和前面的代码要保持一致。每次限幅时直接反推积分值本质是让积分项“配合”比例项保证输出始终在限幅边界内。实测下来用这个方案后启动过冲能从原来的30~40V降到10V以内。3.5 软启动与状态机别让上电瞬间冲烂管子还有一个很容易被忽视的坑是启动瞬间。PFC输出电容在刚开始是0V如果直接跑电压环电压误差很大电流环基准会瞬间拉满相当于直接短路充电冲击电流非常恐怖很容易炸整流桥和MOSFET。所以启动流程必须分成几个状态用状态机管理状态一预充电。通过继电器或限流电阻给母线电容充电让输出电压先升到一个安全值比如市电峰值的90%。这个过程不跑PFC控制只是不控整流充电。状态二开环软启动。PWM以非常小的固定占空比开始运行让电压缓慢上升。比如每10ms占空比增加0.5%直到输出电压接近目标值。状态三切入闭环。当输出电压达到目标电压的90%以上时电压环从当前积分值开始运行电流环同步切入。状态机的实现不复杂但是处理不好会带来各种玄学问题。比如继电器切换瞬间触点弹跳导致电压跌落、软启动速度太快导致电流尖峰、切换闭环瞬间积分值没对齐导致输出跳动。我的建议是每个状态切换时把PID结构体清零重置避免旧状态残留的积分值污染新状态。4. 调试实录实测遇到的五个典型坑4.1 启动过冲电压冲到500V这是第一次跑PFC时最容易遇到的问题。现象是启动后输出电压直接冲到500V以上然后触发过压保护。排查下来原因基本是两个一是软启动没做好二是积分饱和没处理。先在代码里确认软启动占了几个周期的占空比爬升再确认抗饱和逻辑是否生效了。如果两个都做了还过冲检查电压环的Kp是不是太大了。4.2 输入电流畸变过零附近有平台输入电流波形在过零附近出现一段平底看起来像被削平了。这个问题的根源通常不在PID而在电流基准的生成。比较常见的原因是输入电压采样在过零附近信噪比太低导致归一化后的基准波形失真。解决思路是加一阶低通滤波器或者采用“电压前馈相位锁定”的方式生成基准。4.3 参数整定的三个口诀调试PID参数时按下面这个顺序来能少走很多弯路先把电流环调稳断开电压环给一个固定电流基准只跑电流环观察电感电流是否和基准一致。电流环稳了PFC就有了基础。再调电压环换回双闭环从小到大增加电压环Kp观察输出电压动态响应和电流波形畸变程度。最后加减载测试空载、半载、满载分别测试特别关注满载切空载时的电压超调和空载切满载时的电压跌落。4.4 常见问题速查表现象可能原因检查方向输出电压低频振荡电压环带宽太高或Kp过大减小Kp降低电压环执行频率电流过零畸变电流环增益不足或采样噪声大增大电流环Kp检查ADC采样时刻启动冲击电流大软启动不完善或积分饱和检查状态机切换逻辑和抗饱和代码满载电压跌落过大电压环积分作用不够增大Ki适当提高电压环带宽开关管异常发热占空比突变导致硬开关检查限幅逻辑确认占空比变化是否平滑声音异常吱吱声电流环振荡或采样噪声耦合用示波器测电流波形检查ADC滤波调试PFC最痛苦的问题往往是“看起来像软件问题其实硬件也有锅”。比如电流采样电阻的layout没做好高频噪声直接灌进ADC引脚再好的PID也压不住。所以调试顺序永远是先用示波器确认硬件波形正常再调软件参数。最后分享一个经验PFC的PID控制代码本身真的不复杂难的是对“采样时刻”“控制时序”“基准生成”这事的理解。把这三个东西想明白代码写起来其实很快。我那个工程包里还附带了三相六开关PFC的软件框架不过那是后话了先把单相的跑通、跑稳再往三相扩展会轻松很多。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →