前言
凌晨 4 点,你双眼通红地盯着小车的轮子。
“换了个稍微重一点的底盘,为什么原先完美的 PID 参数全废了?”
“为什么电机在这段速度下很稳,到了高速就开始疯狂震荡?”
大多数人把 PID 调参当成一门“玄学”,全靠运气和直觉。但你知道吗?在真实的工业伺服驱动器和高级温控仪中,根本没有工程师会去手动试凑参数!
它们都带有一个神级功能:Auto-Tuning(自整定)。只要按下一个按钮,机器会自己发出特定的指令去试探电机,然后在一瞬间利用数学公式,自动算出绝对完美的。Kp,Ki,KdKp,Ki,Kd今天,我们将撕开工业控制器的底层护城河,手把手教你在 STM32 中用 C 语言复刻大名鼎鼎的**“继电器反馈自整定法(Relay Feedback Auto-Tuning)”**。让你的单片机长出大脑,自己调参!
@TOC
一、 认知颠覆:为什么你手动调参总是失败?
教科书上教过一种极其经典的理论调参法:Ziegler-Nichols(齐格勒-尼科尔斯)临界比例法。
它的步骤是:
把 Ki 和 Kd 设为 0。
慢慢把 Kp 加大,直到系统出现等幅振荡(不发散也不衰减)。
记下此时的 Kp(称为临界比例度
),以及振荡一个周期的波峰到波峰的时间(称为临界周期KuKu
)。TuTu查表,直接用公式套出
。Kp,Ki,KdKp,Ki,Kd
既然公式这么完美,为什么大家不用?
因为太危险了!手动去寻找“临界振荡点”,一不小心 Kp 给大了一点点,系统瞬间变成发散振荡,你的无人机会像火箭一样冲向天花板,你的机械臂会直接把齿轮扫碎!
有没有一种方法,既能测出
KuKu和
TuTu,又能在绝对安全的极小幅度下进行振荡?
这就是我们今天要祭出的终极魔法:继电器反馈法。
二、 工业界神技:继电器反馈法(Relay Feedback)
瑞典控制学泰斗 Åström 发明了一种极其优雅的自整定技术:用一个“继电器(Bang-Bang 控制)”强行替换掉 PID 控制器!
1. 魔法是怎么发生的?(大白话原理解析)
我们在自整定模式下,不跑 PID,而是跑一个极其暴力的简单逻辑:
如果当前速度小于目标值,单片机瞬间输出一个固定的正向最大安全力量
(比如给 PWM 赋值 +30%)。+d+d一旦当前速度大于目标值,单片机瞬间输出一个固定的反向最大安全力量
(比如给 PWM 赋值 -30%)。−d−d
奇迹出现了!
由于物理世界存在惯性和延迟,系统会在目标值上下形成一个极其完美的、幅度受限的低频正弦波振荡(极限环振荡)!
因为输出的力量
dd是你自己设定的安全值(比如 30% PWM),所以这个振荡绝对安全,绝不会失控发散!
2. 破译密码:从振荡中提取系统灵魂
在这个人为制造的振荡波形中,我们让单片机去自动测量两个值:
(振荡幅度):传感器读数的波峰和波谷之差的一半。aa
(振荡周期):两次穿过目标值的时间差。TuTu
拿到
aa和
dd后,数学家经过傅里叶级数推导,得出了系统的真实临界比例度
KuKu:
Ku=4dπaKu=πa4d有了
KuKu和
TuTu,我们直接掏出 Ziegler-Nichols 终极公式表,瞬间算出最完美的 PID!
Kp=0.6×KuKp=0.6×KuKi=1.2×KuTuKi=1.2×TuKuKd=0.075×Ku×TuKd=0.075×Ku×Tu
三、 C 语言硬核部署:如何手写 Auto-Tuning 状态机?
不要被理论吓到,在单片机里,这仅仅是一个在定时器中断里运行的简单状态机!
🚨 史诗级避坑:必须加入“迟滞(Hysteresis)”
传感器的读数(如编码器速度、温度)是有底噪的。如果在目标值附近不用迟滞,一点点噪声就会导致单片机在一微秒内疯狂切换
+d+d和
−d−d,整个自整定直接崩溃。
必须设置一个容忍区间:超过 目标+噪声 才反转,低于 目标-噪声 才正转!
🏆 工业级 Auto-Tuning C源码模板(直接抄作业)
codeC
#include "math.h" // --- 自整定结构体 --- typedef struct { uint8_t is_tuning; // 是否正在自整定标志位 float target; // 整定的目标值 (比如 1000 RPM) float output_step; // 继电器输出步长 d (比如 PWM = 200) float noise_band; // 迟滞死区 (抗噪声,如 10 RPM) // 波峰波谷记录 float max_val; float min_val; // 周期测算记录 uint32_t cross_time_1; uint32_t cross_time_2; uint8_t cross_count; uint32_t current_time_ms; // 最终算出的 PID float out_Kp; float out_Ki; float out_Kd; } AutoTuner_t; AutoTuner_t Tuner; /** * @brief 启动自整定 */ void AutoTuning_Start(float target_val, float step) { Tuner.is_tuning = 1; Tuner.target = target_val; Tuner.output_step = step; Tuner.noise_band = 5.0f; // 根据传感器底噪设定 Tuner.max_val = -99999.0f; Tuner.min_val = 99999.0f; Tuner.cross_count = 0; } /** * @brief 自整定核心状态机 (放在控制周期的定时器中断中,如 5ms) * @param measure: 当前传感器实测值 * @retval 继电器控制输出 */ float AutoTuning_Update(float measure) { static float relay_output = 0; Tuner.current_time_ms += 5; // 假设运行周期为 5ms // 1. 记录波峰和波谷 if (measure > Tuner.max_val) Tuner.max_val = measure; if (measure < Tuner.min_val) Tuner.min_val = measure; // 2. 带迟滞的继电器控制 (Bang-Bang) if (measure > Tuner.target + Tuner.noise_band) { relay_output = -Tuner.output_step; // 超过上限,全力刹车 } else if (measure < Tuner.target - Tuner.noise_band) { relay_output = Tuner.output_step; // 低于下限,全力加速 } // 3. 核心:捕捉过零点,计算周期 Tu! static float last_measure = 0; if ((last_measure < Tuner.target) && (measure >= Tuner.target)) { // 发生一次由下往上的穿越! Tuner.cross_count++; if (Tuner.cross_count == 1) { Tuner.cross_time_1 = Tuner.current_time_ms; // 记录第一次穿越时间 } else if (Tuner.cross_count == 3) { // 经过了两个完整半波,即一个完整周期 Tuner.cross_time_2 = Tuner.current_time_ms; // ========================================== // !!!整定完成,开始核爆级计算!!! // ========================================== // 1. 算周期 Tu (单位转化为秒) float Tu = (Tuner.cross_time_2 - Tuner.cross_time_1) / 1000.0f; // 2. 算振幅 a float a = (Tuner.max_val - Tuner.min_val) / 2.0f; // 3. 算临界比例度 Ku (4d / pi*a) float Ku = (4.0f * Tuner.output_step) / (3.1415926f * a); // 4. Ziegler-Nichols 公式算出终极 PID! Tuner.out_Kp = 0.6f * Ku; Tuner.out_Ki = 1.2f * Ku / Tu; Tuner.out_Kd = 0.075f * Ku * Tu; Tuner.is_tuning = 0; // 关闭自整定 printf("Auto-Tuning Success! Kp=%.3f, Ki=%.3f, Kd=%.3f\r\n", Tuner.out_Kp, Tuner.out_Ki, Tuner.out_Kd); } } last_measure = measure; return relay_output; }四、 赛场融合与实战流程(装 X 必备)
怎么在电赛测评现场,用这个黑科技震撼评委?
一键自适应展示:在 OLED 屏幕上做一个 [Auto-Tune] 按钮。评委来时,你在小车上临时加一块大铁块(改变了物理模型,原 PID 必废)。
触发魔法:按下按钮,单片机切断普通 PID,进入 AutoTuning_Update()。小车车轮开始极小幅度地来回抽搐(振荡)。
参数生成:5 秒钟后,屏幕“滴”地一声,跳出三行计算好的 Kp, Ki, Kd,并自动将其覆盖到系统真实的 PID 控制器中。
完美运行:你再次启动小车,小车稳如泰山,完美贴合轨迹。
报告怎么写?
“针对传统 PID 无法适应负载突变的问题,本系统引入了基于继电器反馈法的在线自整定(Auto-Tuning)算法。系统通过强迫执行器产生受限极限环振荡,利用傅里叶级数解析系统的临界增益与临界周期KuKu,自主完成 Ziegler-Nichols 参数寻优,实现了对未知负载的自适应闭环控制。”TuTu
评委看完这段,直接默认你是来炸鱼的研究生,分数直接拉满!
五、 适用场景与绝对禁区
Auto-Tuning 虽强,但绝不是万能的,千万注意以下禁区:
完美适用:温控系统(烤箱、恒温烙铁)、电机速度环闭环、恒压恒流电源。这些系统允许短暂的振荡,且物理响应相对平缓。
绝对禁忌:倒立摆、无人机姿态环(Pitch/Roll)!这些是不稳定系统。你如果用继电器去让无人机振荡,它的极点在右半平面,一振荡就会翻车炸机!对于这种姿态系统,必须搭建台架限制物理角度后才能自整定,或者老老实实手调 / 用 LQR!
结语
在控制工程的浩瀚宇宙里,“试凑法(盲调)”是刀耕火种的石器时代,而“系统辨识与自整定”才是自动化大工业真正的引擎。
当你放下毫无根据的猜测,开始让单片机用继电器去触碰物理世界,用傅里叶级数去聆听机械的振荡,用严密的微积分公式去推导闭环的灵魂时……
你就不再是一个被 PID 支配的“调参猴子”,而是一位能够洞察系统底层频率响应的控制大师。
预祝各位挑战高阶控制的创客们:振荡圆润完美,计算一发入魂,参数百发百中,用极致的智能化降维绝杀整个电赛!