考虑扰动的欠驱动船舶轨迹跟踪自适应滑模控制水面上的铁疙瘩要听话可不容易。三万吨的货轮在风浪里扭秧歌舵机转得冒火星子还追不上预定航线这场景让多少控制工程师头秃。传统PID那套在平静水域还行遇上横风横流立马歇菜——这时候就得掏出滑模控制这把瑞士军刀。先看船的运动学模型简化后的状态方程长这样def ship_dynamics(state, tau_u, tau_r, d_eta): u, v, r, x, y, psi state m 1.2e5 # 质量 Iz 4.5e6 # 转动惯量 Xu -5.0e3 # 水动力系数 u_dot (tau_u m*v*r - Xu*u)/m d_eta[0] v_dot (-m*u*r)/m d_eta[1] r_dot (tau_r)/Iz d_eta[2] # 运动学方程 x_dot u*np.cos(psi) - v*np.sin(psi) y_dot u*np.sin(psi) v*np.cos(psi) psi_dot r return np.array([u_dot, v_dot, r_dot, x_dot, y_dot, psi_dot])这段代码暴露了欠驱动系统的软肋——控制输入只有纵向推力τu和转艏力矩τr横向运动v完全是被动响应的。更麻烦的是最后那个d_eta项代表风浪流的扰动像牛皮糖一样粘在方程里。对付这种情况老司机们会设计这样的滑模面def sliding_surface(x_des, x_actual): s np.zeros(3) # 位置误差 eta_error x_des[0:2] - x_actual[0:2] # 航向误差 psi_error x_des[2] - x_actual[2] # 滑模面设计 s[0] eta_error[0] 0.5 * np.tanh(10*eta_error[0]) # 纵向饱和函数 s[1] eta_error[1] 0.3 * psi_error # 横向-航向耦合项 s[2] psi_error 0.2 * np.sign(eta_error[1]) # 航向补偿项 return s这个滑模面暗藏玄机tanh函数代替sign函数防抖振横向误差和航向误差耦合处理欠驱动约束最后那个sign项专门治侧向漂移。考虑扰动的欠驱动船舶轨迹跟踪自适应滑模控制但扰动d_eta就像薛定谔的猫——你知道它存在但不知道多大。这时候自适应律就该上场了class AdaptiveLaw: def __init__(self): self.theta np.array([0.0, 0.0, 0.0]) # 扰动估计 self.gamma 0.1 # 自适应增益 def update(self, s, states): # 投影算子防参数漂移 rho 1.5 # 估计上限 theta_norm np.linalg.norm(self.theta) if theta_norm rho: self.theta (rho/theta_norm)*self.theta # 自适应律 self.theta self.gamma * s * np.array([states[0], states[1], 1])这个自适应模块像智能吸尘器边跑边估算扰动大小。里面的投影操作相当于给估计值加了围栏防止算法想太多导致发散。最后把这些模块组装成控制器def adaptive_smc_controller(x_des, x_actual, theta_hat): s sliding_surface(x_des, x_actual) K np.diag([8.0, 5.0, 3.0]) # 滑模增益 # 控制量计算 u_control np.dot(K, s) theta_hat[0]*x_actual[0] r_control np.dot(K[2], s[2]) theta_hat[2] # 伪指令转换解决欠驱动问题 tau_u 1.2e5 * u_control tau_r 4.5e6 * r_control return tau_u, tau_r注意控制量计算里的theta_hat项这就是自适应估计的扰动补偿。这里的伪指令转换其实玩了个花活——把横向运动误差转嫁到航向控制上正是处理欠驱动问题的关键技巧。仿真时会出现有意思的现象前20秒控制器在疯狂试探扰动大小船体走位像醉汉等自适应律摸清扰动底细后轨迹突然变得服服帖帖。这过程就像新手司机变老司机——开始手忙脚乱熟悉车况后就能单手搓方向盘了。这种方法的阴暗面在于参数整定——滑模增益K选大了抖振明显选小了跟踪慢自适应增益gamma更是敏感调参时能真切体会到什么叫过犹不及。不过一旦调教得当看着船舶在8级风浪里走出刀切斧砍般的航迹那种满足感堪比在《欧卡2》里完美倒船入库。