
简介本资源面向船舶航迹研究与Matlab聚类算法学习者复现论文《基于轨迹聚类的船舶异常行为识别研究》的核心流程解决航迹数据提取、聚类与偏离预测的建模问题。包内共20个文件以14个m脚本为主体辅以mat数据、zip子包及png图示整体约4.32MB涵盖DBSCAN聚类、改进Hausdorff距离计算、航迹提取、阈值分类与寻优等模块可直接运行。已有766人学习下载。读者可借此掌握DBSCAN距离度量与H距离计算方法理解航迹分类的完整流程并基于聚类中心开展航迹偏离预测经检测准确率较高。船舶航迹方向的研究者可替换数据与相关模块将其拓展为自有模型适合作为聚类算法的应用案例学习。1. 从一次航迹交叉误判说起这套 MATLAB 聚类方案到底在解决什么去年帮一个做港口监控的朋友看数据他手里有几百条 AIS 航迹想按航行模式自动分组。他先用的是最朴素的欧氏距离加 DBSCAN结果两条走向完全一致、只是横向错开几百米的航道被硬生生拆成了两类而两条在交叉口短暂重叠、实际航向差 90 度的船反倒被塞进同一簇。问题不在 DBSCAN在距离度量。欧氏距离只看点位远近不看方向航迹这种带时序和走向的数据用它就是拿错尺子量东西。这套「MATLAB 基于改进 Hausdorff 距离的 DBSCAN 船舶航迹聚类」资源核心就干一件事把航迹之间的相似度度量从「点对点距离」换成「考虑方向与形状的改进 Hausdorff 距离」再喂给 DBSCAN 做密度聚类。它适合三类人手里有 AIS 或雷达航迹数据、想做航行模式挖掘的工程人员用 MATLAB 做交通流或海事方向课题、需要一套能跑通并改得动的研究者以及被欧氏距离坑过、想搞清楚航迹相似度到底该怎么算的人。整套东西是 MATLAB 脚本加函数的形式不依赖深度学习工具箱普通配置的机器就能跑改参数、换数据都直接。2. 改进 Hausdorff 距离为什么不能直接用经典版本2.1 经典 Hausdorff 的致命短板经典 Hausdorff 距离的定义是集合 A 中每个点到集合 B 的最近距离取最大值反过来 B 到 A 也取最大值两者再取大。写成公式就是 H(A,B) max( sup_{a∈A} inf_{b∈B} d(a,b), sup_{b∈B} inf_{a∈A} d(a,b) )。它对「最不像的那一对点」极其敏感——只要有一条航迹里有一个离群点整个距离就被这个点绑架。航迹数据里 GPS 漂移、个别跳点太常见了直接套经典 Hausdorff聚类结果会被几个噪声点带偏。更麻烦的是经典 Hausdorff 只关心「点集之间的几何接近程度」完全不看航迹的走向。两条平行但反向的航迹点集可能挨得很近Hausdorff 距离很小可它们在实际语义上是完全相反的航行模式。这就是我朋友那个案例里交叉口误判的根源。2.2 改进思路方向加权 分位数截断这套资源里的改进主要落在两个地方我拆开讲。第一是方向加权。对每条航迹的每个点除了坐标还算一个局部航向角用相邻点差分得到。在计算点对距离时不只用欧氏距离而是把航向差也纳入d_improved d_euclid λ · (1 - cos(Δθ))。λ 是方向权重系数Δθ 是两点航向角之差。当两条航迹走向一致时 cos(Δθ)≈1惩罚项趋近 0走向相反时 cos(Δθ)≈-1惩罚项拉满。这样反向航迹的距离被显著放大不会再被误聚。第二是分位数截断。把 sup取最大换成取距离集合的某个高分位数比如 90% 分位。这样单个离群点最多影响那 10% 的尾部不会主导整个距离值。分位数参数 q 一般取 0.850.95q 越大越接近经典 Hausdorff、越敏感q 越小越鲁棒但可能过度平滑。2.3 距离矩阵的 MATLAB 实现下面这段是距离计算的核心函数我按资源里的结构重写并加了注释可以直接放进你的工程。function D improvedHausdorff(trajA, trajB, lambda, q) % trajA, trajB: N×3 矩阵列为 [x, y, heading(弧度)] % lambda: 方向权重系数建议 0.3~0.8 % q: 分位数建议 0.85~0.95 % D: 改进 Hausdorff 距离标量 % 计算 A 中每个点到 B 中每个点的改进距离 dAB pairDist(trajA, trajB, lambda); dBA pairDist(trajB, trajA, lambda); % 每个点到对方集合的最近距离 hAB min(dAB, [], 2); % N_A × 1 hBA min(dBA, [], 2); % N_B × 1 % 分位数截断代替 max D max(quantile(hAB, q), quantile(hBA, q)); end function d pairDist(A, B, lambda) % A: M×3, B: K×3返回 M×K 改进距离矩阵 M size(A, 1); K size(B, 1); d zeros(M, K); for i 1:M dx A(i,1) - B(:,1); dy A(i,2) - B(:,2); euclid sqrt(dx.^2 dy.^2); dtheta A(i,3) - B(:,3); dirPenalty 1 - cos(dtheta); % 方向惩罚项 d(i,:) euclid lambda * dirPenalty; end end逻辑说明pairDist算出两个航迹点集之间的全连接改进距离矩阵欧氏距离负责位置接近度1 - cos(Δθ)负责方向一致性惩罚。improvedHausdorff里对每个点取到对方集合的最近距离再用quantile取分位数最后两个方向取大。参数上lambda调大方向权重更强适合航道方向差异明显的场景q调小抗噪更强但太小低于 0.8会让距离区分度下降。我一般先用 lambda0.5、q0.9 跑一版看聚类轮廓再微调。提示航向角要做归一化到 [-π, π]否则 cos 计算会因 2π 跳变出错。资源里在预处理阶段用wrapToPi处理过自己接数据时别忘了这一步。3. DBSCAN 参数整定eps 和 MinPts 到底怎么定3.1 为什么 DBSCAN 配改进距离更稳DBSCAN 靠密度可达性聚类不需要预先指定簇数还能把低密度区域标成噪声——这对航迹数据很合适因为真实航行模式的数量事先不知道而且总有零星异常航迹。但 DBSCAN 对 eps邻域半径极其敏感而 eps 的合理取值直接取决于距离度量的尺度。换成改进 Hausdorff 后距离分布和欧氏距离完全不同不能沿用老参数必须重新整定。3.2 k-距离曲线定 eps标准做法是画 k-距离曲线对每个点算它到第 k 近邻的距离排序后画出来拐点位置就是 eps 的参考值。k 一般取 MinPts。下面这段直接可用。% D: N×N 距离矩阵用 improvedHausdorff 两两算出来 N size(D, 1); MinPts 5; % 先设一个初值 k MinPts; sortedKDist zeros(N, 1); for i 1:N di D(i, :); di(i) inf; % 排除自身 di sort(di); sortedKDist(i) di(k); % 第 k 近邻距离 end sortedKDist sort(sortedKDist, descend); plot(sortedKDist); xlabel(样本序号按k-距离降序); ylabel([第 num2str(k) 近邻距离]); grid on; % 找拐点曲线从陡降转为平缓的位置逻辑说明di(i)inf排除自身距离排序后取第 k 个就是 k-距离。降序排列后曲线拐点曲率最大处对应的纵坐标就是 eps 建议值。实际操作时我会把拐点值上下浮动 20% 各跑一次看哪个簇数稳定、噪声比例合理一般噪声占比 5%15% 可接受。MinPts 的经验规则是 MinPts ≥ 维度1航迹特征维度不高取 48 都行数据量大就往上调。3.3 聚类主流程与参数落点% 假设 trajs 是 cell 数组每个元素是一条 N×3 航迹 n numel(trajs); D zeros(n, n); for i 1:n for j i1:n D(i,j) improvedHausdorff(trajs{i}, trajs{j}, 0.5, 0.9); D(j,i) D(i,j); end end epsVal 0.8; % 由 k-距离曲线拐点确定 MinPts 5; labels dbscan(D, epsVal, MinPts, Distance, precomputed); % labels: 0 表示噪声正整数为簇编号逻辑说明dbscan用precomputed模式直接吃距离矩阵避免它内部再算一遍欧氏距离。epsVal和MinPts就是上面整定出来的值。跑完看labels里 0 的比例和簇的数量如果噪声超过 20%说明 eps 偏小如果几乎所有点挤成一簇eps 偏大。这一步没有银弹就是反复试但有了 k-距离曲线打底通常三五次就能收敛。注意距离矩阵是对称的循环里只算上三角再镜像赋值能省一半计算量。航迹条数上百以后这个优化很实在。4. 避坑与排查我踩过的五个真实问题4.1 现象聚类结果每次跑都不一样原因DBSCAN 本身是确定性的但如果你的航迹点顺序在预处理时被打乱或者用了随机采样做加速距离矩阵就会变。另一个隐蔽原因是航向角计算依赖点顺序顺序一变航向就变。解决预处理阶段固定航迹点顺序按时间戳排序后再算航向。如果做了降采样用等间隔抽取而不是随机抽取。跑之前把随机种子固定rng(42)虽然 DBSCAN 不用随机数但上游如果有任何随机环节都能锁住。4.2 现象两条明显不同的航迹距离却很小原因方向权重 lambda 设得太小或者航向角没做归一化导致 cos 惩罚项失效。还有一种情况是两条航迹长度差异极大短航迹的点全被长航迹的某个局部区域「吸」住了。解决先把 lambda 提到 0.5 以上试检查航向角是否用wrapToPi归一化对长度差异大的航迹考虑先做重采样统一到相近点数或者在距离里加长度惩罚项。资源里对长度差异有处理但如果你换数据这点要自己盯。4.3 现象eps 稍微一动簇数从 3 跳到 15原因距离分布本身没有明显的密度分层说明改进距离的区分度不够或者数据里噪声太多把密度结构糊掉了。解决先回去调距离参数把 lambda 和 q 组合扫一遍看距离分布的直方图有没有双峰或明显间隙。如果数据噪声确实大先做一道航迹清洗去掉点数过少、速度异常的航迹再聚类。别硬调 eps那是治标。4.4 现象计算距离矩阵慢到无法接受原因双重循环加逐点对计算复杂度是 O(n² · L²)n 是航迹数L 是平均点数。n 上百、L 上千时MATLAB 纯循环扛不住。解决把pairDist里的内层循环向量化上面代码已经用矩阵运算替代了逐点循环对航迹做等间隔降采样把 L 压到 100200如果 n 超过 500考虑先用欧氏距离粗筛只对可能邻近的航迹算改进 Hausdorff。我一般会在距离计算前加一个进度打印不然跑着跑着不知道卡在哪。4.5 现象噪声点里混着真实的小众航行模式原因DBSCAN 把低密度区域一律标噪声但有些真实模式样本就是少密度天然低。解决调小 MinPts 让密度门槛降低或者对标记为噪声的点做二次聚类用更小的 eps 再跑一遍 DBSCAN。另一个思路是聚类后对噪声点做人工抽检确认是不是真的异常。资源里默认参数偏向保守小众模式容易被吞这点在做实际业务时要留意。5. 进阶技巧用轮廓系数和可视化把结果验到位聚类跑完不算完得验证。我习惯用两个手段轮廓系数和航迹叠加图。轮廓系数衡量每个点跟自己簇的紧密度 vs 跟最近邻簇的分离度取值 [-1, 1]越接近 1 越好。但注意轮廓系数默认用欧氏距离算对改进 Hausdorff 聚类的评估只能做参考不能当唯一标准。我的做法是用它做相对比较同一份数据不同参数组合下谁的轮廓系数高谁就更可能合理。% labels 来自 dbscanD 是改进 Hausdorff 距离矩阵 sil silhouette((1:numel(labels)), labels, Distance, precomputed); % 注意silhouette 的 precomputed 模式需要传入方阵距离 meanSil mean(sil(~isnan(sil))); fprintf(平均轮廓系数: %.3f\n, meanSil);逻辑说明silhouette在precomputed模式下直接吃距离矩阵但要求距离矩阵是方阵且对角线为 0。如果你的 D 对角线不是 0先D(1:n1:end) 0。meanSil排除 NaN噪声点标签为 0 时可能产生 NaN。这个值我一般拿来横向比参数不追求绝对值多高。可视化更直观。把每条航迹按簇编号上色画在同一张经纬度图上噪声点用灰色。一眼就能看出聚类是不是把走向一致的航迹归到了一起、交叉口有没有误判。figure; hold on; colors lines(max(labels)); for i 1:numel(trajs) if labels(i) 0 plot(trajs{i}(:,1), trajs{i}(:,2), Color, [0.7 0.7 0.7]); else plot(trajs{i}(:,1), trajs{i}(:,2), Color, colors(labels(i),:)); end end xlabel(经度); ylabel(纬度); title(航迹聚类结果); grid on;参数上lines(n)生成 n 种区分度较好的颜色噪声统一灰色。如果簇数超过 7颜色会开始接近可以换turbo或parula色图。画完重点看三处簇内航迹走向是否一致、簇间是否有明显方向差异、噪声点是不是真的零散异常。一个我常用的调参习惯固定 MinPts5把 eps 从 k-距离拐点的 0.8 倍扫到 1.2 倍步长 0.05每个值记录簇数、噪声比例、平均轮廓系数列成表挑最优。这套流程走下来基本不会出现「跑出来不知道对不对」的情况。从那以后我每次拿到新航迹数据都强制先跑一遍 k-距离曲线和参数扫描再动手解释结果——跳过这步的代价就是被错误聚类带偏结论。希望这套东西能帮到你。本文还有配套的精品资源点击获取