ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

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.mMDL 的思想是在给定数据的前提下一组假设如果能用最短的编码长度解释数据就是好的假设。放在轨迹分段里假设是“用若干条直线段表示这条轨迹”编码长度分两部分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:k1, :)); 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.10.3 效果更贴近原论文。参数说明traj 必须是按时间顺序排列的坐标矩阵seg_idx 每行是一个分段的起止下标后续送入 traclus.m 时直接用它索引原始点即可。需要注意如果采样率不固定应先按时间戳重采样否则 k 和 k1 之间的物理距离不均匀判断会失真。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{end1} cluster; end逻辑说明find_neighbors 查询时工程质量差别最大的是“要不要先画格网”。段数量过万时最朴素的两两计算是 O(n²)即使有 mbr_distance 的包围盒裁剪也撑不住。可以参照 set2map.m 的做法把段的包围盒映射到二维网格只在目标段所在网格及相邻网格里找邻居这能把邻域查询从全表扫描降到局部扫描。参数说明eps 的单位和坐标一致lat/lon 坐标下应先把经纬度投影到平面坐标系否则距离单位不统一eps 会失效minLns 默认取 25代表至少要有这么多条段才能构成一个簇调大它会让小簇消失调小则噪声段也会被拉进簇里。3.3 参数标定与可视化迭代参数不能靠猜图也不能靠肉眼硬看。常规做法是先跑一遍默认参数把分段结果画出来测量一下段长的中位数再以这个中位数的 0.20.5 倍作为 eps 初值minLns 取 3然后观察 m_plot_clus.m 输出的簇数变化。簇数骤降说明 eps 过大把相邻子区域都粘连了簇太小太多则是 eps 或 minLns 过小。调参时用同一份数据多试几次直到 m_plot_clus.m 里大部分簇的段数落在 20200 的区间分布比较稳定就可以把参数固化成经验值。下表是调试时常用的参数对照参数含义影响面板建议初值eps邻居段距离阈值过大→簇粘连过小→簇碎片化段长中位数 x 0.3minLns成为核心段的最小邻居数过大→小簇消失过小→噪声入簇3alpha分段延长松弛系数过大→分段粗方向信息糙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 把不同聚类参数的轮廓叠在同一张图上用透明度比较各参数下的稳定性再锁定参数范围这样能避免重复绘图带来的低效也能在汇报结果时直接展示参数敏感性。本文还有配套的精品资源点击获取
返回列表