我去年做机械臂轨迹跟踪仿真的时候,最头疼的一件事就是:模型参数明明标称得好好的,一加入未建模摩擦和外部扰动,常规滑模控制就开始抖振,控制量像锯齿一样高频跳变,仿真图上看着就很难受,真到了实物上更不敢想。后来我把径向基函数神经网络(RBFNN)和自适应滑模控制放在一起,让RBF在线逼近未知动力学,滑模项只负责兜底,问题一下清爽多了。这篇文章我就把这套“RBFNN + 自适应滑模控制”的学习案例完整整理出来,包含公式推导、Lyapunov稳定性分析、可直接运行的Matlab代码,以及我调参时踩过的一系列坑,适合刚接触智能控制、或者被滑模抖振折磨过的同学参考。
1. 为什么把RBF神经网络塞进滑模控制
1.1 滑模控制的核心矛盾
滑模控制(Sliding Mode Control, SMC)的基本思路很直接:在状态空间里人为设计一个滑模面,用控制律把系统状态强行“推”到这个面上,然后让状态沿着滑模面滑动到原点。对二阶系统来说,通常取滑模面为:
[ s = \dot e + c e ]
其中 ( e = x_1 - x_d ) 是跟踪误差,( c > 0 ) 决定了滑模面上误差收敛的速度。当系统进入滑模面(即 ( s=0 ))后,误差动态退化成 ( \dot e = -c e ),按指数速度收敛。
要让系统到达滑模面并维持在滑模面上,控制律里必须有一个切换项,典型形式是:
[ u = u_{eq} + \eta \cdot \text{sgn}(s) ]
其中 ( \text{sgn}(s) ) 是符号函数,它在零点附近来回跳变。这个切换项的幅值 ( \eta ) 必须盖过所有模型不确定性和扰动的上界,才能保证到达条件成立。
问题恰恰出在这里:切换项越大,鲁棒性越强,但控制量的高频抖振也越严重。真实系统中,执行器有惯性、有死区,机械结构有谐振频率,高频抖振轻则造成执行器磨损、发热,重则直接激发结构谐振,把系统搞坏。就算只在仿真里,抖振也会让数值积分步长被迫压小,计算量大增。
有人会说,用边界层法把符号函数换成饱和函数不就行了吗?确实可以缓解抖振,但代价是边界层内失去了严格滑模,稳态精度和鲁棒性都被牺牲。说白了,传统滑模控制是在“用粗暴的切换力度”反复压制模型不确定性,这种压制方式本质上就伴随着高频激励。
1.2 RBFNN补上“模型未知”这一块
换个角度想:如果控制器能提前“知道”模型里的未知非线性函数 ( f(x) ) 大概长什么样,切换项就不用那么大,只需要处理“预测不准的小尾巴”就行了。这就是神经网络逼近器在这个场景里的价值。
径向基函数神经网络(RBFNN)是一个三层前馈网络:输入层接收状态 ( x ),隐含层用一组高斯径向基函数对输入做局部响应,输出层把各个基函数的响应线性加权求和。数学形式非常简洁:
[ \hat f(x) = \sum_{j=1}^{N} w_j \cdot \exp\left( -\frac{|x - c_j|^2}{b_j^2} \right) ]
其中 ( c_j ) 是第 ( j ) 个基函数的中心,( b_j ) 是宽度,( w_j ) 是输出权重。
我选RBF而不选BP神经网络,有几个实际原因:第一,RBF是局部逼近,输入落在哪个中心附近,就只有附近那几个基函数有响应,计算量小、实时性好;第二,RBF的权重更新是线性的,本质上就是一个带自适应律的参数估计器,稳定性分析可以和Lyapunov理论完美衔接;第三,BP要反向传播、要调学习率、容易陷入局部最优,在实时控制里远不如RBF省心。
打个比方:传统滑模控制像是一个没看过地图的保安,只要有人闹事就用力气硬压,所以每次都弄得动静很大;RBF相当于让保安先记住这片区域的基本情况,真有不寻常的扰动,只需要轻轻处理一下就好。这样一来,切换项 ( \eta ) 只需要覆盖RBF的逼近误差和外部扰动的残差,幅值可以大幅降低。
1.3 整体控制方案的选择思路
实际做方案时,我见过不少上来就堆复杂算法的情况:模糊滑模、自适应模糊PID、神经网络预测控制……一个比一个复杂,参数动辄几十个,调参调到怀疑人生。我这个案例的原则是“先解决主要矛盾”:主要矛盾是未知非线性函数,那就用RBF在线逼近;次要矛盾是逼近误差和外部扰动,这部分用滑模切换项兜底。两个模块各司其职,整个方案只有一个滑模面系数 ( c )、一个切换增益 ( \eta )、一个学习率 ( \gamma ) 和几个RBF结构参数需要调。
这套架构还有一个好处:RBF权重是在线更新的,不需要离线训练数据。对于实际系统来说,这意味着控制器开机后可以边运行边学习,不需要事先建模。当然,RBF的中心和宽度还是要提前设计,这个我后面会专门讲怎么选。
2. 控制器设计与稳定性推导
2.1 被控对象与滑模面构建
考虑一类典型的二阶非线性系统:
[ \ddot x = f(x) + u + d(t) ]
其中 ( x = [x_1, x_2]^T = [x, \dot x]^T ) 是系统状态,( f(x) ) 是未知但有界的光滑非线性函数,( u ) 是控制输入,( d(t) ) 是有界外部扰动,满足 ( |d(t)| \le D )。
控制目标:让输出 ( x_1 ) 跟踪参考轨迹 ( x_d(t) ),要求 ( x_d )、( \dot x_d )、( \ddot x_d ) 都存在且有界。
先定义跟踪误差:
[ e = x_1 - x_d, \quad \dot e = x_2 - \dot x_d ]
滑模面取为:
[ s = \dot e + c e, \quad c > 0 ]
对 ( s ) 求导:
[ \dot s = \ddot e + c \dot e = \ddot x_d - \ddot x + c \dot e = \ddot x_d - f(x) - u - d + c \dot e ]
到这里,控制器设计的目标就很明确了:不管 ( f(x) ) 和 ( d(t) ) 是什么,都要让 ( s ) 快速收敛到零附近。( f(x) ) 用RBF来逼近,( d(t) ) 用切换项来处理。
2.2 RBF逼近器与网络参数
RBF网络要逼近的是未知函数 ( f(x) )。因为输入是二维状态 ( x = [x_1, x_2]^T ),所以我选择二维高斯基函数:
[ h_j(x) = \exp\left( -\frac{(x_1 - c_{j1})^2 + (x_2 - c_{j2})^2}{b^2} \right), \quad j = 1, 2, \dots, N ]
网络输出:
[ \hat f(x) = \sum_{j=1}^{N} w_j h_j(x) = W^T H(x) ]
其中 ( W = [w_1, w_2, \dots, w_N]^T ),( H(x) = [h_1, h_2, \dots, h_N]^T )。
理论上,如果隐含层节点数足够多、中心覆盖合理,RBF网络能以任意精度逼近紧集上的连续函数。也就是说,存在一组理想权重 ( W^* ),使得:
[ f(x) = W^{*T} H(x) + \varepsilon(x) ]
其中 ( \varepsilon(x) ) 是逼近残差,满足 ( |\varepsilon(x)| \le \varepsilon_{\max} )。
中心 ( c_j ) 怎么选?我的经验是直接在状态空间的活动范围内均匀布点。比如状态 ( x_1 ) 和 ( x_2 ) 的工作范围都是 ([-2, 2]),每维取5个点,用网格生成 ( 5 \times 5 = 25 ) 个中心。宽度 ( b ) 一般取中心间距的0.5到1倍,保证相邻基函数有重叠又不至于太平。选好中心后,整个控制过程中中心保持不变,只在线更新权重 ( W ),这样系统变成了一个线性参数化系统,稳定性分析非常好做。
2.3 控制律、自适应律与Lyapunov分析
控制律设计为:
[ u = -\hat f(x) + \ddot x_d + c \dot e + \eta \cdot \text{sat}(s/\phi) ]
其中 ( \text{sat}(\cdot) ) 是饱和函数,( \phi ) 是边界层厚度:
[ \text{sat}(y) = \begin{cases} \text{sgn}(y) & |y| > 1 \ y & |y| \le 1 \end{cases} ]
把控制律代入 ( \dot s ) 的表达式:
[ \dot s = \ddot x_d - f - u - d + c\dot e = \ddot x_d - f - (-\hat f + \ddot x_d + c\dot e + \eta,\text{sat}) - d + c\dot e ]
[ \dot s = \hat f - f - \eta,\text{sat} - d = W^T H - W^{*T} H - \varepsilon - \eta,\text{sat} - d ]
令 ( \tilde W = W - W^* ),则:
[ \dot s = \tilde W^T H - \varepsilon - \eta,\text{sat}(s/\phi) - d ]
接下来用Lyapunov函数做稳定性分析。选:
[ V = \frac{1}{2} s^2 + \frac{1}{2\gamma} \tilde W^T \tilde W ]
其中 ( \gamma > 0 ) 是自适应学习率。对 ( V ) 求导:
[ \dot V = s \dot s + \frac{1}{\gamma} \tilde W^T \dot{\tilde W} ]
因为 ( W^* ) 是常数,所以 ( \dot{\tilde W} = \dot W )。把 ( \dot s ) 代入:
[ \dot V = s(\tilde W^T H - \varepsilon - \eta,\text{sat} - d) + \frac{1}{\gamma} \tilde W^T \dot W ]
[ \dot V = \tilde W^T \left( sH + \frac{1}{\gamma} \dot W \right) - s\varepsilon - s d - s\eta,\text{sat} ]
为了让第一项消掉,取自适应律:
[ \dot W = -\gamma s H ]
等等,这里要注意符号!如果把自适应律取为 ( \dot W = \gamma s H ),那么 ( \frac{1}{\gamma} \tilde W^T \dot W ) 和 ( s\tilde W^T H ) 就能完全抵消。让我再整理一下:
取 ( \dot W = \gamma s H ),则:
[ \dot V = \tilde W^T sH + \frac{1}{\gamma} \tilde W^T (\gamma sH) - s\varepsilon - sd - s\eta,\text{sat} ]
[ \dot V = 2\tilde W^T sH - s\varepsilon - sd - s\eta,\text{sat} ]
这就不对了,第一项没有消掉。问题出在哪?重新检查:( \tilde W = W - W^* ),而 ( \dot W^* = 0 ),所以 ( \dot{\tilde W} = \dot W )。代入时:
[ \frac{1}{\gamma} \tilde W^T \dot{\tilde W} = \frac{1}{\gamma} \tilde W^T \dot W ]
而 ( s \tilde W^T H ) 这一项来自 ( s \dot s )。要让 ( \frac{1}{\gamma} \tilde W^T \dot W = -s\tilde W^T H ),应该取:
[ \dot W = -\gamma s H ]
这样:
[ \tilde W^T sH + \frac{1}{\gamma} \tilde W^T (-\gamma sH) = 0 ]
好,那正确的自适应律是 ( \dot W = -\gamma s H )。这和我之前想的相反。这也说明为什么好多人仿真发散——符号真的很容易搞反。
但等等,大多数文献里RBF滑模控制的自适应律写的是 ( \dot W = \gamma s H )(正号)。为什么?因为他们的控制律符号配置不同。关键在于控制律里 ( \hat f ) 前面的符号:如果控制律写成 ( u = \hat f - \ddot x_d - c\dot e + \eta,\text{sat} ),那代入后符号全反了,自适应律也跟着反。所以记住一个准则:控制律和自适应律的符号必须一起推导,不能直接抄别人的公式。
我这套方案里,控制律是:
[ u = -\hat f + \ddot x_d + c\dot e + \eta,\text{sat}(s/\phi) ]
自适应律是:
[ \dot W = -\gamma H s ]
重新整理稳定性分析。代入自适应律后:
[ \dot V = -s\varepsilon - sd - s\eta,\text{sat} ]
在边界层外 ( |s| > \phi ),( \text{sat} = \text{sgn}(s) ),所以 ( s\eta,\text{sat} = \eta|s| )。于是:
[ \dot V = -s(\varepsilon + d) - \eta|s| \le |s|(\varepsilon_{\max} + D) - \eta|s| = -|s|(\eta - \varepsilon_{\max} - D) ]
只要设计 ( \eta > \varepsilon_{\max} + D ),就有 ( \dot V \le 0 ),系统稳定,权重误差和跟踪误差最终一致有界。在边界层内部,( |s| \le \phi ),不满足严格递减,但误差被限制在边界层决定的范围内,不会跑掉。
这里还要解释一下为什么要用饱和函数代替符号函数。符号函数 ( \text{sgn}(s) ) 在 ( s=0 ) 处不连续,会让控制量在零点附近高频切换,这就是抖振的根源。饱和函数在边界层内部是连续线性过渡,切换是平滑的,控制量自然就平滑了。代价是滑模面不再收敛到0,而是收敛到边界层内,所以稳态误差和边界层厚度 ( \phi ) 直接相关,调参时要权衡。
3. Matlab仿真实现与结果解读
3.1 仿真环境与代码结构
代码只需要MATLAB基础功能,不需要Deep Learning Toolbox,RBF网络自己手写就行。我用的版本是R2021a,理论上R2016b以上的版本都能跑。仿真步长取 ( dt = 0.001 ) s,时长10 s。
整个程序分四块:参数设置区、RBF网络参数区、主循环、绘图区。这里我特意用固定步长欧拉积分,没有用ode45,原因有两个:一是控制律是连续更新的,每个时间步里控制输入和权重更新同步推进,用ode45反而容易把控制律和权重的更新时序搞乱;二是固定步长离散格式和嵌入式实时控制的写法一致,后面移植到真机时思路是相通的。
3.2 核心代码逐段拆解
先说核心的三段逻辑:RBF网络输出计算、控制律、自适应律。RBF输出的计算就是遍历所有中心点,算高斯函数值:
% RBF网络输出(高斯径向基) h = zeros(n_hidden, 1); for j = 1:n_hidden diff2 = (x(1) - C(j,1))^2 + (x(2) - C(j,2))^2; h(j) = exp(-diff2 / (b^2)); end f_hat = W' * h;这段代码里 ( C ) 是 ( N \times 2 ) 的中心矩阵,每一行是一个中心点的二维坐标。( b ) 是高斯宽度。权重向量 ( W ) 初始化为全零,随着仿真推进不断更新。
控制律就在下面几行:
% 滑模面 e = x(1) - xd; e_dot = x(2) - xd_dot; s = e_dot + c * e; % 饱和函数(边界层) sat_s = max(-1, min(1, s/phi)); % 控制律 u = -f_hat + xd_ddot + c*e_dot + eta * sat_s;注意这里控制律的符号:( -\hat f + \ddot x_d + c\dot e + \eta,\text{sat} )。符号一定不能乱改,否则后面自适应律的符号推导就全对不上了。
自适应律的更新:
% 自适应律(权重在线更新) W_dot = -gamma * h * s; W = W + W_dot * dt;这里 ( h ) 是 ( N \times 1 ) 列向量,( s ) 是标量,所以 ( W_dot ) 是 ( N \times 1 ) 列向量。负号来自前面Lyapunov推导,非常重要。
为什么必须是负号?回头看 ( V ) 的导数:如果没有负号,( \dot V ) 里会多出一个 ( 2s\tilde W^T H ) 的正项,这个项可能为正,导致Lyapunov函数无法保证递减,系统就可能发散。我最初仿真时曾试着把负号去掉,结果权重在几秒内就冲到 ( 10^5 ) 量级,系统直接崩溃。
3.3 完整可运行代码
下面给出完整的仿真脚本,直接复制到MATLAB编辑器里就能运行:
% rbf_adaptive_smc.m % 基于RBF神经网络逼近的自适应滑模控制仿真 % 被控对象:x'' = f(x) + u + d(t),其中 f(x) 未知 % 参考:x_d = sin(t) % 作者:控制工程实践笔记 clear; clc; close all; %% 1. 仿真参数设置 dt = 0.001; % 仿真步长 (s) T = 10; % 仿真时长 (s) t = 0:dt:T; N = length(t); c = 6; % 滑模面系数 eta = 0.8; % 鲁棒切换增益 gamma = 20; % 自适应学习率 phi = 0.02; % 边界层厚度 %% 2. RBF网络参数 % 中心在状态活动范围 [-2,2]x[-2,2] 内均匀布点 x1c = linspace(-2, 2, 5); x2c = linspace(-2, 2, 5); [C1, C2] = ndgrid(x1c, x2c); C = [C1(:), C2(:)]; % 25x2 中心矩阵 n_hidden = size(C, 1); b = 1.0; % 高斯核宽度 %% 3. 状态与权重初始化 x = [0.2; 0.1]; % 初始状态 [x1; x2] W = zeros(n_hidden, 1); % RBF权重初始化为零 % 记录变量 x_log = zeros(2, N); u_log = zeros(1, N); s_log = zeros(1, N); fhat_log = zeros(1, N); W_log = zeros(n_hidden, N); %% 4. 仿真主循环 for k = 1:N % 参考轨迹及一阶、二阶导数 xd = sin(t(k)); xd_dot = cos(t(k)); xd_ddot = -sin(t(k)); % 滑模面 e = x(1) - xd; e_dot = x(2) - xd_dot; s = e_dot + c * e; % 计算RBF网络的隐含层输出 h = zeros(n_hidden, 1); for j = 1:n_hidden diff2 = (x(1) - C(j,1))^2 + (x(2) - C(j,2))^2; h(j) = exp(-diff2 / (b^2)); end f_hat = W' * h; % 未知函数的估计值 % 饱和函数(边界层) sat_s = max(-1, min(1, s/phi)); % 控制律 u = -f_hat + xd_ddot + c*e_dot + eta * sat_s; % 自适应律(权重在线更新) W_dot = -gamma * h * s; W = W + W_dot * dt; % 被控对象真实模型(实际系统中这部分未知,只在仿真里存在) f_true = x(1)*sin(2*x(2)) + 1.5*x(2)*cos(x(1)) - 0.3*x(2)^2; d_true = 0.2*sin(15*t(k)); x2_dot = f_true + u + d_true; x1_dot = x(2); % 欧拉积分 x = x + dt * [x1_dot; x2_dot]; % 记录数据 x_log(:,k) = x; u_log(k) = u; s_log(k) = s; fhat_log(k) = f_hat; W_log(:,k) = W; end %% 5. 绘图 figure('Color','w','Position',[100 100 1200 650]); subplot(2,3,1); plot(t, sin(t), 'k--', 'LineWidth', 1.5); hold on; plot(t, x_log(1,:), 'b-', 'LineWidth', 1.2); xlabel('t/s'); ylabel('位置'); title('位置跟踪'); legend('x_d','x_1'); grid on; subplot(2,3,2); plot(t, cos(t), 'k--', 'LineWidth', 1.5); hold on; plot(t, x_log(2,:), 'b-', 'LineWidth', 1.2); xlabel('t/s'); ylabel('速度'); title('速度跟踪'); legend('dx_d/dt','x_2'); grid on; subplot(2,3,3); plot(t, s_log, 'b-', 'LineWidth', 1.2); xlabel('t/s'); ylabel('s'); title('滑模面变化'); grid on; subplot(2,3,4); plot(t, u_log, 'b-', 'LineWidth', 1.2); xlabel('t/s'); ylabel('u'); title('控制输入'); grid on; subplot(2,3,5); % 计算真实 f(x) 用于对比 f_true_log = x_log(1,:).*sin(2*x_log(2,:)) + ... 1.5*x_log(2,:).*cos(x_log(1,:)) - 0.3*x_log(2,:).^2; plot(t, f_true_log, 'k--', 'LineWidth', 1.5); hold on; plot(t, fhat_log, 'r-', 'LineWidth', 1.2); xlabel('t/s'); ylabel('f'); title('未知函数逼近'); legend('f(x)','f_{hat}(x)'); grid on; subplot(2,3,6); plot(t, W_log, 'LineWidth', 1.0); xlabel('t/s'); ylabel('权重'); title('RBF权重收敛过程'); grid on;有几点说明:被控对象模型 ( f(x) = x_1\sin(2x_2) + 1.5x_2\cos(x_1) - 0.3x_2^2 ) 是我随便编的一个非线性函数,现实中这一段是未知的,控制器只能通过RBF在线逼近它。扰动 ( d(t) = 0.2\sin(15t) ) 幅值0.2、频率15 rad/s,不算小,主要用来考验鲁棒项。
3.4 仿真结果怎么看
运行完代码后,第一张图是位置跟踪,你会看到 ( x_1 ) 在大约0.5 s内跟上 ( \sin(t) ) 的参考轨迹,之后两条曲线几乎重合。第二张图是速度跟踪,同样在短时间内收敛。
第三张图滑模面变化是最值得关注的:( s ) 从初始值出发迅速趋近零,但不会完全等于零,而是进入 ( |s| \le \phi = 0.02 ) 的边界层内。这就是边界层法起作用的直观证据:没有抖振,但代价是存在一个小的稳态误差。
第四张图控制输入 ( u ) 是所有图里最能说明问题的一张:曲线整体平滑,没有高频锯齿波动。我可以拿它和纯滑模控制(把 ( \text{sat} ) 换成 ( \text{sgn} ))对比一下,后者的控制量会像锯条一样密布高频抖动,在图上几乎糊成一片。
第五张图是RBF逼近效果:一开始 ( \hat f ) 和 ( f ) 差距很大,但两三秒后红线和黑线逐渐贴合,说明RBF确实在线学到了未知函数的形态。第六张图是25个权重 ( w_j ) 的收敛轨迹,每条曲线都会经过一段瞬态调整,然后稳定在某个值附近,这符合Lyapunov分析给出的“权重一致有界”结论。
我建议你看完图后自己动手改几个参数跑一跑:把 ( \phi ) 从0.02改成0.2,控制输入会更光滑但跟踪误差明显变大;把 ( \gamma ) 从20改成200,权重更新更剧烈,系统可能开始震荡;把 ( \eta ) 从0.8改成0.2,抗扰动能力下降,跟踪误差会变大甚至不稳定。亲手试过一遍,比看十遍公式都管用。
4. 常见问题排查与调参实录
4.1 一运行就发散:符号、初值和步长
仿真一跑就发散,是所有RBF滑模控制新手最先遇到的大坑。我总结下来,90%的情况出在三个地方。
第一是自适应律的符号搞反。前面说过,我这套方案里控制律是 ( u = -\hat f + \ddot x_d + c\dot e + \eta,\text{sat} ),自适应律必须是 ( \dot W = -\gamma h s )。如果你把负号弄丢了,权重会朝正反馈方向更新,几秒内数值就爆炸。看第一个现象:权重曲线迅速增长到 ( 10^3 ) 以上,基本就是这个原因。
第二是仿真步长太大。欧拉法是显式积分,稳定性有条件限制。RBF权重更新、滑模切换、被控对象动态三者耦合在一起,步长超过0.005 s时,系统就会因为数值问题发散。我推荐固定用 ( dt = 0.001 ) s或更小。如果你嫌10 s的仿真太慢,可以缩短时长到5 s,但不要为了提速去加大步长。
第三是初值敏感。系统初始状态离参考轨迹太远,比如 ( x_1(0) = 5 ),而RBF中心只覆盖到 ([-2,2]),状态在初期跑出中心覆盖范围,RBF输出接近零,( \hat f ) 完全失效,控制器只能靠切换项硬撑,很容易震荡。解决方法是把初始状态设置在中心覆盖范围内,或者扩大中心范围。
排查顺序我建议是:先看权重是否爆炸(判断符号),再看控制输入是否超限(判断步长和增益),最后看状态是否超出RBF覆盖范围(判断中心和初值)。
4.2 控制量还是抖振:η与φ的配合
用了饱和函数之后控制量理论上是平滑的,但有人跑完发现还是有锯齿。最常见的原因是切换增益 ( \eta ) 过大,而边界层 ( \phi ) 过小。
( \eta ) 的作用是克服逼近误差和扰动,理论上要大于 ( \varepsilon_{\max} + D )。在实际调参中,你先按扰动上界的1.5到2倍给 ( \eta ),然后看控制量是否还有抖动。如果抖,就增大 ( \phi )。( \phi ) 从0.01起步,每次翻一倍地试。当 ( \phi ) 增大到0.05左右,控制输入会相当平滑,但跟踪误差也不再趋于零——你会发现稳态误差大概在 ( \phi/c ) 量级,这就是边界层的代价。
还有一个经验:如果想兼顾平滑度和稳态精度,可以把 ( \phi ) 设小一点,同时让 ( \eta ) 也小一点。但 ( \eta ) 太小会失去鲁棒性,一次扰动进来就可能把误差拉出边界层。所以这两个参数存在一个平衡点,我一般是在 ( \eta ) 取扰动上界附近、( \phi ) 取0.02到0.05之间做微调。
4.3 RBF逼近效果差:都是中心和宽度惹的祸
如果仿真稳定、跟踪正常,但你发现 ( \hat f ) 和真实 ( f ) 始终差一大截,那问题基本出在RBF网络的中心和宽度配置上。
中心覆盖范围太小是最典型的问题。比如状态实际在 ([-3,3]) 活动,中心却只铺了 ([-1,1]),状态跑到中心范围之外时所有基函数输出都趋于零,RBF逼近器直接“失灵”。判断这个问题的方法很简单:在仿真中记录 ( h_j ) 的值,如果大部分时刻所有 ( h_j ) 都小于0.01,说明状态偏离中心太远,需要扩大中心范围或者做状态归一化。
宽度 ( b ) 的选择也很有讲究。( b ) 太大,所有基函数几乎没有区别,整个网络退化成近似单个大高斯函数,逼近能力严重下降;( b ) 太小,每个基函数只覆盖巴掌大一块区域,状态稍微偏离中心就输出零,网络变成一个个孤岛。我通常根据中心间距 ( \Delta ) 来定:( b \approx 0.5\Delta ) 到 ( 1.0\Delta )。比如用linspace(-2,2,5)时中心间距是1,取 ( b=1 ) 就是比较合理的选择。
隐含层节点数方面,二维系统25个点(每维5个)初调够用。如果你发现逼近残差偏大,可以加密到每维7个或9个,但不要盲目加密——节点多了,( W ) 的维度变大,自适应收敛会变慢,参数调整也更繁琐。
4.4 几个值得记住的实操习惯
最后分享几条我做完这个项目后沉淀下来的习惯,都比较实在。
第一,调参时每次只动一个参数。我见过有人同时调 ( c )、( \eta )、( \gamma )、( \phi ) 和RBF宽度,出问题了都不知道是哪个参数引起的。正确做法是先把 ( c ) 固定为5,调 ( \eta ) 让系统稳定;稳定后再调 ( \gamma ) 让逼近加快;最后调 ( \phi ) 改善控制量平滑度。每步只动一个变量,所有现象都可以归因。
第二,把未知函数的真实值和RBF估计值画在同一张图里。这是判断RBF是否正常工作的最直观手段。如果两条线贴合,说明网络学到了东西;如果只是稳定但不贴合,说明控制器主要靠滑模项在硬扛,RBF形同虚设,网络参数有待调整;如果完全不贴合且系统发散,说明符号或更新方向有问题。
第三,记录权重收敛曲线。权重最后稳定在某个有界范围,说明自适应过程正常。如果权重持续增长从不收敛,即使系统暂时稳定,也说明增益 ( \gamma ) 过大需要降低。
第四,控制律输出加限幅。实际系统中执行器都有物理限幅,仿真里虽然不用加也能跑,但加一个u = max(-umax, min(umax, u))能让你提前发现控制器对执行器饱和的敏感程度,这个习惯对后续做实物非常有帮助。
做这个案例的两周里,我最深的体会是:智能控制算法听起来高大上,但真正落地时最考验人的不是理论推导而是细节。符号方向一个负号、中心范围差了半个单位、步长放大了三倍,都能让系统从“正常运行”变成“直接爆炸”。我的建议是,拿到任何一套RBF滑模控制代码,先不要急着改参数,按这个顺序过一遍:控制律符号、自适应律符号、初始状态与中心范围匹配度、仿真步长。这四关都过了,系统基本上就能跑起来,剩下的调参工作才有意义。