一、对比PID和ADRC1. 一句话总结自抗扰控制 —— “不管来什么妖风我都能稳住”的司机想象一下你开车目标是让车在车道中央匀速直线行驶。传统PID司机他只会死盯着“当前偏离中心多少”误差然后猛打方向盘修正。如果突然吹来一阵侧风或者路面有个坑车会先被吹歪他才反应过来去纠正车子就会左摇右晃。自抗扰控制司机他是个“老司机”脑子里有三样法宝跟踪微分器他不仅看现在车偏没偏还预判车子下一秒会怎么偏。这让他动作更平滑不会急打方向。扩张状态观测器这是最核心的“黑科技”。车子跑起来所有乱七八糟的干扰——比如侧风、轮胎打滑、路面不平——在他眼里都不是秘密。他能实时“感觉”到这些干扰力加起来到底有多大并且认为这是车子的一个“额外状态”。非线性状态误差反馈知道了干扰多大他就不只是傻傻地纠偏了。他的策略是“哦原来有股相当于向左5度的妖风在吹我。那我打方向盘时除了把车回正还额外多加一个向右5度的力来抵消它。”相当于把未知干扰估算出来然后直接“干掉”它。一句话总结ADRC它不关心干扰具体是什么风坡它只关心干扰总共造成了多大影响然后实时估算并补偿掉这个影响让控制系统变得非常“抗造”、鲁棒性强。2. 四个基础模块控制水温为例被控对象电加热水箱输入 加热功率 u输出 水温 y存在加热滞后开加热不会立刻升温环境散热室温越低降温越快外部扰动加热丝老化系统内部参数变化ADRC 由 4 部分组成TD 跟踪微分器平滑目标水温给出平滑目标 微分避免设定温度突变导致功率剧烈跳动ESO 扩张状态观测器【ADRC 灵魂】 只用测量到的水温 y实时估算 3 个量z1跟踪实际水温z2水温变化速度升温 / 降温速率z3总扰动散热、滞后、加热老化全部打包成一个量NLSEF 非线性误差反馈控制律计算消除温差的基础控制量扰动补偿用观测出的总扰动 z3 直接抵消得到最终加热功率 u3. 和 PID 对比通俗理解PID看见水温低了慢慢加大功率干扰来了只能等误差变大再补救ADRC我实时算出现在所有乱七八糟干扰一共等效多大影响直接提前减掉几乎无静差、超调很小。二、完整数学公式二阶 ADRC水温二阶系统水温属于二阶惯性系统y实际水温y˙​温度变化速率y¨​温度二阶导数w外部散热扰动f(y,y˙​,w)系统总扰动b控制增益。模块 1TD 跟踪微分器离散形式采样周期 h目标温度 v0​输出平滑目标 v1​目标微分 v2​给你的目标值安排一个“平滑过渡”同时顺带算出它的变化速度微分避免一开始误差太大导致系统猛冲。fhan (x1, x2, r, h) 最速综合函数 作用无超调快速跟踪抑制微分冲击fhan最速综合函数平滑防超调r 跟踪速度h0​ 滤波因子// fhan 最速综合函数 对应数学公式 double fhan(double x1, double x2, double r, double h) { double d r * h * h; double a0 h * x2; double y x1 a0; double a1 sqrt(d * (d 8 * fabs(y))); double a2 a0 sign(y) * (a1 - d) / 2.0; double a; if (fabs(y) d) a a2; else a x1 (a2 - d) * sign(y); return -r * a / d; } // TD跟踪微分器输入目标温度v0更新v1 v2 void TD(double v0) { double fh fhan(v1 - v0, v2, r_TD, h0); v1 v1 h * v2; // 公式v1更新 v2 v2 h * fh; // 公式v2更新 }模块 2ESO 扩张状态观测器二阶目的不管外面刮风还是里面零件磨损把所有干扰打包成一个“总扰动”实时算出来。输入实际水温 y输出 z1 (温度观测)、z2 (温变速率)、z3 (总扰动)fal非线性反馈函数小误差放大、大误差饱和抑制震荡fal(e,α,δ) 分段非线性函数小误差区间高增益快速消静差大误差低增益不剧烈震荡β1​,β2​,β3​观测器增益参数// 非线性函数fal小误差时线性防抖大误差时非线性加快收敛 float ADRC::fal(float e, float alpha, float delta) { if (fabsf(e) delta) { return e / powf(delta, 1.0f - alpha); } else { return powf(fabsf(e), alpha) * sign(e); } } void ADRC::ESO(float feedback, float u) { float e z1_ - feedback; // 观测误差 float z1_dot z2_ b0_ * u - beta1_ * e; float z2_dot -beta2_ * fal(e, 0.5f, 0.01f); // 0.5是非线性因子 z1_ h_ * z1_dot; // 更新状态观测 z2_ h_ * z2_dot; // 更新总扰动观测z2就是算出来的干扰 }模块 3NLSEF 非线性误差反馈 模块 4 扰动补偿目的用TD给的平滑目标减去ESO给的状态得到误差经过非线性放大后算出基础控制量然后直接减去总扰动。把观测到的总扰动z3​直接减掉再除以增益 b得到最终加热功率 u u 限幅0 ≤ u ≤ 100代表 0~100% 加热功率u0​未补偿扰动的原始控制量C代码对应float ADRC::NLSEF() { float e1 v1_ - z1_; // 误差 平滑目标 - 实际观测到的状态 // 非线性组合比普通P控制反应更灵敏 float u0 beta3_ * fal(e1, 0.75f, 0.01f); // 扰动补偿 核心公式 u(u0-z3)/b0 float u (u0 - z2_) / b0_; return u; }把它们串起来主循环float ADRC::calculate(float target, float feedback) { TD(target); // 1. 给目标安排平滑过渡 ESO(feedback, u_prev_); // 2. 观测状态和总扰动 float u NLSEF(); // 3. 算控制量扣除扰动 u_prev_ u; // 保存本次输出下次ESO要用 return u; }总结成一张表三、C 完整实现ADRC 水温控制器功能说明离散 ADRC 二阶标准结构内置 fal、fhan 核心函数水温仿真对象二阶惯性 散热扰动模拟真实水箱闭环控制演示目标 45℃水温输出 0~100 加热功率每一步代码对应上面数学公式注释绑定公式#include iostream #include cmath #include thread #include chrono // 一阶ADRC控制器 class ADRC_WaterTemp { public: // 构造函数设定参数 ADRC_WaterTemp(float h, float r, float beta1, float beta2, float b0) : h_(h), r_(r), beta1_(beta1), beta2_(beta2), b0_(b0) { v1_ 25.0f; // 初始目标水温先等于室温 v2_ 0.0f; // TD微分 z1_ 25.0f; // 初始观测水温 z2_ 0.0f; // 初始总扰动 } // fhan 最速综合函数 float fhan(float x1, float x2, float r, float h) { float d r * h * h; float a0 h * x2; float y x1 a0; float a1 sqrtf(d * (d 8.0f * fabsf(y))); float a2 a0 sign(y) * (a1 - d) / 2.0f; float a (fabsf(y) d) ? a2 : x1 (a2 - d) * sign(y); return -r * a / d; } // 最速跟踪微分器 (TD) void TD(float target) { float fh fhan(v1_ - target, v2_, r_, h_); v1_ h_ * v2_; v2_ h_ * fh; } // 非线性函数 fal float fal(float e, float alpha, float delta) { if (fabsf(e) delta) { return e / powf(delta, 1.0f - alpha); } else { return powf(fabsf(e), alpha) * sign(e); } } // 扩张状态观测器 (ESO) void ESO(float feedback, float u) { float e z1_ - feedback; // 观测误差 观测水温 - 实际水温 // printf(feedback %f, z1 %f, e %f\n, feedback, z1_, e); // z1跟踪实际水温 float z1_dot z2_ b0_ * u - beta1_ * e; // z2估计总扰动散热、效率损失等 float z2_dot -beta2_ * fal(e, 0.5f, 0.01f); z1_ h_ * z1_dot; z2_ h_ * z2_dot; // printf(e %f, z1_dot %f, z2_dot %f, z1 %f, z2 %f\n, e, z1_dot, z2_dot, z1_, z2_); } // 非线性状态误差反馈 (NLSEF) float NLSEF(float target) { float e1 target - z1_; // 误差 目标水温 - 观测水温 // 用非线性函数处理误差比普通P控制更智能 float u0 300.0f * fal(e1, 0.75f, 0.01f); // 300是比例增益 // 核心扣除总扰动 z2 float u (u0 - z2_) / b0_; // 限制输出在 0~100% 之间 u std::max(0.0f, std::min(u, 1.0f)); return u; } // 主计算接口 float calculate(float target, float feedback) { TD(target); // 1. 让目标平滑变化 ESO(feedback, u_prev_); // 2. 观测水温和干扰 float u NLSEF(v1_); // 3. 计算加热功率 u_prev_ u; return u; } private: float sign(float x) { return x 0 ? 1.0f : (x 0 ? -1.0f : 0.0f); } float h_; // 控制周期 (秒) float r_; // TD跟踪速度 float beta1_; // ESO增益1 float beta2_; // ESO增益2 float b0_; // 补偿系数 float v1_; // TD给出的平滑目标 float v2_; // TD微分输出 float z1_; // ESO观测的水温 float z2_; // ESO观测的总扰动 float u_prev_; // 上一次的控制量 }; // 模拟被控对象一桶水 float simulate_water_tank(float current_temp, float heating_power, float dt) { // 加热功率转化为升温速率 (假设1.0功率每秒升1度实际会受扰动影响) float heat_gain heating_power * 6.5f * dt; // 自然散热 (温差越大散热越快假设散热系数0.1) float heat_loss 0.12f * (current_temp - 25.0f) * dt; return current_temp heat_gain - heat_loss; } // 主函数 int main() { // 创建ADRC控制器参数含义 // h0.1秒, r0.5(跟踪速度), beta1100, beta2500, b02.0 ADRC_WaterTemp adrc(0.1f, 1.5f, 15.0f, 80.0f, 2.0f); float real_temp 25.0f; // 真实水温初始25℃ float target_temp 60.0f; // 目标水温 60℃ std::cout 时间(s)\t目标(℃)\t实际(℃)\t功率(%)\t扰动估计\n; std::cout ----------------------------------------------------\n; for (int i 0; i 400; i) { // 模拟20秒 (200步 * 0.1秒) float time i * 0.1f; // ADRC计算加热功率 float power adrc.calculate(target_temp, real_temp); // 模拟水温变化 real_temp simulate_water_tank(real_temp, power, 0.1f); // 打印数据 (每2秒打印一次) if (i % 20 0) { std::cout time \t target_temp \t\t real_temp \t\t power * 100 %\t\t (内部观测)\n; } // 模拟延时 std::this_thread::sleep_for(std::chrono::milliseconds(10)); } return 0; }