简介:面向船舶运动控制与智能控制方向的研究者,聚焦欠驱动船舶在模型不确定、外界干扰及速度不可测条件下的轨迹跟踪问题,提供基于神经网络、动态面与自适应滑模相结合的控制方案设计与仿真验证。压缩包共29个文件,大小5.72MB,以MATLAB的m脚本和mdl模型为主,辅以txt程序说明与caj文献资料,涵盖RBF、MLP等不同神经网络结构下的直线与圆形轨迹跟踪仿真,便于对照学习控制律推导与Simulink实现。目前已有434人浏览学习。资源价值在于将反步设计、动态面技术、最小参数学习法、非线性观测器等理论方法系统集成,并给出可运行的仿真程序,适合作为课程设计、学位论文或科研预研的参考素材。使用者可结合文献深入理解算法细节,再通过仿真验证不同扰动界假设下控制律的鲁棒性,从而快速迁移至类似的欠驱动系统控制设计任务。
1. 欠驱动船舶轨迹跟踪:神经网络和动态面为什么能把滑模自适应从“理论证明”推到“仿真收敛”
当海面船舶只有推进器和艏摇力矩、没有侧向推力时,要沿着一条随时间变化的轨迹走,问题本质上是一个欠驱动系统的跟踪稳定问题。传统的反步法把虚拟控制重复求导后会放大噪声,而纯滑模用符号函数换来了抖振。RBF神经网络在前馈通道里在线逼近模型不确定性,动态面通过一阶滤波斩断微分链,再保留滑模的鲁棒性,这一组合是当前工程仿真中最容易收敛的结构。这篇博文从数学模型讲到仿真调参,适合已经掌握反步法和滑模基础、想落地轨迹跟踪算法的运动控制工程师。
2. 欠驱动船舶模型与复合型控制结构的数学骨架
2.1 三自由度水面船模型和欠驱动约束
船舶在海面上的水平运动通常用惯性系下的位置和航向 $\eta=[x,y,\psi]^T$、随船速度 $\nu=[u,v,r]^T$ 描述。运动学方程为:
$\dot{\eta}=R(\psi)\nu$
其中 $R(\psi)$ 是横摆角旋转矩阵,满足 $R^{-1}=R^T$。动力学上,我习惯把刚体惯性、附加质量、科氏力和阻尼合并写成集总参数形式:$M\dot{\nu}+C(\nu)\nu+D(\nu)\nu=\tau+\tau_d$。
欠驱动体现在控制输入 $\tau=[\tau_u,0,\tau_r]^T$,横向速度 $v$ 没有直接控制力。实现轨迹跟踪时,不能简单地对所有速度通道都设计反步虚拟控制,必须把横向误差通过航向角 $r$ 去消除。这也是标题中“欠驱动”不能删掉的原因。为了后续设计,我把动力学改写成标量形式:
$\dot{u}=f_u(\nu)+g_u\tau_u+d_u(t)$ $\dot{v}=f_v(\nu)+d_v(t)$ $\dot{r}=f_r(\nu)+g_r\tau_r+d_r(t)$
其中 $f_u,f_v,f_r$ 包含了质量矩阵、科氏力和阻尼的耦合项,$d_i(t)$ 代表海流、风浪和未建模动态。$g_u,g_r$ 是已知的控制增益,在标准模型中通常等于对应惯性项的倒数。这样拆分后,神经网络要逼近的对象就是 $f_u+d_u$ 和 $f_r+d_r$,物理含义清楚。
2.2 轨迹跟踪误差在船体坐标系下的重新表示
期望轨迹为 $\eta_d(t)$,在全局坐标系下的参考速度来自 $\dot{\eta}_d$。为了把误差反馈从全局坐标系拉回到船体坐标系,我采用如下投影:
$e_x=(x-x_d)\cos\psi+(y-y_d)\sin\psi$ $e_y=-(x-x_d)\sin\psi+(y-y_d)\cos\psi$ $e_\psi=\psi-\psi_d$
这个变换的好处是:即使船在旋转,$e_x,e_y$ 的方向始终对齐船艏和船舷,比例控制器的物理意义清楚。对时间求导并整理,得到误差运动学:
$\dot{e}_x=u-\dot{x}_d\cos\psi-\dot{y}_d\sin\psi+r e_y$ $\dot{e}_y=v+\dot{x}_d\sin\psi-\dot{y}d\cos\psi-r e_x$ $\dot{e}\psi=r-\dot{\psi}_d$
这里最关键的是第二式:$e_y$ 的动态里没有直接的 $v$ 控制量,但出现了 $-r e_x$。这说明只要船保持一个合理的前向速度,艏摇角速度 $r$ 就能间接修正横向误差。这也是欠驱动轨迹跟踪和全驱动轨迹跟踪在控制器结构上的本质区别。
2.3 动态面滤波器取代虚拟控制求导
反步法里虚拟控制 $\alpha_u,\alpha_r$ 一旦确定,下一步就需要计算 $\dot{\alpha}_u,\dot{\alpha}_r$,而它们又包含系统的二阶动态,迭代下去就是“微分爆炸”。动态面控制的核心是引入一阶低通滤波器:
$\tau_i\dot{\alpha}{fi}+\alpha{fi}=\alpha_i,\quad \alpha_{fi}(0)=\alpha_i(0)$
其中 $i=u,r$,$\tau_i$ 是滤波时间常数。控制器直接使用滤波值作为速度参考,并用 $(\alpha_i-\alpha_{fi})/\tau_i$ 替代 $\dot{\alpha}_i$。这样做不是精确求导,而是用一个一阶惯性环节去平滑虚拟控制信号。时间常数通常在 0.05~0.3s 之间,太大会让参考速度滞后,太小滤波器作用趋近于直连,“微分爆炸”又会回来。
2.4 RBF神经网络逼近不确定项
我把 $f_u,d_u$ 合并成 $F_u(\nu,t)$,用RBF神经网络在线逼近。RBF属于前馈神经网络的一种,隐藏层输出是高斯径向基函数:
$h_j(x)=\exp\left(-\frac{|x-c_j|^2}{2b_j^2}\right)$
输入 $x=[u,v,r,\psi,\sin\psi,\cos\psi]^T$,输出 $F_u\approx W_u^T h(x)$。选择RBF而不是多层感知机,是因为它的局部逼近性质稳定,更新律是线性的,理论上只需要证明权重有界。中心 $c_j$ 按状态范围覆盖,宽度 $b_j$ 一般取相邻中心的距离。下面是一个很简短的Python类,在后续仿真里我会直接复用:
import numpy as np class RBFNetwork: """径向基函数网络,用于在线逼近模型不确定项""" def __init__(self, n_input, n_hidden, gamma=10.0, sigma=0.001): self.c = np.random.uniform(-1.5, 1.5, size=(n_hidden, n_input)) self.b = np.ones(n_hidden) self.w = np.zeros(n_hidden) self.gamma = gamma self.sigma = sigma def basis(self, x): diff = x - self.c return np.exp(-np.sum(diff**2, axis=1) / (2 * self.b**2)) def predict(self, x): return self.w @ self.basis(x) def update(self, x, s, dt): hvec = self.basis(x) self.w += dt * self.gamma * (hvec * s - self.sigma * self.w)这段代码要注意几点:中心点用随机均匀分布铺开,适合多维输入;如果输入维数超过一维,需要把c改成网格或随机点,这里用随机点;权重更新先乘滑模面再乘高斯输出,完成的是梯度下降,gamma决定估计收敛速度,sigma防止权重漂移。实际使用前需要把状态归一化到中心所在范围,否则高斯函数会恒等于0,网络永远不学习。
3. 自适应滑模控制器设计与参数矩阵
3.1 从速度误差到滑模面
根据第2章的误差模型,我先定义虚拟控制律。常见的做法是让纵向通道负责 $e_x$,让艏摇通道同时承担 $e_\psi$ 和横向误差 $e_y$ 的耦合调节:
$\alpha_u = -k_1 e_x + \dot{x}_d\cos\psi+\dot{y}d\sin\psi$ $\alpha_r = -k_2 e\psi + \dot{\psi}_d - k_3 e_y$
这里的 $k_1,k_2,k_3$ 全部取正数。把 $\alpha_u,\alpha_r$ 送入动态面滤波器,得到平滑后的参考速度 $\alpha_{fu},\alpha_{fr}$。实际速度与滤波参考的偏差记为:
$\zeta_u=u-\alpha_{fu}$ $\zeta_r=r-\alpha_{fr}$
为了让外扰作用时稳态误差消失,我引入积分型滑模面:
$s_u=\zeta_u+\lambda_u\int_0^t\zeta_u d\tau$ $s_r=\zeta_r+\lambda_r\int_0^t\zeta_r d\tau$
$\lambda_u,\lambda_r$ 是积分系数,通常取 0.1~1.0。积分项会带来超调,在实船应用中要加抗饱和逻辑,仿真阶段则直接累积。
3.2 控制律、自适应律与鲁棒项
对 $s_u,s_r$ 求导,并把第2章的动力学代入,可以得到包含未知非线性函数的表达式。未知部分由RBF网络输出 $\hat{F}_u=\hat{W}_u^T h(x)$ 和 $\hat{F}_r=\hat{W}_r^T h(x)$ 逼近。最终控制律写为:
$\tau_u = g_u^{-1}\left[ -\hat{F}u + \dot{\alpha}{fu} - \lambda_u\zeta_u - \eta_u,\mathrm{sat}(s_u/\varepsilon_u) \right]$
$\tau_r = g_r^{-1}\left[ -\hat{F}r + \dot{\alpha}{fr} - \lambda_r\zeta_r - \eta_r,\mathrm{sat}(s_r/\varepsilon_r) \right]$
其中 $\dot{\alpha}{fi}$ 直接取自动态面滤波器状态:$\dot{\alpha}{fi}=(\alpha_i-\alpha_{fi})/\tau_i$,不需要对期望轨迹求三阶导。饱和函数保留边界层 $\varepsilon_i$,代替符号函数,避免把抖振输送给推进器。
权重自适应律采用带sigma修正的梯度律:
$\dot{\hat{W}}_u = \Gamma_u\left(h(x)s_u-\sigma_u\hat{W}_u\right)$ $\dot{\hat{W}}_r = \Gamma_r\left(h(x)s_r-\sigma_r\hat{W}_r\right)$
$\Gamma_u,\Gamma_r$ 是正定自适应增益,sigma修正是为了防止持续激励不足时权重无限增长。选取李雅普诺夫函数:
$V=\frac{1}{2}s_u^2+\frac{1}{2}s_r^2+\tilde{W}_u^T\Gamma_u^{-1}\tilde{W}_u+\tilde{W}_r^T\Gamma_r^{-1}\tilde{W}_r$
对时间求导后,利用RBF网络的逼近误差有界性和鲁棒切换增益 $\eta_i$ 大于逼近误差上界,可以得到 $\dot{V}$ 负半定,即系统一致最终有界。这一步的工程含义是:只要切换增益压得住“伪逆”误差和外部扰动,系统就不会因模型偏差而发散。
3.3 控制器参数表和调整顺序
下表是我在中等规模无人船上常用的初始参数,单位制为SI:
| 参数 | 推荐范围 | 作用 | 一般初始值 |
|---|---|---|---|
| $k_1$ | 0.5~3 | 纵向位置误差增益 | 1.5 |
| $k_2$ | 1~5 | 航向误差增益 | 3 |
| $k_3$ | 0.1~1 | 横向误差到艏摇的耦合 | 0.5 |
| $\tau_u,\tau_r$ | 0.05~0.3 | 动态面滤波时间常数 | 0.15 |
| $\lambda_u,\lambda_r$ | 0.1~1 | 滑模积分系数 | 0.5 |
| $\eta_u,\eta_r$ | 0.1~2 | 滑模切换增益 | 0.8 |
| $\varepsilon_u,\varepsilon_r$ | 0.01~0.1 | 边界层厚度 | 0.02 |
| $\Gamma_u,\Gamma_r$ | 5~20 | RBF自适应增益 | 10 |
| $\sigma_u,\sigma_r$ | 0.001~0.01 | 权重修正系数 | 0.005 |
调整顺序不要乱。先给一组偏保守的值,把等效控制跑到稳定,再逐渐加大 $\eta$。$\tau$ 并不是越小越好,它必须在带宽和测量噪声之间取中点;神经网络中心范围要与实际状态归一化范围一致,否则高斯函数输出恒为0,权重自适应永远不更新。
4. Python仿真:从“能跑”到“收敛”的完整流程
4.1 仿真环境与被控对象
仿真目的是验证控制律、滤波和神经网络三个模块之间的接口。我不使用Simulink,而是用Python配合numpy,方便切进度条和改参数。被控对象用一艘简化的小型欠驱动无人船:
- 质量项 $m_x=25.8$,$m_y=33.8$,转动惯量 $m_z=2.76$;
- 线性阻尼近似为 $d_u=2$,$d_v=7$,$d_r=0.5$;
- 在动力学里额外叠加 $d_u=0.3\sin(0.2t)$,$d_v=0.2\cos(0.1t)$,$d_r=0.1\sin(0.3t)$,作为风浪扰动。
期望轨迹选择圆形:
$x_d(t)=10\sin(0.05t),\quad y_d(t)=10(1-\cos(0.05t))$
这样初始位置正好在原点附近,目标以恒定角速度绕动,前向速度期望约为 0.5 m/s。初始条件设为 $x_0=0.5$,$y_0=-0.3$,$\psi_0=0.1$,$u_0=0.1$。
4.2 控制器核心代码
下面给出可以直接放进脚本的主循环片段。RBF网络使用第2章的类,被控对象用四阶Runge-Kutta推进。
import numpy as np dt = 0.01 T = 60.0 t = 0.0 state = np.array([0.5, -0.3, 0.1, 0.1, 0.0, 0.0]) alpha_fu = 0.0 alpha_fr = 0.0 int_u = 0.0 int_r = 0.0 tau_u = 0.15 tau_r = 0.15 lam_u = 0.5 lam_r = 0.5 eta_u = 0.8 eta_r = 0.8 eps_u = 0.02 eps_r = 0.02 m_x = 25.8 m_r = 2.76 net_u = RBFNetwork(6, 21, gamma=10.0, sigma=0.005) net_r = RBFNetwork(6, 21, gamma=10.0, sigma=0.005) while t < T: x, y, psi, u, v, r = state xd = 10*np.sin(0.05*t) yd = 10*(1-np.cos(0.05*t)) psid = 0.05*t + np.pi/2 xd_dot = 0.5*np.cos(0.05*t) yd_dot = 0.5*np.sin(0.05*t) psid_dot = 0.05 ex = (x - xd)*np.cos(psi) + (y - yd)*np.sin(psi) ey = -(x - xd)*np.sin(psi) + (y - yd)*np.cos(psi) epsi = np.arctan2(np.sin(psi-psid), np.cos(psi-psid)) alpha_u = -1.5*ex + xd_dot*np.cos(psi) + yd_dot*np.sin(psi) alpha_r = -3.0*epsi + psid_dot - 0.5*ey alpha_fu += ((-alpha_fu + alpha_u)/tau_u)*dt alpha_fr += ((-alpha_fr + alpha_r)/tau_r)*dt d_alpha_fu = (alpha_u - alpha_fu)/tau_u d_alpha_fr = (alpha_r - alpha_fr)/tau_r z_u = u - alpha_fu z_r = r - alpha_fr int_u += z_u*dt int_r += z_r*dt s_u = z_u + lam_u*int_u s_r = z_r + lam_r*int_r x_rbf = np.array([u, v, r, psi, np.sin(psi), np.cos(psi)]) f_hat_u = net_u.predict(x_rbf) f_hat_r = net_r.predict(x_rbf) u_ref_acc = -f_hat_u + d_alpha_fu - lam_u*z_u - eta_u*np.clip(s_u/eps_u, -1, 1) r_ref_acc = -f_hat_r + d_alpha_fr - lam_r*z_r - eta_r*np.clip(s_r/eps_r, -1, 1) force_u = m_x * u_ref_acc force_r = m_r * r_ref_acc net_u.update(x_rbf, s_u, dt) net_r.update(x_rbf, s_r, dt) state = rk4_step(state, force_u, force_r, t) t += dt其中u_ref_acc和r_ref_acc是期望加速度,最终推力要乘上质量项。np.clip(s/eps, -1, 1)就是饱和函数实现,控制律中的积分项由lam_u*int_u带回。alpha_fu的增量式更新就是动态面滤波,d_alpha_fu是控制律需要的虚拟控制导数。
被控对象和RK4函数补全如下:
def ship_dynamics(state, force_u, force_r, t): m_x, m_y, m_z = 25.8, 33.8, 2.76 d_u, d_v, d_r = 2.0, 7.0, 0.5 x, y, psi, u, v, r = state x_dot = u*np.cos(psi) - v*np.sin(psi) y_dot = u*np.sin(psi) + v*np.cos(psi) psi_dot = r u_dot = (-d_u*u + force_u + 0.3*np.sin(0.2*t)) / m_x v_dot = (-d_v*v + 0.2*np.cos(0.1*t)) / m_y r_dot = (-d_r*r + force_r + 0.1*np.sin(0.3*t)) / m_z return np.array([x_dot, y_dot, psi_dot, u_dot, v_dot, r_dot]) def rk4_step(state, force_u, force_r, t): k1 = ship_dynamics(state, force_u, force_r, t) k2 = ship_dynamics(state + 0.5*dt*np.array(k1), force_u, force_r, t+0.5*dt) k3 = ship_dynamics(state + 0.5*dt*np.array(k2), force_u, force_r, t+0.5*dt) k4 = ship_dynamics(state + dt*np.array(k3), force_u, force_r, t+dt) return state + (dt/6.0)*(np.array(k1)+2*k2+2*k3+k4)逻辑说明:ship_dynamics里额外加的三个正弦项就是被控对象内部的风浪扰动,控制器不感知它们的真实表达式,只靠RBF网络在线估计。干扰强度故意设得比控制器鲁棒项小,是为了先验证体系收敛,不然一开始就同时调大扰动和增益,问题难以定位。
4.3 调参对照表和仿真输出
我跑下来的经验是:前 20 秒误差最大,因为初始 $u$ 太小,纵向速度无法立刻跟上期望值;RBF 网络需要约 10 秒的激励才会把权重充实起来。以下是同一模型下三组参数的结果:
| 调参动作 | 现象 | 下一步 |
|---|---|---|
| $\tau$ 从0.15调到0.02 | 位置误差缩小,但速度通道出现高频振荡 | 调回0.08,再用边界层抑制 |
| $\eta$ 从0.8调到2.0 | 横向最大误差从0.4m降到0.15m,但推力矩抖动 | 保持1.2,配合 $\varepsilon=0.05$ |
| 网络中心范围从 $[-1.5,1.5]$ 扩大到 $[-6,6]$ | 权重初始化阶段误差变大,收敛变慢 | 把状态做归一化,再使用原范围 |
输出误差指标建议记录RMS误差和控制力矩标准差。用这两者衡量跟踪性能和能耗,避免只看轨迹图像。
提示:如果你在仿真里发现神经网络权重短时间内增长到几十甚至上百,先检查滑模面是否在零附近抖动。权重更新是对误差的积分,滑模面噪声过大,权重自然会被带偏。
5. 收敛性检查与抖振抑制的调试顺序
5.1 用李雅普诺夫函数判据观察收敛过程
仿真时除了画轨迹,更重要的是记录李雅普诺夫函数变化轨迹。在第3章定义的 $V$ 中,网络权重项其实是可以计算的。我通常在每个采样周期记录:
record_V.append(0.5*s_u**2 + 0.5*s_r**2 + np.sum(net_u.w**2)/net_u.gamma)如果 $V$ 持续下降并最终进入一个较小的边界层,说明神经网络和滑模的配合是健康的。如果 $V$ 先降后升,那不是控制器发散,而是动态面滤波初期 $\alpha_f$ 与 $\alpha$ 之间存在较大偏差,一般发生在开始后的前5个采样周期。解决方法是初始化时让滤波状态等于虚拟控制初值,这个我在第2章已经约定过。
5.2 调试顺序的三个检查点
第一个检查点是动态面滤波输出是否饱和。如果 $\tau$ 太小,$\alpha_f$ 追踪 $\alpha$ 的速度太快,滤波器等于旁路;如果太大,速度误差会带上明显延迟。调试时先把 $\alpha_u,\alpha_{fu}$ 画在一起,观察追踪是否平滑。第二个检查点是RBF中心是否覆盖状态空间。归一化后的状态往往在 $[-1,1]$ 内,如果中心范围设到 $[-6,6]$,高斯函数几乎恒定输出,权重更新永远无法定位到局部特征。第三个检查点是滑模项是否在零附近抖动。若s的符号频繁变化,先减小积分系数 $\lambda$,再适当加宽边界层 $\varepsilon$,不要一上来就降低切换增益 $\eta$。
5.3 从仿真到半实物仿真的一点经验
我会刻意在动力学中加入与状态相关的非线性项,比如 $d_u=2+0.5u^2$,再观察控制器是否稳定。这类非线性对神经网络很有压力,但也是检验系统鲁棒性的好方法。另一个经验是采样频率与动态面时间常数的比值:至少要保证 $\tau > 5T_s$,否则数字离散化会让一阶滤波失稳。最后,如果实际船只能提供有限的推力和力矩,给控制律加饱和限幅后,积分滑模容易积分饱和。处理方式是把积分项限幅在 $[-0.2,0.2]$,或者改用条件积分:当控制量饱和时暂停累积。这些细节在仿真中不容易暴露,但上了真平台都会成为第一现场。
本文还有配套的精品资源,点击获取