
简介面向NGSIM数据集车辆跟驰研究的Python数据处理工具主要针对交通流微观分析场景帮助研究者快速完成跟驰车辆数据的初步筛选与字段整合。程序能够依据连续时间戳提取目标车辆及其对应前车的相关信息输出整理后的数据表便于后续利用pandas、csv等工具进行深入分析同时简介中特别说明该结果仅为初步筛选未做平滑和去噪处理使用者需结合科研需求进一步清洗。整体以zip压缩包形式提供体积仅36KB轻量易用适合熟悉Python的交通数据研究人员、相关课题学生直接获取。该程序已在CSDN平台被浏览学习5123次对于需要从NGSIM原始数据中提取跟驰片段、构建格式化训练集或开展数据预处理的用户具有实用参考价值。1. NGSIM跟驰车辆数据处理程序到底在解决什么拿到 NGSIM 轨迹数据的第一天大部分人会先被它的原始结构搞懵一张大表里躺着几十万行记录每一行代表某一帧里某辆车的位置、速度、加速度和车道编号时间分辨率是 0.1 秒。看起来信息很全可真要研究跟驰行为时你会发现原始表里根本没有「这辆车在跟哪辆车」这一列。跟驰车辆数据处理程序的核心任务就是把这张扁平的大表重构成「前车-后车」配对结构从中切出连续的跟驰片段再输出可供标定智能驾驶模型或交通流模型直接使用的干净数据。NGSIM 之所以值得花力气做这套处理是因为它是目前公开可得的、覆盖面最广的高精度车辆轨迹数据之一来自美国多段高速公路的无人机和路边摄像机采集记录了完整换道、加减速和跟驰过程。所有与跟驰相关的研究——无论是标定 IDM、Gipps 模型还是做安全距离预警仿真——都绕不开这个把原始轨迹转换成跟驰样本的预处理阶段。它的坑也很集中坐标单位、车辆长度换算、前后车判定、噪声滤波、换道瞬间的干扰帧每一个处理不当都会让后续模型参数失真。这篇文章会按一个可复现的 NGSIM 跟驰车辆数据处理程序为主线从字段解读、配对逻辑、清洗策略讲到质量验证。适合正在做交通流仿真、车辆跟驰模型标定或者轨迹数据挖掘的工程师和研究者。读完你能够独立写出一套自己的 NGSIM 跟驰数据流水线而不是只会跑别人封装好的工具。2. NGSIM原始轨迹结构与跟驰车辆配对逻辑2.1 读懂 NGSIM 轨迹表里的关键字段NGSIM 轨迹数据最常见的形式是 CSV 文件每一行是一条轨迹点记录核心字段包括Vehicle_ID、Frame_ID、Global_Time、Local_X、Local_Y、v_Vel、v_Acc、Lane_ID、Preceding等。Preceding字段就是官方预处理里给出的前车 ID但这个字段并不总是可靠的——它在前车换道、后车换道、车辆驶入驶出检测区域时会缺失或跳变所以做跟驰研究不能直接拿它当最终结果只能作为参考和校验。需要特别注意 NGSIM 的时间和空间单位时间以 0.1 秒为一个采样帧速度单位是英尺/秒ft/s加速度单位是英尺/秒²ft/s²位置坐标是英尺ft。如果直接用这些单位去计算加速度变化率或者跟驰间距得到的结果数值会与公制习惯相差很大。常见的做法是读取之后先统一换算成米和米/秒避免在后续的阈值判断、模型标定中因为单位搞错而给出完全失真的参数。还有一个容易被忽略的字段是Vehicle_Length它记录的是每辆车的实际车长。NGSIM 的位置坐标Local_X、Local_Y通常指车辆前保险杠中心的位置所以后车前保险杠到前车后保险杠的实际间距不能直接拿坐标差去算必须减去前车的车长。换算公式是gap (lead_y - follow_y) - lead_length # 单位统一换算为米后计算其中lead_y和follow_y是纵向上前后车的前保险杠位置坐标lead_length是前车车长。这个式子在后文的配对和间距指标计算里会反复用到。2.2 跟驰配对的坐标基准与方向问题NGSIM 不同路段采集时坐标系定义并不完全一致。比如有的路段Local_X是沿道路方向Local_Y是垂直道路方向另一些路段则正好相反。处理程序里不能写死「用 Local_X 排序」或「用 Local_Y 排序」而是要先把纵向坐标识别出来每辆车在同一个车道内沿行驶方向移动时变化最大的那个坐标就是纵向坐标。我一般会在程序里加一个自动化的坐标方向探测逻辑取同一辆车若干连续帧的位置变化比较Local_X和Local_Y各自的变化幅度变化幅度大的一方即纵向坐标。这样可以避免不同路段数据切换时改代码。识别出纵向坐标之后跟驰配对的基准也就清楚了——同一时刻、同一车道内沿纵向坐标方向排在当前车辆前方且距离最近的车辆就是它的前车。配对时的方向陷阱要格外小心NGSIM 某些路段的数据是自南向北行驶某些是自东向西纵向坐标增大的方向不一定就是行驶方向。如果简单按坐标数值从小到大排序很可能把后方车辆当成前车。稳妥的做法是先以一辆车为参照看它下一帧的纵向坐标相对当前帧是增大还是减小从而确定行驶方向再决定前车是取坐标更大的那一辆还是更小的那一辆。2.3 从原始 CSV 读取到按帧分组的基线代码在进入配对算法之前先把数据读取和分组的基础代码写好。下面这段代码完成单位换算、纵向坐标识别和按帧分组三件事import pandas as pd import numpy as np df pd.read_csv(trajectories-0400-0415.csv) # NGSIM 原始单位换算为公制 df[x_m] df[Local_X] * 0.3048 df[y_m] df[Local_Y] * 0.3048 df[v_mps] df[v_Vel] * 0.3048 df[a_mps2] df[v_Acc] * 0.3048 df[len_m] df[Vehicle_Length] * 0.3048 # 识别纵向坐标比较单辆车连续帧的位移幅度 sample_v df[df[Vehicle_ID] df[Vehicle_ID].iloc[0]] dx sample_v[x_m].diff().abs().median() dy sample_v[y_m].diff().abs().median() longitudinal_col x_m if dx dy else y_m print(纵向坐标字段:, longitudinal_col) # 按帧分组给每个分组内的车辆按纵向坐标排序做准备 df df.sort_values([Frame_ID, Lane_ID, longitudinal_col])这段代码做了三件关键事第一把英尺单位统一换算成米和米/秒避免后续所有阈值在英制单位下失真第二用单辆车的连续帧中位位移来判断哪个坐标是纵向坐标这个判断每换一个数据集都要做一次第三按Frame_ID分组并且组内按车道和纵向坐标排序为接下来给每辆车找到它的前车铺平道路。这里不直接用Preceding字段的核心原因是它在换道和检测区域边界上的不稳定性。你自己算出来的前后车关系配合上车道一致性判断才能得到干净的跟驰配对结果。3. 跟驰车辆识别的判定条件与配对代码实现3.1 判定一辆车处于跟驰状态的三个必要条件跟驰不是简单的「前面有车」它要求后车的运动状态在时间和空间上都在响应前车的运动。行业内最常用的判定条件有三个缺一不可。第一车道一致性。前后车必须在同一Lane_ID上且这个车道一致要持续一段连续的时间。如果前车在换道过程中它横向位置变化导致与后车不再是同年道关系那这段时间的轨迹不能算跟驰。第二纵向相对位置。前车必须位于后车前方且纵向间距在一个合理范围内。间距太小说明后车在强制动甚至已经处于碰撞风险状态间距太大说明后车可能根本不受前车影响常见做法是设定一个最大跟驰间距阈值比如 120 米超过这个值就视作自由流状态。第三时间持续性。前后车配对必须连续保持至少 N 帧比如 50 帧5 秒。短暂碰巧前面有车的场景不能算作跟驰行为样本。这三个条件组合起来才构成后续模型标定和指标统计的数据基础。只满足前两个条件的数据里会混入大量前车刚换道进来或后车刚换道出去的瞬时样本这类样本的时间序列特征不稳定用于标定会造成反应时间参数严重失真。3.2 用分组移位法找到同一车道内的前车算法实现上最常见的做法不是在每个时间步做两两距离扫描那样复杂度太高。NGSIM 一帧内可能有几十辆车逐对计算会产生大量无用组合。更高效的做法是依赖上一步的排序结果因为数据已经按Frame_ID、Lane_ID、纵向坐标排好序所以同一帧、同一车道内排在一条记录下面一条的那个车辆 ID就是它的后车反过来说排在它上面一条的车辆 ID就是它的前车。用 pandas 的groupby加shift操作可以一次性完成全量配对代码如下df df.sort_values([Frame_ID, Lane_ID, longitudinal_col]) df[leader_id] ( df.groupby([Frame_ID, Lane_ID])[Vehicle_ID] .shift(-1) # 取同车道内纵向坐标更大的下一辆车作为前车 )shift(-1)的含义是在每个Frame_ID与Lane_ID组合的分组内部把下一行的Vehicle_ID赋值给当前行。由于分组内已经按纵向坐标排序下一行就是当前车辆的紧邻前车。一个分组内的最后一行会得到NaN表示这条车道内它前面没有车。这一段的性能非常好全部配对逻辑由 pandas 的 C 层分组操作完成处理 NGSIM 的一个 15 分钟片段大约 60 万行数据只需要一两秒。相比逐帧遍历循环的方式这种方法代码量少、不容易出索引错位也方便后续直接按列筛选跟驰样本。3.3 间距计算与跟驰片段的连续切分配对完成之后把前后车的位置信息合并到同一行然后计算跟驰间距和相对速度。NGSIM 的位置坐标是前保险杠中心所以间距必须扣除前车车长df df.merge( df[[Vehicle_ID, Frame_ID, longitudinal_col, v_mps, len_m]], left_on[leader_id, Frame_ID], right_on[Vehicle_ID, Frame_ID], howleft, suffixes(, _lead) ) df[gap] df[longitudinal_col _lead] - df[longitudinal_col] - df[len_m_lead] df[rel_v] df[v_mps_lead] - df[v_mps] # 排除无效配对 df df.dropna(subset[leader_id]) df[is_following] ( (df[Lane_ID] df[Lane_ID]) (df[gap] 0) (df[gap] 120) (df[leader_id] ! -1) )合并操作把前车的速度、位置、车长字段拼接到了每一辆后车的行上。间距gap的意义是实际车头间距即后车保险杠到前车保险杠的空隙距离。rel_v是相对速度正值代表前车比后车快间距在拉大。有了逐帧的is_following布尔标记后需要对连续帧做片段切分一段连续is_followingTrue的记录才构成一个跟驰片段。切分时用「标记从 True 变 False 或 ID 变化处断开」的方法df[pair_id] (df[Vehicle_ID].astype(str) _ df[leader_id].astype(str)) df[break_flag] ( (df[pair_id] ! df[pair_id].shift(1)) | (df[is_following] ! df[is_following].shift(1)) | (df[Frame_ID] ! df[Frame_ID].shift(1) 1) ) df[segment_id] df[break_flag].cumsum() df df[df[is_following]] segment_stats ( df.groupby([pair_id, segment_id]) .agg(frame_count(Frame_ID, count), duration_s(Frame_ID, count)) ) valid_segments segment_stats[segment_stats[frame_count] 50]break_flag在三种情况下置位车辆 ID 或前车 ID 变化、跟驰状态翻转、帧号不连续。三者的并集确保换道、数据缺失、状态切换都能断开片段。frame_count 50表示跟驰持续至少 5 秒这是筛选有效跟驰样本的常用底线。这个环节是整套程序里最容易被忽略的部分。很多直接拿Preceding字段做研究的人最后标定出来的跟驰模型反应时间偏小、期望间距偏大原因就是把大量不足 1 秒的瞬时跟驰片断混入了训练集。4. NGSIM预处理里的噪声清洗与换道帧摘除4.1 速度与加速度的抖动来源及滤波策略NGSIM 原始轨迹数据中的速度v_Vel和加速度v_Acc并非直接测量值而是从位置差分得到的估计值因此高频抖动非常明显。用原始加速度序列去标定跟驰模型你会发现计算出的期望加速度波动剧烈完全不像人类驾驶行为。常见的做法是对速度和加速度做平滑但平滑窗口太大会抹掉真实的加减速事件窗口太小又滤不干净噪声。我在处理 NGSIM 时首选 Savitzky-Golay 滤波它相比滑动平均的优点是能在平滑的同时保留数据的高阶变化趋势不会让急加速的尖峰被削平。窗口长度一般取 11 到 15 帧对应 1.1 到 1.5 秒多项式阶数取 3。这个组合在 NGSIM 轨迹上表现稳定既能去除大部分定位抖动又不会让跟驰中常见的 1 到 2 秒反应时间特征丢失。from scipy.signal import savgol_filter def smooth_trajectory(group): if len(group) 15: return group group[v_smooth] savgol_filter(group[v_mps], window_length15, polyorder3) group[a_smooth] savgol_filter(group[a_mps2], window_length15, polyorder3) return group df df.groupby(pair_id, group_keysFalse).apply(smooth_trajectory)滤波时要按pair_id分组保证不会把两段不同跟驰片段的数据混在一起平滑。窗口长度设为 15 在 10Hz 采样率下对应 1.5 秒如果采样频率不同应按时间长度换算而不是直接沿用帧数。对于不足 15 帧的短片段直接跳过平滑防止 Savitzky-Golay 在短序列上产生边界振荡。平滑后的数据可以在后续统计中替代原始速度与加速度。不少交通流研究里的基本图、跟驰模型标定都建议使用平滑后的速度序列而原始加速度只用于检测急变速事件等特殊场景。4.2 换道瞬间的干扰帧识别与摘除逻辑换道是污染跟驰数据的主要来源。前车换道时它在当前车道的纵向位置变化不再反映正常的跟驰行为间距可能突然跳变后车换道时它与前车的关系可能瞬间建立或解除。这两种情况都会被误判为「异常跟驰」需要专门处理。判断换道最直接的方式是看车道 ID 变化df[lead_lane_change] df.groupby(leader_id)[Lane_ID].diff().abs() df[follow_lane_change] df.groupby(Vehicle_ID)[Lane_ID].diff().abs() df[is_lane_change] (df[lead_lane_change] 0) | (df[follow_lane_change] 0)当后车或前车的Lane_ID相比于上一帧发生变化时当前时刻的跟驰关系就不成立应将这一帧标记为干扰帧。实际操作时我不止删掉换道发生的那一帧而是把换道前 5 帧和后 10 帧都标记为不参与计算。原因是车辆在换道前就会开始横向偏移纵向速度曲线也会随之波动这部分过渡数据同样不符合稳定的跟驰假设。除了车道 ID横向速度也是一个辅助判据。NGSIM 的轨迹点可以直接算横向位移的时间导数当横向速度超过某个阈值比如 0.5 m/s时判断车辆正在执行换道或大幅横向调整。将这个信号与车道 ID 变化结合可以识别出数据中某些车道 ID 还没来得及更新、但车辆已经开始横向移动的过渡帧。4.3 跟驰样本的有效性筛选参数表经过配对和清洗后需要用一张参数表把最终进入输出的样本卡一遍。下表是我在 NGSIM 跟驰车辆数据处理程序里常用的推荐阈值参数推荐值说明最小跟驰间距0.5 m小于此值基本是碰撞或数据错误最大跟驰间距120 m超过此值视为自由流不再受前车影响最小跟驰时长5 s50 帧保证样本包含完整的加速或制动响应最大相对速度30 m/s超过此值说明前后车运动状态完全解耦横向速度阈值0.5 m/s超过则判定在换道过程中加速度绝对值上限8 m/s²NGSIM 中极少出现更大的值多为噪声尖峰这些阈值不是随意定的最大跟驰间距参考了高速公路基本路段上驾驶员开始响应前车制动的距离范围最小跟驰时长则对应一次完整制动或加速事件的最短可辨识时间。使用 NGSIM 时建议先按上表清洗再根据具体研究问题调整阈值例如做紧急制动场景时最大间距阈值可以收紧到 60 米。筛选操作在gap、rel_v、平滑后的加速度列上进行全部条件用布尔组合一次性过滤然后按pair_id和segment_id重新统计剩余样本量。如果某段数据过滤后样本损失超过 40%需要回头检查是不是换道判定过于激进或者数据本身质量问题导致的配对错误。5. 用THW与TTC验证跟驰数据质量的落地手法5.1 计算车头时距 THW 与碰撞时间 TTC清洗完的数据能不能用于后续模型标定需要定量验证。最直观的检验指标是车头时距Time HeadwayTHW和碰撞时间Time to CollisionTTC。THW 指后车到达前车当前位置所需的时间反映跟驰的紧迫程度TTC 则是在当前相对速度下两车发生碰撞的剩余时间。计算方式如下df[thw] df[gap] / df[v_mps] df[ttc] np.where( df[rel_v] 0, df[gap] / np.abs(df[rel_v]), np.inf )THW 的计算里v_mps是后车速度当速度为 0 时数据点需要丢弃否则会出现无穷大值。TTC 只在相对速度为负、即前车比后车慢时才有定义相对速度大于等于零时设为无穷大表示没有碰撞风险。实际研究中通常只统计 TTC 小于某阈值如 5 秒或 3 秒的样本这个比例也叫风险暴露频率。正常跟驰数据中TTC 小于 3 秒的样本比例一般不应超过 10%如果超过这个范围说明配对或清洗环节可能把换道压缩、急减速等非稳态样本留了下来。THW 则呈现明显的偏态分布峰值通常在 1 到 3 秒之间这与高速公路实际车头时距分布规律一致。5.2 用基本图重算验证清洗前后的一致性跟驰数据处理程序跑完以后我会再做一个额外验证把清洗后的数据按 30 秒聚合画流量-密度散点图观察数据点是否落在合理的基本图区域。流量可以由单车速度乘以密度估算密度可以用车头间距倒数乘以车道数来表示。如果清洗前的数据在低密度区出现大量高速高流量点往往说明有换道瞬间的异常配对混入清洗后会看到这些点被剔除散点呈现更清晰的主线。这个验证同时也能帮你发现滤波参数是否过强如果平滑后的速度让整体流量比原始数据明显偏低说明窗口太大了真实的高速行驶事件被磨平了。此时把窗口从 15 帧调回 11 帧重新跑一遍即可。5.3 跟驰数据处理程序的任务化封装最后一个小技巧是把整套流程封装成一个命令行工具用输入文件名和路段参数作为入口方便批量处理 NGSIM 的多个时间段文件。参数化时把第 4 节表格里的阈值全部设计成可覆盖的默认值这样换数据集时不需要改代码只需修改配置。数据输出建议按pair_id、segment_id分片保存为 parquet 格式比 CSV 节省大量磁盘空间读取速度也快一个数量级。每条记录保留平滑后的速度、加速度、间距、THW、TTC 和车道信息就足够下游去训练 IDM 参数、评估 AEB 策略或者做驾驶风格聚类分析了。验证到这一步一套 NGSIM 跟驰车辆数据处理程序的闭环就算彻底跑通。本文还有配套的精品资源点击获取