news 2026/9/14 5:30:45

TRACLUS轨迹聚类算法解析:MDL分段与DBSCAN聚类的MATLAB实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TRACLUS轨迹聚类算法解析:MDL分段与DBSCAN聚类的MATLAB实现

简介:面向轨迹数据在线分类与聚类需求的TRACLUS-master源码包,基于密度聚类思想完整实现了TRACLUS算法,支持在线输入位置点、轨迹分段、距离度量与聚类结果可视化,适合地理信息、交通分析及数据挖掘领域的研究者、工程师及高校学生使用。资源压缩包共38个文件,以26个MATLAB脚本(.m)为核心,覆盖数据预处理、算法主流程、绘图展示等模块;另含8个.mat数据文件(模拟轨迹与实测数据)、3个.asv自动备份文件及1个README说明文件,整体大小400KB,小巧清晰。已有506人学习下载。借助源码包,读者可深入理解轨迹聚类的核心步骤,直接复用或改造代码,也可结合附带数据快速验证,适用于人群流动规律挖掘、交通热点识别、物流路线优化等实际任务。

1. 轨迹聚类不是点聚类:TRACLUS 把整条轨迹当作聚类对象

手头有一批出租车 GPS 轨迹,每一条是时间顺序排列的坐标序列,想找出主干通勤流。用 DBSCAN 直接对坐标点聚类,得到的是“热点”,却看不出“从哪个方向来、往哪个方向去”的整体运动模式。原因在于点聚类丢失了轨迹的序列信息。TRACLUS 在这一步做了一个关键转换:先把轨迹切成分段,再把段当成最小单元做基于密度的聚类,最后用每个簇里的段合成一条代表轨迹。这个 MATLAB 源码包把整套流程落成了模块——traclus.m 是主流程,segment.m 负责分段,cal_LH.m 和 cal_LDH.m 算 MDL 成本,m_plot_seg.m、m_plot_clus.m 负责可视化。适合要快速验证轨迹聚类效果、跑对比实验,或者把算法移植到自己数据集上的工程师和数据研究人员。

2. 两阶段框架与 MDL 分段:为什么先切段而不是直接聚类整条轨迹

2.1 整条轨迹做相似度计算的三个问题

直接用整条轨迹做聚类,先碰到的就是长度不一致。一条从望京到中关村的轨迹有 120 个 GPS 点,另一条绕五环的轨迹有 400 个点,算相似度时,长轨迹里的大量点会把短轨迹“淹没”。第二个问题是起点终点漂移,同一条路上反向行驶的两条轨迹,空间上重叠但运动方向相反,若只算几何距离,会把它们归到一起。第三个问题是中间绕路,车辆等红灯、送乘客拐进小区,这些局部偏移在整条轨迹尺度上会变成噪声,却会让相似度计算失真。

TRACLUS 的两阶段框架用“分区-聚合”绕开了这些问题。第一阶段用 MDL(最小描述长度)原则把所有轨迹切成轨迹段;第二阶段把这些段当作带方向的线段,用基于密度的聚类算法聚合,最后从每个簇中生成代表轨迹。切段是整套方法的地基,本小节先讲清楚分段这一层的原理。

2.2 MDL 分段的成本构成:cal_LH.m 与 cal_LDH.m

MDL 的思想是:在给定数据的前提下,一组假设如果能用最短的编码长度解释数据,就是好的假设。放在轨迹分段里,假设是“用若干条直线段表示这条轨迹”,编码长度分两部分:

  • L(H):描述这些段本身需要的成本。段数少、总长度短,成本就低。
  • L(D|H):用这些段代替原始轨迹后产生的误差。段数少、误差大,成本就高。

两个成本互相拉扯,最小化 L(H) + L(D|H) 的那个分段点集合,就是 MDL 选出的最优分段。

源码里 cal_LH.m 在计算这个组合成本,cal_LDH.m 在其基础上多了方向差成本,两者的输入输出都是分段候选集。需要说明的是,纯 MDL 分段一般不跨轨迹,只对单条轨迹内部做决策,所以它并不考虑相邻轨迹之间的空间关系,这是它和后面聚类阶段的职责边界。

分段点选择的最小化过程

MDL 选择是贪心扫过的。对一条轨迹,从第 1 个点开始,不断尝试把当前段延长到更远的点;每次延长都算一次 LH 或 LDH 成本,当延长带来的误差成本增量大于节省的段描述成本时,就在当前点切一刀。常见做法是枚举所有可能的切分组合再选最小,但那是 O(n²),在 GPS 高频采样下会卡住,所以 TRACLUS 源码里配套了 gen_segment_set.m 来生成候选段集合,把搜索范围限制在有意义的分段点上。

核心代码长的样子(分段循环的核心判断):

% 输入: traj 是 N x 2 的坐标序列, cal_fun 指向 cal_LH 或 cal_LDH % 输出: seg_idx 记录每个分段起止点的下标 seg_idx = []; start = 1; k = 2; while k <= size(traj, 1) cur_cost = cal_fun(traj(start:k, :)); nxt_cost = cal_fun(traj(start:k+1, :)); if nxt_cost > cur_cost * (1 + alpha) % alpha 是松弛系数 seg_idx = [seg_idx; start, k]; start = k; end k = k + 1; end seg_idx = [seg_idx; start, size(traj, 1)];

这段代码的作用是逐个点尝试把当前段向后延伸,若延伸后的 MDL 成本超过当前段成本的一定比例,就认定再延下去误差收益不划算,在 k 处落刀。alpha 用来给“误差容忍”留余量,源码包不显式暴露它,但实际复现时取 0.1~0.3 效果更贴近原论文。参数说明:traj 必须是按时间顺序排列的坐标矩阵,seg_idx 每行是一个分段的起止下标,后续送入 traclus.m 时直接用它索引原始点即可。需要注意如果采样率不固定,应先按时间戳重采样,否则 k 和 k+1 之间的物理距离不均匀,判断会失真。

2.3 包内分段相关文件的功能分工

文件名作用输入输出
segment.m单条轨迹分段的入口原始轨迹矩阵分段起止索引
gen_segment_set.m生成候选分段集合轨迹点集候选段的起止对
cal_LH.m / cal_LDH.m计算 MDL 分段成本一段坐标成本标量
struct_traj_segs_set.mat分段结果的数据结构样例保存分段集合

这里面 struct_traj_segs_set.mat 是已经跑过的分段结果,想对比算法参数影响时,先用它作为 baseline 再跑自己的数据,比直接对原始数据调试更快,原因在于它隔离了分段环节的变量,只暴露聚类环节的调试空间。

3. 距离度量与 DBSCAN 变体:traclus.m 里 eps 与 minLns 怎么设

3.1 轨迹段之间的距离到底怎么算

分段产出的是一堆有向线段。聚类时判断两个段是否靠近,TRACLUS 用的不是欧氏距离,而是三个分量的组合:垂直距离 d_perp、水平距离 d_paral、角度距离 d_angle。垂直距离衡量一条段的端点到另一条段的垂直投影偏移,水平距离衡量两条段在彼此方向上的重叠长度差,角度距离是两者方向的夹角差。

源码里 mbr_distance.m 用最小包围矩形来加速这两个几何量的计算,th_distance.m 处理的是阈值化距离,min_st_distance.m 在计算段起点到参照点的最小距离,这些工具函数最后都汇聚到 traclus.m 的邻域查询里。需要指出的是,TRACLUS 原文里距离公式对两条段是对称的,但包装成代码时如果直接对坐标反复调 atan2 和投影,会在段数量大时变成性能瓶颈,所以包里的实现大多先求段的几何参数(起点、终点、方向角、长度),再套距离公式。

3.2 traclus.m 的主流程与核心参数

traclus.m 是把分段集合变成簇的枢纽,流程上就是 DBSCAN 的轨迹版:先对每个段找 eps 邻域内的邻居段,再根据邻域大小判断它是不是核心段,最后从核心段出发做密度可达的扩展。

% 输入: seg_list 为分段结构体数组, 每个元素有 start_point, end_point, angle % 参数: eps, minLns 由外部传入 clusters = {}; visited = false(length(seg_list), 1); for i = 1:length(seg_list) if visited(i), continue, end visited(i) = true; neighbors = find_neighbors(seg_list, i, eps); % 调 mbr_distance 加速 if length(neighbors) < minLns continue; % 噪声段, 不参与扩展 end % 扩展簇 cluster = expand_cluster(seg_list, i, neighbors, eps, minLns); clusters{end+1} = cluster; end

逻辑说明:find_neighbors 查询时工程质量差别最大的是“要不要先画格网”。段数量过万时,最朴素的两两计算是 O(n²),即使有 mbr_distance 的包围盒裁剪也撑不住。可以参照 set2map.m 的做法,把段的包围盒映射到二维网格,只在目标段所在网格及相邻网格里找邻居,这能把邻域查询从全表扫描降到局部扫描。参数说明:eps 的单位和坐标一致,lat/lon 坐标下应先把经纬度投影到平面坐标系,否则距离单位不统一,eps 会失效;minLns 默认取 2~5,代表至少要有这么多条段才能构成一个簇,调大它会让小簇消失,调小则噪声段也会被拉进簇里。

3.3 参数标定与可视化迭代

参数不能靠猜,图也不能靠肉眼硬看。常规做法是先跑一遍默认参数,把分段结果画出来,测量一下段长的中位数,再以这个中位数的 0.2~0.5 倍作为 eps 初值,minLns 取 3,然后观察 m_plot_clus.m 输出的簇数变化。簇数骤降,说明 eps 过大,把相邻子区域都粘连了;簇太小太多,则是 eps 或 minLns 过小。调参时用同一份数据多试几次,直到 m_plot_clus.m 里大部分簇的段数落在 20~200 的区间,分布比较稳定,就可以把参数固化成经验值。

下表是调试时常用的参数对照:

参数含义影响面板建议初值
eps邻居段距离阈值过大→簇粘连;过小→簇碎片化段长中位数 x 0.3
minLns成为核心段的最小邻居数过大→小簇消失;过小→噪声入簇3
alpha分段延长松弛系数过大→分段粗,方向信息糙0.15
坐标投影距离计算基于投影坐标不统一→eps 失真UTM 或 Web Mercator

最后那行坐标投影不是 TRACLUS 源码的参数,但它是实际跑数据最容易翻车的地方。原始 GPS 纬度一度对应的地面距离和经度不同,直接拿经纬度算 eps,高纬度区域会在经度方向被“拉伸”,聚类形状会畸变,所以必须在加载数据后先做投影变换再进入算法。

4. 在线分类与数据接入:load_raw_traj.m 与 RanTraj.m 的批处理方式

4.1 包内置数据与加载函数对照

这个数据集里既有真实形态的轨迹,也有仿真数据生成器。看数据文件名就能判断:TRACK.mat、ASTS.mat、struct_traj.mat 是原始轨迹对象;struct_traj_segs.mat、ASTS_SEG.mat 是分段结果;struct_traj_segs_map.mat、ASTS_SEG_MAP.mat 是构建了空间索引后的分段。配套的加载函数把不同格式统一成 struct 数组,后面接 traclus.m 就不需要关心数据从哪来。

数据文件加载函数内容说明典型用途
TRACK.matload_raw_traj.m原始轨迹,每行一条跑分段与聚类全流程
ASTS.matload_asts_data.mASTS 格式轨迹对比不同数据源的兼容性
struct_traj.matload_raw_traj.m结构体封装的轨迹调 get_segs.m 提取分段
ASTS_SEG.matload_asts_data.m + segment.m分段缓存跳过分段直接调参
struct_traj_segs_map.matload_raw_traj.m + gen_segment_map.m带空间索引的分段大数据量邻域查询加速

在线分类的需求常见于轨迹流式进入的场景,比如实时交通监控。TRACLUS 原版是批处理算法,把“在线”落到这套代码里时,围绕的是 get_new_data.m 与 set2map.m 的组合:新轨迹到达后,先用 segment.m 切段,再与已有分段集合合并,重新执行 traclus.m 的聚类,只对新增段影响的局部区域做重算,这样能在不全局重跑的前提下完成分类。

4.2 get_new_data.m 的工程化封装

get_new_data.m 承担数据接入的职责,本质是一个带缓冲的读取器,每次调用返回一段新轨迹。用它做在线模拟,核心是维护一个“当前已见轨迹集合”的状态,让聚类器可以增量消费数据。下面的示例展示这种状态如何组织:

% 假设全局状态存储在 appdata 中 % online_state 包含已经处理过的分段与当前索引 state = getappdata(0, 'traclus_online_state'); if isempty(state) state = struct('all_segs', [], 'next_idx', 1); end [raw, next_idx] = get_new_data(state.next_idx); state.next_idx = next_idx; % 对新增轨迹做分段 new_segs = segment(raw); state.all_segs = [state.all_segs; new_segs]; setappdata(0, 'traclus_online_state', state);

这段代码把 get_new_data.m 的返回值接到分段器上,并把分段结果追加到全量集合。next_idx 的作用是记录读取游标,保证每条轨迹只处理一次;setappdata 在 MATLAB 的 GUI 环境下可以跨函数共享状态,命令行环境下换成持久变量或返回结构体同样可行。理解这一点对二次开发很重要:在线分类的本质是分段结果随数据到达而累积,聚类过程本身并不需要重跑全部历史,只需针对受影响区域增量展开。

4.3 仿真数据生成与随机轨迹的边界

想在没有真实轨迹的情况下验证算法,或者是测试算法对噪声的鲁棒性,可以用 RanTraj.m 和 gen_simu_raw_data.m 生成模拟数据。这类生成器的关键参数是段数、噪声点比例、轨迹的随机游走步长。生成数据时需要注意:模拟轨迹的方向分布应尽量贴合真实场景,否则调出来的参数在真实数据上会失效。比如交通流轨迹在早晚高峰有明显的方向集聚特征,随机游走数据则各向同性,后者调参的结果拿到前者数据上,eps 大概率需要成倍调整。

生成器输出的轨迹在喂给 load_raw_traj.m 之前,最好检查时间戳列是否存在,TRACLUS 分段只依赖坐标序列的顺序,过密的采样会拉长 MDL 分段处理时间,建议按固定时间间隔重采样,比如 5 秒一个点,既保留方向变化信息,又降低后续距离计算的开销。数据量级大时,把重采样放在生成器内部做,比在加载后做,能省去不少不必要的内存拷贝。

5. 用 m_plot 系列函数校准聚类,并从代表轨迹反推参数

5.1 三层绘图函数的调用关系

调试 TRACLUS 时,绘图函数价值最高的不是“画得好看”,而是“画完能把问题暴露出来”。m_plot_seg.m 画分段,检查分段点是否切在转弯处;m_plot_clus.m 画聚类,每个簇用不同颜色,观察簇的空间聚集形态;m_plot_rep_traj.m 画代表轨迹,它把簇内所有段的几何平均体现在一条粗线上,用来看簇的中心趋势是否合理。

% 绘制分段结果与聚类结果 figure(1); m_plot_seg(seg_idx, traj, 'color', 'k', 'linewidth', 1); figure(2); m_plot_clus(clusters, 'colormap', 'lines', 'show_noise', true); figure(3); m_plot_rep_traj(clusters, rep_traj, 'color', 'r', 'linewidth', 2);

第一行把分段点之间用线段连起来,能直观看到切点是否在方向突变处。如果切点在直道路段上密密麻麻,说明 alpha 太大,分段偏碎;如果转弯处没有被切开,说明 alpha 太小,分段跨过了方向转折。第二行聚类图里噪声段用灰点显示,簇用不同颜色,如果噪声占比超过全部段的 30%,先调 eps 和 minLns,而不是先改分段逻辑。第三行代表轨迹是在簇内段集合上做几何平均得到的,坐标平滑但方向可能略钝,属正常现象。

5.2 从图返回到参数的一个完整判断路径

以一段车辆轨迹为例。m_plot_seg.m 显示分段线在环形交叉口处出现了两条互相交叉的段,说明该区域分段没有在合适的点切开,可以尝试把 alpha 从 0.15 降到 0.10,或者把轨迹先按角度变化率重采样,让转弯处的点密度更高。m_plot_clus.m 显示四条颜色簇,但其中一条横跨了垂直方向和水平方向的长直线,这条簇的段方向角跨度超过 90°,说明 eps 偏大,把两条方向不同的道路段拉进同一个簇。此时将 eps 从段长中位数的 0.4 倍降到 0.25 倍,重新聚类,代表轨迹会明显分开,簇内方向角方差也会下降。这套判断在调试新数据集时大约占整个调参时间的一半,先把图画出来再动参数,比直接改数值要有依据得多。

5.3 代表轨迹的平滑度作为调参信号

代表轨迹是每条簇内所有段的加权平均。如果代表轨迹出现锯齿状折线,而原始轨迹本身是光滑的,说明簇内的段在方向上有冲突,通常是由 eps 过大引起的;如果代表轨迹过短,只覆盖了簇的一小段空间范围,说明 minLns 设得太高,把本应连成一体的轨迹段拆散到了不同簇。可以结合簇内的段长分布一起看,段长中位数偏低,也表明分段阶段偏碎。实际复现时,我会每一步都用 figure 和 hold on 把不同聚类参数的轮廓叠在同一张图上,用透明度比较各参数下的稳定性,再锁定参数范围,这样能避免重复绘图带来的低效,也能在汇报结果时直接展示参数敏感性。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 5:29:57

弱电基础知识:强电分界、常见子系统与施工避坑指南

干弱电这行&#xff0c;最怕的就是一上来就钻到某个设备或系统里&#xff0c;结果干了两三年&#xff0c;连自己做的工程在整个弱电系统里处在什么位置都说不清楚。我见过不少刚入行的兄弟&#xff0c;一听说“弱电”两个字&#xff0c;第一反应就是“网线和监控嘛”。这么说没…

作者头像 李华
网站建设 2026/9/14 5:29:51

iOS App不重启实时切换中英文的原生实现

简介&#xff1a;本资源是一份面向iOS开发者的实战型本地化方案包&#xff0c;聚焦应用内动态切换中英文语言的完整实现&#xff0c;适用于需要支持多语言适配的App项目及进阶学习者。核心包含自定义LanguageManager工具类源码与根控制器销毁重建机制的技术落地代码&#xff0c…

作者头像 李华
网站建设 2026/9/14 5:29:46

STM32F103实现NEC红外遥控学习与发送的完整方案

简介&#xff1a;采用STM32F103实现红外遥控信号学习与发送的完整Keil工程资源包&#xff0c;适合嵌入式入门及红外通信开发者参考。压缩包内共95个文件&#xff0c;以39个C源文件、43个头文件和启动汇编文件为主&#xff0c;同时包含Keil工程配置与调试输出文件&#xff0c;整…

作者头像 李华
网站建设 2026/9/14 5:29:28

基于Vue和JavaScript的AJ-report大屏驾驶舱设计源码解析

简介&#xff1a;这是基于Vue和JavaScript开发的AJ-report大屏驾驶舱设计源码。AJ-report是一套专注于报表设计与大屏展示的开源框架&#xff0c;面向需要建设数据可视化大屏的前端与全栈开发者&#xff0c;可用于业务报表、运营监控、指挥中心等场景&#xff0c;适合有一定Vue…

作者头像 李华
网站建设 2026/9/14 5:25:44

金融行业Agent落地:权限治理、数据隔离与审计的工程实践

Agent在金融行业里喊了好几年&#xff0c;真正敢在生产环境跑起来的并不多。不是不愿意&#xff0c;而是不敢。金融机构面对的不只是"AI能不能完成任务"&#xff0c;而是"AI出错了谁负责、数据去了哪里、权限有没有失控"这一连串相当现实的问题。WorkBuddy…

作者头像 李华