在工业控制领域,PID控制器已经统治了近百年,但面对现代控制系统中越来越复杂的扰动因素,传统PID开始显得力不从心。去年我在为某型工业无人机开发飞控系统时,就深刻体会到了这一点——当遭遇突风扰动时,尽管已经精心调校了PID参数,飞行器姿态仍然会出现明显波动。正是这个痛点促使我开始研究自抗扰控制技术(ADRC),并最终封装出这个二阶线性自抗扰控制器L_ADRC2。
与PID相比,L_ADRC2最大的优势在于其内置的状态观测器。想象一下,PID就像是一位蒙着眼睛的调酒师,只能通过品尝最终成品来调整配方;而L_ADRC2则像配备了智能传感器的调酒机器人,不仅能尝味道,还能实时监测每种原料的流速、温度等状态。这种对系统"内状态"的观测能力,使得L_ADRC2在面对系统扰动时,反应速度比PID快30-40%,这点在我们后续的无人机抗风实验中得到了验证。
L_ADRC2的核心在于其状态观测器,这部分的代码实现虽然只有寥寥几行,却蕴含着精妙的设计思想:
python复制e = y - self.z1
self.z1 += (self.z2 + self.beta1 * e) * self.dt
self.z2 += (self.beta2 * e) * self.dt
这三行代码实际上实现了一个二阶扩张状态观测器(ESO)。其中z1跟踪系统输出y,z2则估计系统的总扰动(包括模型不确定性和外部干扰)。beta1和beta2这两个参数就像观测器的"调焦旋钮":beta1决定观测器对误差的敏感程度,beta2则影响扰动估计的收敛速度。
实际调试中发现,将beta1设为系统带宽的3-5倍,beta2取beta1平方的0.8-1.2倍时,观测器性能最优。例如对于带宽10Hz的系统,我通常会从w0=30(beta1=60,beta2=900)开始试调。
控制器的输出计算部分:
python复制u0 = (target - self.z2) / self.b0
self.u = u0 - self.z1 / self.b0
这实际上是做了两件事:首先用z2补偿系统总扰动,然后用z1实现状态反馈。b0这个参数特别关键,它相当于控制器的"力度调节器"。在四旋翼控制项目中,我发现当b0取值接近系统真实增益的倒数时,控制效果最好。例如某型电机的电压-转速增益实测为1.2(rpm/V),那么b0取0.8左右就能获得很好的初始性能。
手动调参w0和b0既耗时又依赖经验,为此我集成了粒子群优化(PSO)算法:
python复制from pyswarm import pso
def tune_parameters(system_model):
def cost_func(params):
w0, b0 = params
controller = LADRC2(w0, b0, dt=0.01)
ise = simulate(system_model, controller)
return ise
lb = [1, 0.1] # w0下限1,b0下限0.1
ub = [100, 10] # w0上限100,b0上限10
xopt, _ = pso(cost_func, lb, ub, swarmsize=20, maxiter=50)
return xopt
这个实现有几个技术要点:
经过多个项目的实践,我总结出几个提升调参效率的方法:
对数变换搜索:对b0参数进行对数变换后再搜索,可以更高效地覆盖大范围值域
python复制# 在代价函数内添加
b0 = 10**params[1] # 假设params[1]是log10(b0)
多阶段优化:先大范围粗调,再小范围精调
python复制# 第一阶段:大范围搜索
x1 = pso(cost_func, [1,0.1], [100,10], swarmsize=30, maxiter=30)
# 第二阶段:小范围优化
xopt = pso(cost_func, x1*0.8, x1*1.2, swarmsize=20, maxiter=50)
并行评估:使用multiprocessing加速仿真评估
python复制from multiprocessing import Pool
def parallel_eval(params_list):
with Pool(4) as p:
return p.map(cost_func, params_list)
根据不同类型的被控对象,我整理了一些初始参数经验值:
| 被控对象类型 | w0范围 | b0范围 | 建议采样周期 |
|---|---|---|---|
| 无人机姿态 | 30-50 rad/s | 0.5-2.0 | 5-10ms |
| 直流电机转速 | 10-30 rad/s | 0.1-1.0 | 1-5ms |
| 温度控制系统 | 1-5 rad/s | 0.01-0.1 | 0.5-2s |
观测器发散:
控制输出震荡:
响应迟缓:
在最近的四旋翼项目中,L_ADRC2表现尤为出色。对比传统PID,在相同风扰条件下:
| 指标 | PID控制 | L_ADRC2 | 提升幅度 |
|---|---|---|---|
| 滚转角稳态误差 | ±3.2° | ±1.8° | 43% |
| 恢复时间(1m/s突风) | 0.8s | 0.5s | 37% |
| 超调量(阶跃响应) | 12% | 5% | 58% |
实现时特别注意了几个细节:
python复制# 无人机滚转轴控制器示例
roll_controller = LADRC2(w0=40, b0=1.2, dt=0.01)
def attitude_loop():
while True:
# 获取传感器数据
roll_angle, roll_rate = get_imu_data()
# 更新控制器
u = roll_controller.update(roll_angle, target_roll)
# 分配电机指令
set_motor_output(u)
time.sleep(0.01)
这个案例中最深刻的体会是:当遇到电机响应非线性时,仅仅调整控制器参数是不够的。我们最终通过以下组合方案解决问题:
在长期运行中发现,固定参数的L_ADRC2在面对工况变化时仍有局限。为此我试验了几种在线调参方法:
增益调度:根据工作点调整参数
python复制def schedule_params(operating_point):
if op < 0.5:
return 30, 0.8 # 低增益模式
else:
return 50, 1.2 # 高增益模式
模型参考自适应:使用参考模型生成误差信号来调整w0
强化学习调参:正在试验的DDPG算法,初步结果显示在变负载场景下比固定参数提升约15%性能
对于资源受限的嵌入式平台,我做了这些优化:
c复制// STM32上的定点数实现示例
int32_t z1 = 0, z2 = 0;
int32_t beta1_dt = 32768*2*30*0.01; // Q15格式
int32_t beta2_dt = 32768*30*30*0.01;
void ADRC_Update(int16_t y, int16_t target) {
int32_t e = (y << 16) - z1; // Q16
z1 += ((z2 >> 16) + ((beta1_dt * e) >> 15)) >> 1;
z2 += (beta2_dt * e) >> 15;
int32_t u0 = ((target << 16) - z2) / b0;
return (u0 - (z1 / b0)) >> 16;
}
在移植到STM32F4平台时,这些优化使计算耗时从56μs降至18μs,完全满足1kHz的控制频率要求。