☰
基于LOS的无人水面艇路径跟踪仿真:运动学、动力学与Simulink工程实践
2026/10/22 3:35:38 网站建设 项目流程

简介:面向无人水面艇(USV)轨迹规划与路径跟踪研究人员,提供基于LOS视线法的完整MATLAB/Simulink实现。资源围绕“Thinking”主题,覆盖从运动学/动力学模型构建、LOS制导律设计到航行轨迹生成的闭环代码,可用于复现路径跟踪仿真、分析控制参数影响以及验证改进策略。压缩包共31个文件,包含18个mat数据文件、6个m脚本或函数、1个slx模型文件及工程辅助文件,总大小333KB,结构紧凑,适合初学者快速上手或研究者对比算法效果。已有1084人学习下载。通过阅读控制律代码与模型文件,可掌握USV建模、LOS跟踪误差分析与调参方法,并将同一套框架迁移至实船应用或扩展至多障碍物环境。

1. 基于LOS的无人水面艇路径跟踪:这套仿真源码能帮你省下多少调参时间

做水面无人艇(USV)路径跟踪的人,大概率都遇到过这种情况:算法论文读了一堆,LOS导引律的公式背得滚瓜烂熟,真到自己搭Simulink仿真的时候,模型初始化报错、控制器震荡、路径切换跳变,一个晚上全耗在排错上。这套Thinking_轨迹规划_航行轨迹_基于LOS无人水面艇的路径跟踪_own3oh源码包,就是用来看清LOS路径跟踪全链路怎么落地的一套现成工程——从USV运动学模型、动力学模型到LOS导引控制器,再到Simulink仿真工程,全都齐了。

适合两类人:一是刚开始接触USV路径跟踪的研究生,需要一套能跑通、能改参数的基线工程;二是已经在做LOS相关控制、但想对比不同模型细节或排查自己仿真问题的从业者。这套资源的价值不在代码多复杂,而在于它把运动学、动力学、控制器三层拆开了,每一层都能单独改、单独验证,这是很多论文开源代码做不到的。

2. 运动学与动力学模型拆解:USV的LOS导引先要解决“船怎么动”的问题

LOS(Line-of-Sight)路径跟踪的核心,是让USV的航向角去追踪一个由视线方向决定的期望艏向角。但前提是,你得先有足够准确的模型来描述“船怎么动”。这套资源里最值得先读的不是Controller,而是两个模型文件:USV_Kinematic_Model.m和USV_Dynamic_Model.m。一个管几何关系,一个管受力运动,二者在仿真里的角色完全不同。

2.1 三自由度运动学模型:位置、艏向角与速度的几何关系

水面无人艇在水平面的运动,通常用三个自由度描述:纵荡(surge)、横荡(sway)和艏摇(yaw)。运动学模型描述的是位置和姿态随速度的变化关系,不涉及力。

function [x_dot, y_dot, psi_dot] = USV_Kinematic_Model(u, v, r, psi) % 三自由度运动学模型 % 输入: u-纵荡速度(m/s), v-横荡速度(m/s), r-艏摇角速度(rad/s), psi-艏向角(rad) % 输出: x_dot-大地坐标系下x轴速度, y_dot-大地坐标系下y轴速度, psi_dot-艏向角速度 x_dot = u * cos(psi) - v * sin(psi); y_dot = u * sin(psi) + v * cos(psi); psi_dot = r; end

这段代码的核心逻辑,就是把船体坐标系下的速度(u, v, r)转换到大地坐标系下的位置变化率。很多人第一次写这个模型容易忽略一件事:u和v是船体坐标系下的速度分量,而x_dot和y_dot是大地坐标系下的速度分量,中间必须经过艏向角psi的旋转变换。不转换的话,航向和位置会出现明显的方向错位。

参数说明:

  • u(纵荡速度)和v(横荡速度)的单位是m/s,仿真中通常通过动力学模型计算得到;
  • psi是艏向角(单位rad),在LOS导引律中就是被控对象的核心状态量;
  • 很多论文里会引入“侧滑角”beta来修正v的影响,那是升级版,基础版直接用v就行。

在实际工程中,这个运动学模型往往还要加上位置初始值x0和y0的积分环节。这套源码里虽然没有显式写出积分过程,但Simulink的Integrator模块承担了这个工作,模型文件只负责计算导数。

2.2 LOS导引律的几何含义:视线角公式与前视距离的作用

LOS导引律的基本思路是:在期望路径上取一个“前视点”,让USV朝那个点航行。这个前视点与USV当前位置的连线方向,就是期望艏向角。核心公式来自几何关系,而不是复杂的控制理论。

function psi_ref = LOS_Guidance(x, y, path_x, path_y, lookahead_dist) % LOS导引律:计算期望艏向角 % 输入: x,y-当前位置; path_x,path_y-期望路径点序列; lookahead_dist-前视距离(m) % 输出: psi_ref-期望艏向角(rad) % 找到距离当前位置最近路径点的索引 dists = sqrt((path_x - x).^2 + (path_y - y).^2); [~, idx] = min(dists); % 计算前视点:在最近路径点的前方取前视距离对应的点 if idx < length(path_x) % 路径方向角 path_angle = atan2(path_y(idx+1) - path_y(idx), path_x(idx+1) - path_x(idx)); % 前视点坐标 lookahead_x = path_x(idx) + lookahead_dist * cos(path_angle); lookahead_y = path_y(idx) + lookahead_dist * sin(path_angle); % 期望艏向角 = 当前位置到前视点的连线方向 psi_ref = atan2(lookahead_y - y, lookahead_x - x); else % 到达终点 psi_ref = atan2(path_y(end) - y, path_x(end) - x); end end

这个实现里有一个关键参数:lookahead_dist(前视距离)。它直接决定了跟踪的“激进程度”——前视距离小,USV会更快地转向以贴近路径,但容易振荡;前视距离大,USV会以更平缓的弧线接近路径,跟踪误差会变大但更平稳。工程上的经验值是取船体长度的2到3倍,但具体要看路径的曲率和最大艏摇角速度。

注意这段代码里的索引判断:if idx < length(path_x),这是为了避免在最后一个路径点之后越界。很多人在自己写LOS的时候不会处理终点逻辑,导致仿真最后一步报错——这是个看似微小但很常见的坑。

2.3 从运动学到动力学:为什么这一套里要拆成两个文件

运动学模型只算出了位置变化率,但产生这些速度的力从哪来?这就是动力学模型的作用。USV_Dynamic_Model.m基于刚体动力学和流体力学,计算出在推进力和舵力作用下,纵荡速度、横荡速度和艏摇角速度的变化率。

function [u_dot, v_dot, r_dot] = USV_Dynamic_Model(u, v, r, tau_u, tau_r, m, I_z, Xu, Yv, Nr) % 三自由度欠驱动USV动力学模型(简化) % 输入: u,v,r-当前速度状态; tau_u-纵荡控制力(N); tau_r-艏摇控制力矩(N*m) % m-船体质量(kg); I_z-艏摇转动惯量(kg*m^2) % Xu,Yv,Nr-线性阻尼系数 % 输出: 各自由度的加速度 % 刚体动力学(忽略科氏力和向心力项,适用于低速USV) u_dot = (tau_u + m * v * r + Xu * u) / m; v_dot = (0 - m * u * r + Yv * v) / m; % 欠驱动:无侧向推力 r_dot = (tau_r + Nr * r) / I_z; end

这里有几个值得注意的工程细节:

第一,这是个“简化”模型——忽略了科氏力矩阵中的非线性耦合项,只保留了线性阻尼。对于低速USV(通常航速1~2 m/s),这个简化的精度足够用;但如果你的USV要做高速机动,这套模型就不够看了,需要引入非线性阻尼项。

第二,注意v_dot这行的写法:0 - m*u*r表示横荡方向没有直接推力输入,这就是“欠驱动”的含义——USV只有推进器和舵,没有侧向推进器,所以横荡运动是被动产生的。这是USV路径跟踪和AUV(自主水下机器人)最大的区别,也是LOS导引律在USV上应用时一个非常关键的约束。

第三,阻尼系数Xu、Yv、Nr通常为负值。它们代表水对船体运动的阻力。如果这些参数和模型实际不符,会导致系统动态响应偏差很大——比如Nr设得太大,船转不过弯来;设得太小,船会来回摆动且需要很长时间才能稳定。

3. 跑通Simulink仿真:从USV.slx到Controller.m的完整信号流

拿到这份源码包,第一件事不是双击打开.slx就开始点Run,而是要把文件结构先捋清楚。这套工程里除了核心的USV.slx和三个.m文件,还有一堆辅助目录和脚本,如果不搞明白各自用途,很容易在仿真报错时找不到北。

3.1 工程文件结构:slx主模型、功能脚本与缓存目录的分工

解压Thinking_轨迹规划_航行轨迹_基于LOS无人水面艇的路径跟踪_own3oh_水面无人艇_源码.rar之后,目录里会看到这样几类东西:

文件/目录类型作用
USV.slxSimulink模型主仿真工程,包含所有模块和信号连接
Controller.mMATLAB函数LOS导引控制器,计算期望艏向角和输出控制量
USV_Kinematic_Model.mMATLAB函数运动学模型,计算位置变化率
USV_Dynamic_Model.mMATLAB函数动力学模型,计算速度变化率
Diff1.m/Diff2.m/Diff3.mMATLAB脚本初始化/后处理脚本,配置参数或生成对比图
slprj、_sfprj、_jitprj缓存目录Simulink代码生成中间文件,无手动修改价值
sl_proj.tmw、modeladvisor工程配置文件Simulink工程元数据

需要特别说明的是slprj、_sfprj、_jitprj这几个目录。它们在Simulink每次点击Run或Build时自动生成,里面是编译过程中产生的中间代码,不需要手动去改。如果仿真出现诡异问题,一个常见的修复操作反而是把这些缓存目录删掉,重新打开模型,让Simulink全部重新生成。

而Diff1.m到Diff3.m,从命名习惯来看大概率是这个作者在做参数对比实验时留下的脚本——Diff1可能是给第一组参数仿真、Diff2给第二组,Diff3做结果对比。如果不确定哪个脚本对应哪种功能,建议按顺序先执行Diff1.m,它会生成基础工作区变量,这样打开USV.slx时才不会出现“变量未定义”的红色报错。

3.2 初始化脚本的执行策略:为什么先跑Diff1.m是关键步骤

Simulink模型里的很多参数都来自工作区变量。如果直接双击打开USV.slx就点Run,MATLAB会报“Undefined function or variable”之类的错误,因为工作区里根本没有这些变量。解决方式就是先运行初始化脚本。

% Diff1.m - 仿真初始化脚本(示例结构) % 该脚本用于向工作区写入模型所需的全部参数 % 仿真步长配置 sim_time = 200; % 仿真时长(秒) dt = 0.1; % 定步长(秒),用于固定步长求解器 % USV船体参数 m = 23.0; % 船体质量 kg I_z = 1.8; % 艏摇转动惯量 kg*m^2 length_L = 1.2; % 船长 m % 路径定义(一条直线路径,从起点到终点) path_x = [0, 50, 100, 150]; % 路径点x坐标 path_y = [0, 20, 40, 60]; % 路径点y坐标 % LOS控制器参数 lookahead_dist = 3.0; % 前视距离(这里取2.5倍船长) Kp_psi = 1.5; % 艏向角比例控制增益 Kd_psi = 0.3; % 艏向角微分控制增益 % 控制力限制 tau_u_max = 20; % 纵荡推力上限(N) tau_r_max = 5; % 艏摇力矩上限(N*m) disp('Diff1.m 执行完毕,仿真参数已写入工作区。');

这段初始化脚本的逻辑很直白:把所有需要在仿真中引用的变量统一写进工作区。sim_time和dt控制仿真时长和精度——定步长的好处是可复现性,同一套参数每次跑出来的结果完全一致,这在算法调试阶段非常重要。

参数说明:

  • m和I_z来自实船参数,这套工程使用的是小型USV(船长约1.2米),对应中等偏轻的船体;
  • path_x和path_y定义了一条折线路径,注意头尾坐标差形成了45度左右的转角,这是为了测试LOS在路径拐点处的表现;
  • lookahead_dist = 3.0约为2.5倍船长,属于中间档设定,如果你想让USV更激进地追踪路径,可以把它调到1.0~1.5倍船长。

有一个调试习惯值得养成:每次修改Diff1.m里的参数后,重新运行对应脚本,再跑Simulink仿真。很多人直接在Simulink里改常量模块的值,但那样做没有留痕,下次发现仿真异常时根本不知道是哪个参数动过。

3.3 Controller.m的接口设计:从期望艏向角到控制输出的完整链路

Controller是整个仿真里的核心部件。它的输入是当前状态和期望路径,输出是实际控制量。搞清楚它的接口和工作流程,你才能真正理解LOS导引闭环是怎么运转的。

function [tau_u, tau_r] = Controller(x, y, psi, u, v, r, path_x, path_y, lookahead_dist, Kp_psi, Kd_psi) % LOS控制器:计算控制力,跟踪期望路径 % 输入: x,y-当前位置(m); psi-当前艏向角(rad); u,v-线速度(m/s); r-艏摇角速度(rad/s) % path_x,path_y-期望路径点; lookahead_dist-前视距离(m); Kp_psi,Kd_psi-PD增益 % 输出: tau_u-纵荡控制力(N); tau_r-艏摇控制力矩(N*m) % 第一步:调用LOS导引律,计算期望艏向角 psi_ref = LOS_Guidance(x, y, path_x, path_y, lookahead_dist); % 第二步:对艏向角误差做PD控制 e_psi = wrapToPi(psi_ref - psi); % 角差归一化到[-pi, pi],避免跳变 tau_r = Kp_psi * e_psi - Kd_psi * r; % 第三步:纵荡控制,保持恒定巡航速度 u_ref = 1.5; % 期望巡航速度(m/s) tau_u = 5.0 * (u_ref - u); % 简单的速度P控制 end

这段代码里最值得学习的地方,是第二行的wrapToPi处理。如果不用它,当期望艏向角从179度变到-179度时(即绕了一圈),角度差会计算成358度,控制器会拼命去“追”这个358度的差值,实际上路径就在正前方。wrapToPi把误差归一化到-180度到180度之间,消除这种跳变问题——这是所有角度控制器的必修课,也是很多新手第一次跑LOS仿真时“船在原地打转”的常见原因。

参数说明:

  • Kp_psi = 1.5是比例增益,决定了对航向误差的反应强度——过大会导致振荡,过小会导致跟踪迟钝;
  • Kd_psi = 0.3是微分增益,提供阻尼,抑制振荡——但要注意仿真步长太大会使微分项噪声放大;
  • 纵荡控制用的是固定期望速度u_ref = 1.5加上比例控制,在实际工程中这个部分可能换成油门控制或速度规划。

另外一个细节:tau_u和tau_r在输出后,还要在Simulink中经过限幅模块(或者你在初始化脚本里定义了tau_u_max和tau_r_max)来限制物理范围。如果这条链路没做好,在路径起点位置误差较大时,控制器输出会非常大,仿真直接发散。

3.4 Simulink模型内信号流:从路径输入到船体响应的五个关键环节

跑通这套仿真,光有函数文件还不够,还要理解USV.slx内部的结构。虽然不能用截图展示,但从一个工程项目的标准架构来推断,这套模型的信号流大致如下:

  1. 路径生成模块:从工作区读取path_x和path_y,输出给LOS控制器。这段可以是一个From Workspace模块,也可以直接把路径序列放在Constant里。
  2. 控制器模块:内部的MATLAB Function调用Controller.m,输入为当前位置/速度/艏向状态,输出为tau_u和tau_r。
  3. 动力学模块:接收控制量,内部调用USV_Dynamic_Model.m,输出u_dot、v_dot、r_dot。
  4. 积分器模块:将加速度积分成速度(u,v,r),再将速度通过运动学模型积分成位置(x,y,psi)。
  5. 可视化模块:通过Scope或XY Graph显示跟踪轨迹和误差曲线。

从工程角度看,这套结构是非常合理的——每个模块职责清晰,替换或修改任意一环都不会影响其他环路的正常工作。比如你想换一个导引律,那就只改Controller模块内部逻辑;想换船体模型,可以替换动力学模型而不影响控制器的代码结构。这也是为什么我们说这套资源适合做基线研究的原因:可拆分、可替换、可对比。

4. LOS路径跟踪仿真避坑指南:五个实测翻车场景与排查方法

把源码跑通只是第一步,真正让人崩溃的是后面调参数、换路径、改模型的时候遇到的各种问题。以下五个坑是我从这套工程里复盘出来的,每条都标注了现象、原因和解决手段,希望能让后来的使用者少走弯路。

4.1 坑一:舱向角误差跳变导致USV原地打转

现象:仿真中USV在路径起点附近就开始绕圈,轨迹完全不收敛到期望路径上,看起来像“原地画圆”。

原因:控制器中没有对角度误差做归一化处理。当路径方向与当前艏向夹角大于180度时,计算出的角度差会异常增大,控制器输出饱和。本套工程里的Controller.m使用了wrapToPi,但如果你自己复写控制器漏掉这一步,十有八九会碰到这个问题。

解决:对任何角度误差计算,都加上wrapToPi或wrapTo180处理。在MATLAB中可以使用wrapToPi函数,它会将输入角度映射到[-pi, pi]区间。验证方法:在仿真中观察e_psi是否一直处于[-pi, pi]范围内。

注意:wrapToPi要求输入是角度制(rad),不要传角度制(degree),否则会得到完全错误的归一化结果。

4.2 坑二:Simulink报错“Simulink cannot solve the algebraic loop”

现象:点击Run后,MATLAB报错说模型中存在代数环,无法求解。

原因:这是Simulink仿真中最经典的报错之一。在USV模型中,如果动力学模型直接输出的u_dot被用于同一个时间步内计算控制量tau,而tau又反过来参与u_dot的计算,就形成了代数环。常见于Controller模块内同时使用u(当前速度)和tau(控制量),但模型里没有加延迟模块。

解决:打开Simulink模型的Diagnostics设置,在“Simulink > Diagnostics > Solver”中找到“Algebraic loop”选项,设置为“none”,Simulink会用数值迭代破环。但如果代数环涉及物理刚性较大的系统,数值迭代会导致求解变慢且不稳定。更工程化的做法是在Controller输出端加一个Unit Delay或Memory模块,把控制量延迟一个步长再送入动力学模型。

4.3 坑三:仿真进行到路径拐点时出现剧烈振荡

现象:直线路径跟踪没问题,切换到折线路径的拐点附近时,USV的艏向角发生剧烈振荡,甚至失控。

原因:LOS导引律在直线段表现良好,但当路径方向发生剧烈变化时,前视点会突然跳变到新方向,导致期望艏向角产生阶跃变化。如果此时前视距离lookahead_dist设置得过小,USV相当于要求瞬间完成大角度转弯,而动力学模型有延迟,结果就是“参考值突变-响应滞后-超调-再校正”的正反馈振荡。

解决:把前视距离适当加大,让USV提前感知路径变化。在本工程里,船长1.2m对应的lookahead_dist建议设置在2.4到3.6m之间。另外一个做法是参考路径做平滑处理——比如对折线拐点做圆弧过渡或采用B样条插值,这样LOS导引律的输入路径就不会有突变角度。

4.4 坑四:初始化脚本运行时提示“Unrecognized function or variable”

现象:执行Diff1.m时报错,提示某个变量未定义,且报错位置指向disp('Diff1.m 执行完毕...')之前的一行赋值语句。

原因:大概率是脚本依赖了某个非默认路径下的自定义函数,而当前MATLAB的工作目录或路径(path)里没有包含该函数所在的文件夹。比如Diff1.m里调用了USV_Kinematic_Model.m,但当前工作目录不对,MATLAB找不到这个函数。

解决:在脚本开头加一行cd切换到源码包根目录,或者用addpath(genpath(pwd))把当前目录及所有子目录加入MATLAB搜索路径。这是MATLAB工程中最常见的路径管理问题,养成好习惯即可避免。还有一个额外推算:Diff1.m中可能引用了某些建模结果或外部数据,如果缺少对应数据文件也会报错,这时需要确认解压后的源码包文件是否齐全。

4.5 坑五:仿真结果中USV的跟踪轨迹呈锯齿状

现象:位置曲线看起来整体正确,但细节有锯齿状抖动,轨迹不平滑。

原因:仿真步长dt设置过大。当dt = 0.5秒时,LOS控制器对误差的更新频率太低,相当于“瞄一眼走一大步”,航向修正严重滞后。此外,如果PID控制器中有微分项,大仿真步长还会放大微分噪声。

解决:将仿真步长从0.5减小到0.1或更小。在你本机性能允许的前提下,尽量让dt不超过系统最小时间常数的十分之一。除了步长外,检查模型中是否有quantization或离散化处理,这类模块也会导致轨迹不平滑。另外,推荐在仿真结束后用Diff3.m做离线数据平滑,再绘制轨迹图。

5. 验证与进阶:从仿真曲线到自适应LOS的拓展思路

仿真跑通只是第一步,怎么确认这套LOS路径跟踪实现是“对的”,需要一套标准的验证流程。我自己的习惯是三个指标一起看:第一,跟踪误差曲线——即当前点到期望路径的最短距离e(t),它应该单调收敛到一个稳定小值(通常在船长的10%以内);第二,艏向角曲线——它应该平滑变化,没有剧烈跳变;第三,控制量曲线——tau_u和tau_r的幅值应在限幅范围内,且没有高频振荡。如果三条曲线都符合预期,这套存在你手上的基线实现就可以信赖了。验证方法可以写成一个脚本validate_results.m,流程是读取logsout或Scope导出的数据,计算横向跟踪误差e(t)并画图。

进阶方向我推荐优先尝试自适应LOS(Adaptive LOS)。在基础LOS中,lookahead_dist是固定值,但实际海况下USV的航速会受风浪影响,固定前视距离会导致跟踪性能波动。自适应LOS的核心思想是把前视距离变成航速的函数:

lookahead_dist(t) = alpha * u(t)

其中alpha是时间常数,u(t)是当前纵荡速度。当船速快时前视距离自动变大,路径跟踪更平滑;船速慢时前视距离变小,跟踪精度更高。另一种进阶做法是引入侧滑角补偿——在psi_ref上修正一个由横流引起的侧滑角beta_ss,公式为psi_ref_compensated = psi_ref - beta_ss。

当时我拿到这套工程后的第一件事,就是照着三个Diff脚本把表格里的核心参数画在同一张图上做对比,理解“前视距离增大 → 超调减小但稳态误差增大”这个权衡。从那以后我每次接手一套USV仿真代码,都会强制走一遍这个流程:先跑通基线,再做参数扫描对比,最后加大前视距离试试极限工况。这套方法论是拆解这份源码给我最大的收获。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询