资讯详情

资讯详情

ADRC自抗扰汽车纵向速度控制MATLAB仿真与参数整定

简介这是一份基于自抗扰控制ADRC理论、已在 MATLAB 中完成仿真验证的控制程序面向自动化、车辆控制方向的学习者与工程实践者尤其适合希望摆脱精确建模依赖、提升系统抗扰能力的读者。压缩包共 1 个文件为 .m 脚本文件如扩展状态观测器 ESO 模块整体仅 563B体量精简便于直接阅读与移植。目前已有 1934 人学习下载说明该案例在相关方向具有一定参考价值。程序参考韩京清先生的自抗扰理论将扩展状态观测器对系统状态与未知扰动的实时估计、控制量的在线补偿串联起来并与汽车引擎控制场景结合可用于观察不同工况下动力输出平顺性与抗扰表现的仿真结果。读者可从中理解 ADRC 相较 PID 不依赖精确模型的优势掌握 ESO 的搭建方式、参数调节思路与仿真验证流程并以此为模板改写被控对象迁移到自己课题或工程项目中完成从理论到代码的落地。1. 满载爬坡就发飘为什么这套 adrc 自抗扰仿真值得拆开看做过汽车纵向速度控制的人多半在某个工况上吃过亏空载标定得挺漂亮副驾坐满、后备厢压上行李再遇上连续上坡车速就开始往下掉PID 怎么调都只能二选一——要么稳态留静差要么增益调大之后超调压不住。这套 adrc 自抗扰程序就是冲着这个场景写的在 MATLAB 环境下把仿真跑通了跟踪微分器、扩张状态观测器、非线性误差反馈三段齐全参数集中在一个脚本里改整车质量、坡度、执行器时间常数就能重跑一遍。它适合正在做汽车控制方向毕设或课设、需要一套能出曲线、能对比、能答辩的读者也适合手上有实车标定需求、想先在仿真里把控制器骨架验证一遍的从业者。2. 控制器骨架设计TD、ESO、NLSEF 三段式怎么拆ADRC 常被当成一个“大算法”整体看待其实拆开就三件东西每件都有清晰的输入输出边界边界划清楚了代码才好写、参数才好调。跟踪微分器负责“把指令变得能跟”扩张状态观测器负责“把说不清的东西估出来”非线性误差反馈负责“把误差和扰动合成一个控制量”。三者共享同一个时间基准串起来才是一个闭环。这里先把一条原则摆出来三个部件的采样周期必须一致中间不要出现“控制器 1 ms、观测器 10 ms”这种混搭。仿真是定步长推进的混搭的后果在第 5 章会详细说一句话概括就是观测器估出来的扰动永远滞后一拍控制量会跟着抖。下面的结构全部按单一步长h展开。2.1 跟踪微分器给阶跃指令安排一段跟得上的过渡直接把 0 到 20 m/s 的阶跃丢给控制器第一拍误差就有 20控制量瞬间顶到限幅执行器进入饱和区后面的积分和观测全被带偏。跟踪微分器的做法是先安排一条过渡曲线给一个加速度受限的斜坡信号v1去逼近v_ref同时把这条斜坡的导数v2一并算出来等于免费送了一路微分信号不用再去差分放大噪声。最速控制综合函数 fhan 是这套东西的核心它的作用是在“加速度不超过 r”的约束下让v1用最短时间追上目标function f fhan(x1, x2, r, h0) % 最速控制综合函数输入位置误差 x1、速度 x2输出加速度指令 d r * h0^2; % 线性区宽度 a0 h0 * x2; % 速度项补偿 y x1 a0; % 综合误差 a1 sqrt(d * (d 8 * abs(y))); % 二次方程正根 a2 a0 sign(y) * (a1 - d) / 2; % 分段判据 sy (sign(y d) - sign(y - d)) / 2; a (a0 y - a2) * sy a2; sa (sign(a d) - sign(a - d)) / 2; f -r * (a / d - sign(a)) * sa - r * sign(a); endr是跟踪速度因子单位是 m/s²物理含义就是允许的最大加速度取 610 对乘用车速度环比较合适h0是滤波因子一般取采样周期的 520 倍太小了微分信号带毛刺太大了过渡过程拖沓。d、a0、a1、a2这一串是韩京清原式的分段判别目的是让函数在“线性区”和“最速区”之间平滑切换避免符号函数在零点附近来回跳变产生抖振。调用时把位置误差传进去就行两行完成一步迭代[v1, v2] deal(v1 h * v2, v2 h * fhan(v_ref - v1, v2, c.r, c.h0));注意这里用的是欧拉前向积分h是控制器步长。步长超过 5 ms 之后fhan 输出的加速度指令在过渡段末尾会出现明显的一拍过冲曲线看着就是“追上目标之后又冲出去一点再回来”。这不是参数问题是离散化精度问题只能靠减小步长或改用二阶龙格库塔解决。2.2 扩张状态观测器把模型说不清的部分打包成一个状态车辆模型永远不可能精确质量会变、坡度在变、风阻系数是个经验值、执行器还有滞后。ESO 的思路很干脆——不去逐项分辨这些因素把它们统统归到“总扰动”f里然后把f当成系统的一个新状态扩张出来用输出y和控制量u反推它的实时值。二阶对象配三阶观测器线性形式LESO的离散实现是这样的function [z1, z2, z3] leso3(y, u, z1, z2, z3, wo, b0, h) % y: 实测车速 u: 上一步实际控制量 z1: 速度估计 z2: 加速度估计 z3: 总扰动估计 e z1 - y; % 输出估计误差 beta1 3 * wo; % 观测器增益由带宽 wo 直接配置 beta2 3 * wo^2; beta3 wo^3; z1 z1 h * (z2 - beta1 * e); z2 z2 h * (z3 - beta2 * e b0 * u); z3 z3 h * (-beta3 * e); end三个增益不是拍脑袋定的用beta1 3wo、beta2 3wo²、beta3 wo³把三重极点全部配到-wo等于把三个旋钮压缩成一个。这是工程上最省事的做法调试时只盯着wo一个量看就行wo越大观测越快、抗扰越猛但噪声放大也越厉害通常取期望闭环带宽的 35 倍车辆速度环取 1020 rad/s 是常见区间。b0是控制增益的估计值也就是“一个单位控制量能带来多少加速度”。这个参数宁可估小不要估大估小了控制器偏保守、响应慢一点但稳估大了直接发散。第 3 章会给出它的算法来源。u这里必须是实际加到对象上的控制量也就是经过限幅之后的值不是 NLSEF 算出来的原始值。这一条是新手最容易写错的地方写错了观测器会在饱和区持续偏离。2.3 非线性误差反馈与扰动补偿控制量最后怎么拼前两段拿到v1, v2指令及其导数和z1, z2, z3状态及扰动估计剩下的就是把误差合成控制量。分两步先算一个不含扰动补偿的“名义控制量”u0再用z3把它顶回去。function [u, u0] nlsef(v1, v2, z1, z2, z3, c) % 非线性误差反馈 扰动补偿 e1 v1 - z1; % 位置误差 e2 v2 - z2; % 速度误差 u0 c.kp * fal(e1, 0.75, c.delta) ... c.kd * fal(e2, 1.25, c.delta); % 非线性 PD u (u0 - z3) / c.b0; % 扰动前馈抵消 end function f fal(e, alpha, delta) % 小误差大增益、大误差小增益的非线性函数 if abs(e) delta f e / delta^(1 - alpha); % 线性区避免零点抖振 else f abs(e)^alpha * sign(e); % 非线性区 end endalpha取 01 之间小于 1 时小误差段增益被放大跟踪精度好delta是线性区半宽一般取 0.010.1根据量的量纲调速度环取 0.05 左右。如果嫌非线性参数多、调起来烦fal直接退化成线性 PD 也完全能用把alpha都填 1 就是标准线性 ADRC代码里留个开关就行——我做方案时一般两套都留着答辩时拿线性版讲原理非线性版讲性能对比曲线一摆效果更直观。u (u0 - z3)/b0这一行是整个 ADRC 的“点睛之笔”含义是如果观测器估出当前有个等效力矩在拖后腿那就提前把控制量加大到能抵消它的程度。这一步做完剩下的误差反馈只需要处理残差增益不用往死里调鲁棒性自然就上来了。2.4 离散化约定三个部件共用的时间基准三个部件全部用前向欧拉积分步长统一为h。这种写法在 1 ms 步长下精度足够好处是代码直观、和 Simulink 定步长求解器行为一致出了问题好对照。要提醒的是欧拉积分的稳定域有限wo * h超过 0.3 之后观测器开始振铃超过 0.5 直接发散。反推一下如果wo 20 rad/sh最大只能到 15 ms留余量取 15 ms 比较稳妥。还有一处容易混淆h0是 TD 内部用的滤波因子和积分步长h是两个东西只是习惯上取成h的整数倍。把两者写成同一个变量名后面改步长时 TD 的过渡品质会莫名其妙变差。3. 被控对象与总体方案汽车纵向速度环的模型搭建控制器写得再漂亮对象模型不对仿真跑出来的曲线也只能骗自己。这一章把车辆纵向动力学的取舍、执行器滞后的处理、扰动注入的位置、参数的量纲统一这几件事说清楚。3.1 纵向动力学从受力分析到阶次的取舍车辆纵向受力就四项驱动力Fx、滚动阻力Fr、空气阻力Fa、坡道阻力Fg。运动方程% 车辆纵向动力学二阶含执行器一阶滞后 Fx Fx h * ((u - Fx) / p.tau); % 驱动力一阶滞后 Fr p.f * m * p.g; % 滚动阻力 Fa 0.5 * p.rho * p.Cd * p.A * v^2; % 空气阻力 Fg m * p.g * sin(theta); % 坡道阻力 v v h * ((Fx - Fr - Fa - Fg) / m); % 车速积分如果只把对象建成一阶dv/dt b0*u f代码短、调试快但有个硬伤它假设驱动力能瞬时响应实际发动机或电机的转矩响应有 100300 ms 的滞后。这个滞后正好落在速度环带宽附近会让仿真结果明显偏乐观实车一上线就打脸。所以这里用二阶——多一个执行器状态多一个时间常数tau代价很小收益是仿真结论更可信。0.5*rho*Cd*A*v^2这一项是二次项车速 20 m/s 时大约是 160 N30 m/s 时翻到 370 N高速段的非线性很明显这也是 ADRC 相比固定增益 PID 有优势的地方。3.2 执行器滞后与扰动注入点把“难控”的地方摆到台面上验证一个抗扰控制器靠的不是阶跃响应好看而是扰动上来之后能不能快速拉回。仿真里至少要注入三类扰动而且注入点要选对扰动类型注入位置仿真写法考察点坡度突变阻力项Fg第 8 s 时theta从 0 阶跃到 4%观测器能否在 1 s 内估出并补偿载荷突变质量m第 14 s 时m从 1500 加到 1950参数摄动下的鲁棒性风阻突变阻力项Fa第 20 s 时Cd加 50%非线性项变化时的适应能力注意坡度要用弧度制4% 的坡度对应atan(0.04) ≈ 0.03998直接填 0.04 误差不到千分之一仿真够用但如果填成 4当成角度或百分比阻力能大到让车倒着走这是新手常见的翻车点。载荷突变这一段比较有意思质量变了b0就该跟着变但控制器里b0是固定的等于故意留了一个 30% 的参数失配给观测器去补。跑出来的曲线如果只是有个小凹坑然后 2 s 内恢复说明z3干活了。3.3 参数表与量纲统一仿真跑飞十有八九栽在这所有参数集中在一个结构体里单位和量纲逐条核对符号含义取值单位m整车质量1500kgCd风阻系数0.30—A迎风面积2.2m²rho空气密度1.2258kg/m³f滚动阻力系数0.015—tau执行器时间常数0.20sb0控制增益估计1/(m·tau) ≈ 0.00333m/(s²·N)h仿真与控制器步长0.001sb0的算法值得单独说一句从控制量u期望驱动力N到车速v的传递函数是1/(m·s·(tau·s1))把二阶写成标准形式v f b0·ub0就取1/(m·tau)。这个推导不用背记住“质量越大、滞后越大b0越小”就够了估的时候再打个八折更保险。量纲上最容易出问题的是车速需求文档里写的是 km/h模型里用的是 m/s中间忘了除 3.6控制器就会以为车只跑了三分之一的速度控制量一路加到底。这种错在仿真里表现为“车一直加速不停”排查的时候第一眼就要去看单位换算。3.4 整体信号流与三个模块的职责边界整个闭环的信号流是这样的期望车速v_ref进 TD输出过渡曲线v1及其导数v2车辆输出实测车速v进 ESO配合上一拍实际控制量u输出z1、z2、z3NLSEF 拿这五路信号算出u0减掉z3、除以b0再经限幅后送到执行器。执行器模型把u变成实际驱动力车辆模型把驱动力变成车速闭环合上。边界划分上有个约定TD 只关心指令不碰反馈ESO 只关心反馈和控制量不碰指令NLSEF 是唯一同时看到指令和反馈的地方。这个约定守住了后面加前馈、加限幅、加抗饱和都只改一个模块不会牵一发动全身。4. MATLAB 落地从 m 脚本闭环到 Simulink 模型仿真程序的落地路径有两条纯 m 脚本做定点闭环或者 Simulink 搭模块图。前者适合快速试参数、批量跑工况后者适合答辩展示和后续生成代码。这套程序两条路都留了先讲脚本。4.1 主仿真脚本一个循环跑完整个闭环主体就是一个 for 循环每拍按固定顺序调用四个函数顺序不能乱——ESO 必须用上一拍实际输出的u不能先用 NLSEF 算完再回头喂给它否则观测器等于偷看了答案抗扰性能在仿真里会虚高。%% ADRC 汽车纵向速度环 - 主仿真 clear; clc; close all; p struct(m,1500,Cd,0.30,A,2.2,rho,1.2258,f,0.015,g,9.81,tau,0.20); c struct(h,0.001,h0,0.01,r,8,wo,12,b0,1/(1500*0.20), ... kp,6,kd,2.5,delta,0.05,u_max,6000,u_min,0); T 25; N round(T / c.h); v 0; Fx 0; u_prev 0; % 对象状态 z1 0; z2 0; z3 0; % 观测器状态 v1 0; v2 0; % TD 状态 log_v zeros(N,1); log_ref log_v; log_u log_v; log_z3 log_v; for k 1:N t (k-1) * c.h; % 1) 期望车速0~5s 保持 05s 阶跃到 20 m/s v_ref 20 * (t 5); % 2) 扰动8s 上 4% 坡14s 加载 450kg20s 风阻加 50% [theta, m_now, cd_now] dist_profile(t, p); % 3) TD - ESO - NLSEF v1 v1 c.h * v2; v2 v2 c.h * fhan(v_ref - v1, v2, c.r, c.h0); [z1, z2, z3] leso3(v, u_prev, z1, z2, z3, c.wo, c.b0, c.h); [u_raw, ~] nlsef(v1, v2, z1, z2, z3, c); u_prev min(max(u_raw, c.u_min), c.u_max); % 限幅后的值下一拍喂回 ESO % 4) 对象推进 Fx Fx c.h * ((u_prev - Fx) / p.tau); Fres p.f * m_now * p.g 0.5 * p.rho * cd_now * p.A * v^2 ... m_now * p.g * sin(theta); v v c.h * ((Fx - Fres) / m_now); log_v(k) v; log_ref(k) v_ref; log_u(k) u_prev; log_z3(k) z3; end几个关键点u_prev存的是限幅后的值ESO 拿到的和对象拿到的完全一致Fres里用m_now而不是p.m因为载荷突变之后质量已经变了记录z3是为了后面看观测器到底估出了多少扰动这张图在答辩时比速度曲线更有说服力。跑完之后画三张图速度跟踪含期望曲线、控制量看有没有长时间饱和、z3估计值看扰动发生时有没有台阶响应。三张图摆在一起抗扰过程一目了然。4.2 Simulink 侧用 MATLAB Function 块替换 S-FunctionSimulink 模型里不用写 Level-2 S-Function那样调试成本太高。常规做法是用三个 MATLAB Function 块把上面三个函数原样搬进去控制器状态用persistent变量保存或者在每个块后接一个单位延迟模块做状态回环。模块清单和连线关系大致是Step产生期望车速进TD块输出v1、v2Vehicle Plant子系统由Integrator、Gain、Trigonometric Function搭出纵向动力学输出v与u_prev一起进ESO块三个块的输出进NLSEF块输出经Saturation限幅后分成两路一路回ESO一路进Vehicle Plant。求解器统一设为定步长FixedStepDiscrete步长 0.001和脚本保持一致。用persistent保存状态时要注意初始化只在第一次调用时执行改参数之后必须重启仿真否则旧的观测器状态还留在内存里跑出来的曲线会莫名其妙地带个初值偏移。这一条在答辩现场切参数演示时特别容易翻车养成“改完参数先 CtrlE 重置”的习惯。4.3 带宽法整定先 wo、再 b0、最后 TD 的 r整定顺序不能反。先给b0一个粗略值1/(m·tau)打八折然后把 TD 的r设得很大相当于直通kp、kd先按线性 PD 给个保守值最后扫wowo_list [6 10 14 20 30]; for i 1:numel(wo_list) c.wo wo_list(i); run_sim; % 复用 4.1 的仿真主体 fprintf(wo%2d 超调%5.2f%% 调节时间%5.2fs 扰动恢复%5.2fs\n, ... c.wo, overshoot(log_v, log_ref), settle_time(log_v, 20), recover_time(log_z3)); end判据很直白wo太小扰动发生后z3要三四秒才跟上速度曲线拖一个长尾巴wo太大控制量在稳态段开始高频抖动z3曲线毛刺明显这时候要么降wo要么把h再缩小一档。找到“扰动恢复快、稳态又不抖”的那个点通常就在 1020 之间。wo定下来之后再回头调b0把b0故意改小 30%看超调变化多少变化小说明鲁棒性够改大 30% 如果直接发散说明b0的余量不够往小再取一点。最后才去动 TD 的r因为它只影响指令过渡不影响抗扰放在最后调不会被前面的参数变化干扰。4.4 工况脚本把单次仿真变成一组对比单条曲线说明不了问题答辩要的是对比。写一个外层脚本把 ADRC 和固定增益 PID 放在同一组工况下跑cases {空载阶跃,满载阶跃,爬坡4%,载荷突变}; for i 1:numel(cases) [v_adrc, v_pid, ref] run_case(cases{i}); % 同一对象、同一扰动 m_adrc calc_metrics(v_adrc, ref); m_pid calc_metrics(v_pid , ref); fprintf(%-10s ADRC: 超调%.1f%% 稳态%.3f | PID: 超调%.1f%% 稳态%.3f\n, ... cases{i}, m_adrc.os, m_adrc.ess, m_pid.os, m_pid.ess); endcalc_metrics里就三个量超调百分比、调节时间进入 ±2% 误差带、稳态误差最后 2 s 的平均偏差。四个工况跑下来一张表八个数ADRC 的优势集中在“满载”和“爬坡”两栏这也正好对应第 1 章说的那个场景逻辑闭环。5. 避坑与排查仿真“跑通”之后最容易翻车的五个点曲线能画出来不等于程序对了。下面五条是这套东西调试过程中反复出现的每条按“看到什么、为什么、怎么改”写。5.1 起步阶段控制量长时间顶在限幅上现象阶跃给下去u直接冲到 6000 N 不动车速缓慢爬升超调倒是不大但响应慢得离谱。原因TD 的r给太大比如 50过渡曲线几乎是阶跃误差瞬间拉满或者kp给太大u0一步就冲到天上。执行器一旦饱和观测器看到的u是被削平的z3的估计跟着失真闭环实际处于开环状态。解决r按物理意义取乘用车 610 m/s² 足够同时把限幅值调到“稳态需求 50% 余量”20 m/s 匀速大约需要 700 N 驱动力6000 N 的上限对应 0.4 g 加速合理。真要提速优先加r而不是加kp。5.2 z3 曲线一直单向漂移越跑越大现象没有扰动的时候z3也缓慢往上爬几秒之后控制量被它拖得越来越小车速上不去。原因b0和真实增益不匹配观测器为了解释这个差额只能让z3持续输出一个偏置另一种可能是 ESO 里的u用了 NLSEF 的原始输出而不是限幅后的值饱和期间两者差得远误差全被算进扰动里。解决先把b0按1/(m·tau)重算一遍再乘 0.8检查 ESO 输入口接的到底是哪一路信号用 Scope 把u_raw和u_sat同时打出来看一眼两者在稳态应该重合。5.3 改小仿真步长之后反而发散现象h从 1 ms 改到 0.1 ms理论上更精确结果曲线开始高频振荡甚至数值爆炸。原因h0是跟着h走的但代码里写死成 0.01 没改相对步长变成了 100 倍fhan 的线性区判别失效另一种情况是wo没变wo*h虽然更小了本该更稳但如果之前是靠大步长的数值阻尼压住的振荡步长一小隐藏的不稳定就暴露了。解决h0用k*h的形式跟着h联动k取 520改步长之后重跑一次wo扫描别假设参数可以照搬。5.4 稳态曲线有细密毛刺放大看是周期性的现象车速在目标值附近 ±0.05 m/s 来回跳频率很高u和z3上同样有。原因fal的delta给太小误差在零点附近反复穿越线性区边界增益来回切换或者wo太大观测器把对象模型的离散化误差当成了真实扰动在追。解决delta按稳态误差量级取速度环 0.05 比较合适把wo降一档再看如果毛刺消失说明之前是观测器过激不是对象问题。实在要去毛刺可以在u后面加一个一阶低通时间常数取 23 个采样周期代价是相位滞后一点。5.5 换成变步长求解器结果完全对不上现象脚本里跑出来好好的搬到 Simulink 用ode45一跑超调和调节时间全变了有时候直接振荡。原因ADRC 的三个部件都是按固定步长离散推导的ode45会在每一步内部插值多次调用persistent状态被调用次数不受控等于控制器的时间基准乱了。解决Simulink 里控制器部分一律用FixedStepDiscrete或Discrete-Time Integrator对象部分可以用连续模块两者之间加Rate Transition。整个模型统一定步长最省事实在需要变步长就把控制器单独封成一个定步长子系统的Model Reference。6. 验收与答辩三张曲线、一张指标表把“抗扰”讲清楚答辩现场最怕的不是参数不好看是评委问“你怎么证明它抗扰”的时候答不上来。我一般按三步走先给指标门槛再给对比曲线最后给一张能扫一眼看懂的表。指标门槛按车型和工况定乘用车纵向速度环我用的这套考察项工况门槛值判定方式超调量0→20 m/s 空载阶跃≤ 5%峰值相对目标值的百分比调节时间同上≤ 4 s进入 ±2% 误差带且不再出去稳态误差20 m/s 匀速≤ 0.2 m/s最后 2 s 平均偏差扰动恢复时间4% 坡度阶跃≤ 2.5 s速度跌出 ±2% 到重回带内参数鲁棒性质量 30%仍满足上四项同工况重跑三张曲线固定搭配第一张速度跟踪图ADRC 和 PID 叠在一起重点圈出满载工况那一段的差异第二张控制量图证明没有长时间饱和否则前面所有性能指标都不作数第三张z3估计图坡度上来时那条台阶曲线就是最好的证据评委一看就知道观测器真的在工作。答辩时有个技巧值得说主动交代边界。比如“这套参数在 025 m/s 范围内验证过再高要考虑风阻二次项带来的增益变化需要重调b0”。主动把适用范围讲清楚比被问出来强得多也显得你真的跑过而不是抄的。还有个常被忽略的点——仿真步长和控制器周期要在答辩材料里写清楚。有人用 0.1 ms 步长跑出来的漂亮曲线被问一句“实车控制器跑 10 ms 你怎么办”就露馅了。稳妥的做法是直接按 1 ms 或 5 ms 出结果顺便给一张“步长 1 ms 和 10 ms 的对比”证明这套离散实现对周期不敏感这一页的含金量比多跑十个工况都高。说个自己的教训。早年做这类仿真我习惯先调参数再看曲线跑出个能看的形状就收工结果换一组工况全崩回头查才发现是b0的量纲错了——质量用了 kg时间常数用了 ms差了一千倍被z3硬生生补回来了看着稳其实全靠观测器死撑。从那以后我每次开工前都强制走一遍“单位自检”把m、tau、b0三个数代回1/(m·tau)手算一遍和代码里的值对上了才开始调参。这套 ADRC 仿真程序也是按这个习惯整理出来的参数表、脚本、Simulink 模型放在一起改车型就改结构体别去动函数体。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →