前一段时间在做车辆模拟碰撞相关的数据分析时,一直想找一个能完整呈现“正面 40% 偏置碰撞”测试思路的练习案例。正好手头有一套模拟环境,可以复现一台 2005 年的老旧 SUV 在不同碰撞条件下产生的动态响应。本文就以《【BASA】2005 Bruckell VRV 正面40%碰撞测试》为场景,完整梳理从测试场景设定、数据采集,再到 Python 解析与可视化的一整套流程。
这篇文章比较适合对车辆碰撞安全、仿真测试数据分析和 Python 数据处理感兴趣的读者。如果你手头有类似的气囊标定数据、碰撞仿真输出或整车道路试验数据,也可以在阅读过程中对比这份思路。文章不会下任何“车辆安全性好坏”的结论,重点是把“如何分析一次碰撞测试结果”这件事讲清楚。
1. 什么是正面 40% 偏置碰撞测试
1.1 碰撞测试的基本分类
车辆碰撞测试按照碰撞形式可以简单分成三大类:正面碰撞、侧面碰撞和追尾碰撞。正面碰撞又可以继续细化成正面 100% 重叠刚性墙碰撞、正面 40% 偏置碰撞、正面 25% 小重叠碰撞等。
不同重叠率模拟的是不同的真实事故场景:
- 100% 重叠刚性墙碰撞,用来模拟整车正面直接撞墙的工况,对车身纵梁、防火墙和约束系统考核比较全面。
- 40% 偏置碰撞,模拟车头一侧约 40% 的宽度与对向车辆或障碍物发生重叠碰撞,这种工况对乘员舱结构强度要求更高。
- 25% 小重叠碰撞,模拟车头外侧与树、护栏等刚性物体刮擦碰撞,对 A 柱、门槛梁和侧围结构压力比较大。
本文提到的“正面 40% 碰撞测试”,指的就是正面前方障碍物与车辆前端 40% 重叠的碰撞工况。这类试验对车辆结构侵入、假人伤害指标、安全带约束效果都有很强的区分度。
1.2 为什么 40% 偏置碰撞更值得关注
在实际交通事故中,两车正面相撞时很少是完整的车头对车头。驾驶员通常会下意识打方向避让,所以很多正面碰撞都伴随着一定偏置量。40% 偏置碰撞正是基于这种真实场景设计的。
从力学角度分析,车头右侧 40% 参与碰撞时,吸能区域不完整,碰撞力会通过左侧纵梁、副车架传递到乘员舱。如果车身结构设计不够好,防火墙、A 柱下段、门槛梁就会发生明显变形,进而挤压驾驶员腿部空间和仪表板区域。
所以 40% 偏置碰撞测试关注的核心指标不只是整车减速过程,还包括:
- 乘员舱结构侵入量
- A 柱是否发生弯折
- 防火墙是否明显后移
- 转向管柱是否发生位移
- 门框变形是否影响车门开启
在模拟环境中,我们同样可以从这些维度去分析 Bruckell VRV 这台车的表现。
1.3 模拟碰撞测试的特点
在当前项目中,我们的碰撞测试是在车辆动力学模拟软件中完成的。相比真实物理碰撞试验,模拟测试有这些优点:
- 成本低,不需要制造实车样件,不需要报废整车。
- 可重复性高,同一参数可以反复运行,测试结果不会因环境波动产生差异。
- 参数调整方便,可以通过修改碰撞重叠率、初始速度、障碍物刚度等快速对比。
- 数据采集灵活,可以同时记录成百上千个通道的传感器数据,不担心传感器损坏或丢失。
当然,模拟测试也有局限性。它依赖车辆动力学模型、材料失效模型和接触算法,结果只能无限接近真实试验,不能完全替代法规认证。这也是本文反复强调的一点:模拟数据用于开发阶段横向对比和趋势分析,不要把它当成国家法规认证结果。
2. 测试场景与数据分析环境准备
2.1 BASA 项目与测试车辆信息
本次测试项目代号为“BASA”,可以理解为一套“基础整车安全分析”练习流程。测试对象是一台 2005 年款 Bruckell VRV,这是一台带有复古风格的家用 SUV 车型,轴距较长、车身较高,整备质量在同级车型中属于偏大的类型。
由于车辆年款较老,车身结构设计更多强调舒适性和实用性,对轻量化高强钢的使用比例并不高。在碰撞安全分析中,老旧车型往往更容易暴露结构吸能不足、A 柱变形偏大等问题。这也是我们选择这台车作为分析样本的原因之一。
下表整理了本次模拟分析的基本参数:
| 参数项 | 设定值 |
|---|---|
| 测试车辆 | 2005 Bruckell VRV |
| 碰撞类型 | 正面 40% 偏置碰撞 |
| 碰撞重叠率 | 40% |
| 试验质量 | 整备质量 + 驾驶员模拟质量 |
| 碰撞速度 | 以测试场景实际设定为准 |
| 地面条件 | 干燥平整沥青路面 |
| 温度环境 | 常温 20℃ 左右 |
需要特别说明:以上参数是模拟场景中使用的预设值,真实测试标准会严格按照法规要求执行。这里只是展示分析思路,参数本身需要根据你自己的软件版本和场景设定来调整。
2.2 数据分析软件清单
在做碰撞测试数据分析之前,首先确认电脑里的基础环境:
- 操作系统:Windows 10/11 或 Ubuntu 20.04+ 均可
- Python 版本:Python 3.8 及以上
- 编辑器:VS Code、PyCharm 或 Jupyter Notebook 任选
- 核心依赖库:pandas、numpy、matplotlib、scipy
如果还没有安装这些库,可以在终端中执行下面的命令:
pip install pandas numpy matplotlib scipy如果你使用的是 Anaconda,也可以使用 conda 安装:
conda install pandas numpy matplotlib scipy安装完成之后,可以通过一条命令检查版本是否正常:
python -m pip list | findstr pandas这里先不粘贴具体的版本号,因为每个人的环境不同。只要 pandas、numpy、matplotlib 能正常导入,就可以继续往下操作。
2.3 数据文件组织方式
一次典型的碰撞测试会生成多个数据文件,包括车辆轨迹、姿态、速度和加速度波形等。建议在项目目录中按下列方式组织文件:
BASA-2005-VRV/ ├── data/ │ ├── crash_40_overlap/ │ │ ├── telemetry.csv │ │ ├── vehicle_state.csv │ │ └── intrusion_points.json │ └── readme.md ├── scripts/ │ ├── load_data.py │ ├── speed_analysis.py │ ├── acceleration_analysis.py │ └── intrusion_analysis.py └── output/ └── figures/这样的目录结构好处很明显:原始数据、处理脚本和输出图表彼此隔离,不容易出现误改数据文件的问题。尤其是当你手头有多组碰撞工况对比的时候,建议每一组工况单独建一个数据文件夹。
3. 碰撞测试结果的关键分析指标
3.1 车辆速度变化曲线
碰撞过程最直观的表现就是速度变化。车辆从初始速度逐步降到零,整个过程一般在几十毫秒到上百毫秒内完成。
速度曲线主要关注三个信息:
- 碰撞初始速度:决定了碰撞能量的大小。
- 速度归零时间点:反映了碰撞吸能的持续时间。
- 速度曲线的斜率:斜率越陡,说明碰撞越剧烈。
如果速度曲线在短时间内发生剧烈跳变,往往意味着车身结构没有充分参与吸能,或者是传感器数据本身存在噪声。
3.2 加速度与碰撞强度
加速度是碰撞测试中最重要的物理量之一。车辆在碰撞过程中的加速度变化,直接反映了乘员受到的惯性载荷大小。
碰撞加速度通常用 g 表示,1g 约等于 9.8 m/s²。加速度值越大,乘员受到的冲击越强烈。
分析加速度时,一般会使用滤波处理后的波形。滤波的作用是去掉高频噪声,保留与车辆结构响应相关的低频成分。常见的滤波方式包括 SAE J211 标准滤波和低通滤波。
在实际代码中,我们可以先计算原始加速度数据,再用 scipy 的 butterworth 滤波器做平滑处理。后面的演示代码会具体展示。
3.3 乘员舱侵入量
除了整车的速度、加速度,车身局部变形量同样是判断碰撞结果的重要指标。乘员舱侵入量主要指 A 柱、防火墙、地板、转向管柱、踏板等区域相对乘员舱原始位置的位移。
侵入量越大,乘员被结构挤压的风险越高。尤其对老旧 SUV 来说,防火墙刚度不足时,碰撞后制动踏板和转向管柱会向驾驶员方向移动,这是非常危险的。
在模拟软件中,我们可以直接输出几个关键测量点在碰撞前后的坐标,然后在 Python 中计算空间位移量。
4. 基于 Python 的碰撞数据读取方法
4.1 读取 CSV 遥测文件
碰撞测试软件导出的数据格式并不统一,常见的包括 CSV、Excel、JSON 甚至二进制格式。为了演示,假设我们已经导出了一份 telemetry.csv 文件,里面包含时间戳、速度、加速度等字段。
下面是一段读取并预览数据的代码:
# 文件路径:scripts/load_data.py import pandas as pd # 读取碰撞过程遥测数据 df = pd.read_csv('../data/crash_40_overlap/telemetry.csv') # 查看数据列名 print("数据列名:") print(df.columns.tolist()) # 查看前 5 行 print("\n前 5 行数据预览:") print(df.head()) # 查看数据基本统计信息 print("\n数据统计信息:") print(df.describe())运行后,你会看到类似下面的输出结构:
数据列名: ['time', 'speed_kmh', 'accel_x_g', 'accel_y_g', 'accel_z_g'] 前 5 行数据预览: time speed_kmh accel_x_g accel_y_g accel_z_g 0 0.00 35.40 -0.02 0.01 1.00 1 0.02 ...注意:不同模拟软件导出的列名可能不同,如果直接复制这段代码发现 KeyError,不用紧张,先用 print(df.columns) 查看字段名,再去改列名即可。
4.2 时间轴对齐与空值处理
碰撞数据经常会出现两种问题:一是时间戳不连续,二是某些传感器在某段时间内无数据。直接分析会导致曲线突变或中断,因此需要先对时间轴进行检查和处理。
# 文件路径:scripts/load_data.py import numpy as np import pandas as pd df = pd.read_csv('../data/crash_40_overlap/telemetry.csv') # 检查是否存在空值 print("每列空值数量:") print(df.isnull().sum()) # 如果存在空值,可以选择删除或插值 # 碰撞数据中短时间缺失采用线性插值比较合理 if df.isnull().sum().sum() > 0: df = df.interpolate(method='linear', limit_direction='both') # 检查时间列是否单调递增 time_diff = np.diff(df['time'].values) if (time_diff <= 0).any(): print("警告:时间轴存在非递增时刻,需要排序") df = df.sort_values(by='time').reset_index(drop=True) print("数据清洗完成,总记录数:", len(df))这里选用线性插值而不是删除缺失行,是因为碰撞过程非常短,任何瞬间的缺失都可能影响后续峰值的判断。当然,如果数据缺失过多,整段传感器的可靠性就得打问号了。
4.3 JSON 格式侵入点数据读取
侵入量数据有时会以 JSON 格式保存,因为里面通常包含多个测量点的三维坐标。下面演示如何读取并转换成 DataFrame:
# 文件路径:scripts/load_data.py import json import pandas as pd # 读取侵入点数据 with open('../data/crash_40_overlap/intrusion_points.json', 'r', encoding='utf-8') as f: intrusion_data = json.load(f) # 将每个测量点转换为行记录 records = [] for point_name, info in intrusion_data.items(): records.append({ 'point': point_name, 'initial_x': info['initial']['x'], 'initial_y': info['initial']['y'], 'initial_z': info['initial']['z'], 'final_x': info['final']['x'], 'final_y': info['final']['y'], 'final_z': info['final']['z'], }) df_points = pd.DataFrame(records) # 计算合位移 df_points['displacement'] = ( (df_points['final_x'] - df_points['initial_x']) ** 2 + (df_points['final_y'] - df_points['initial_y']) ** 2 + (df_points['final_z'] - df_points['initial_z']) ** 2 ) ** 0.5 print(df_points.sort_values(by='displacement', ascending=False))这段代码会输出每个测量点在碰撞前后的三维位移量。对我们分析碰撞侵入情况非常关键。
5. 碰撞数据分析与可视化实战
5.1 速度曲线分析
车辆碰撞的过程中,车速从初始碰撞速度逐渐下降。为了观察速度变化的整体趋势,我们可以绘制散点或折线图。
# 文件路径:scripts/speed_analysis.py import matplotlib.pyplot as plt import pandas as pd # 读取数据 df = pd.read_csv('../data/crash_40_overlap/telemetry.csv') # 截取碰撞时间段,比如碰撞前 0.1 秒到碰撞后 0.3 秒 mask = (df['time'] >= -0.1) & (df['time'] <= 0.3) df_crash = df[mask].copy() # 将速度单位从 km/h 换算成 m/s df_crash['speed_mps'] = df_crash['speed_kmh'] / 3.6 # 绘制速度曲线 plt.figure(figsize=(10, 5), dpi=120) plt.plot(df_crash['time'], df_crash['speed_kmh'], label='Vehicle Speed (km/h)', color='#E74C3C', linewidth=2) plt.axhline(y=0, color='gray', linestyle='--', linewidth=0.8) plt.xlabel('Time (s)') plt.ylabel('Speed (km/h)') plt.title('2005 Bruckell VRV - Frontal 40% Overlap Crash Speed') plt.grid(alpha=0.3) plt.legend() plt.tight_layout() plt.savefig('../output/figures/speed_curve.png', dpi=150) plt.show()这段代码的关键点在于先过滤出碰撞时间段,再绘图。如果不做时间段裁剪,画出来的曲线会被前后很长一段匀速或减速过程压扁,碰撞阶段的细节就看不清楚了。
5.2 加速度波形计算与滤波
加速度信号通常比较“毛躁”,直接画原图会看到很多高频抖振。为了观察趋势,需要先滤波。
使用巴特沃斯低通滤波器是常见选择。下面的代码实现了 100Hz 低通滤波,并绘制碰撞过程中的纵向加速度曲线:
# 文件路径:scripts/acceleration_analysis.py import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy.signal import butter, filtfilt # 读取数据 df = pd.read_csv('../data/crash_40_overlap/telemetry.csv') # 定义低通滤波函数 def lowpass_filter(data, cutoff_hz, sample_rate_hz, order=4): nyquist = 0.5 * sample_rate_hz normal_cutoff = cutoff_hz / nyquist b, a = butter(order, normal_cutoff, btype='low', analog=False) return filtfilt(b, a, data) # 根据时间列估算采样率 dt = np.median(np.diff(df['time'].values)) sample_rate = 1.0 / dt print(f"估算采样率:{sample_rate:.0f} Hz") # 截取碰撞时间段 mask = (df['time'] >= 0.0) & (df['time'] <= 0.2) df_crash = df[mask].copy() # 对纵向加速度进行低通滤波 filtered_accel = lowpass_filter(df_crash['accel_x_g'].values, cutoff_hz=100, sample_rate_hz=sample_rate) # 绘制原始与滤波后加速度对比 plt.figure(figsize=(12, 6), dpi=120) plt.plot(df_crash['time'], df_crash['accel_x_g'], alpha=0.3, label='Original Accel X', color='#BDC3C7') plt.plot(df_crash['time'], filtered_accel, label='Filtered Accel X (100Hz)', color='#2980B9', linewidth=2) plt.axhline(y=0, color='gray', linestyle='--', linewidth=0.8) plt.axhline(y=np.min(filtered_accel), color='#E74C3C', linestyle=':', linewidth=1.5) plt.text(df_crash['time'].iloc[-1], np.min(filtered_accel), f"Peak: {np.min(filtered_accel):.2f} g", ha='right', va='bottom', color='#E74C3C') plt.xlabel('Time (s)') plt.ylabel('Acceleration (g)') plt.title('BASA - Bruckell VRV Frontal 40% Overlap Longitudinal Acceleration') plt.grid(alpha=0.3) plt.legend() plt.tight_layout() plt.savefig('../output/figures/acceleration_curve.png', dpi=150) plt.show() # 输出峰值信息 print(f"滤波后纵向加速度最大负向峰值:{np.min(filtered_accel):.2f} g")峰值加速度所在的时间点,对应乘员受到最大惯性载荷的时刻。可以把该时间点与前面速度曲线归零的时间点做对比,从而判断碰撞峰值的发生时机是否合理。
5.3 侵入量变化趋势图
侵入量分析主要是横向对比。我们可以把各测量点的时域侵入过程画出来,考察车门框、防火墙、门槛梁是否存在过大位移。
# 文件路径:scripts/intrusion_analysis.py import json import matplotlib.pyplot as plt import numpy as np # 读取侵入点数据 with open('../data/crash_40_overlap/intrusion_points.json', 'r', encoding='utf-8') as f: intrusion_data = json.load(f) # 初始化图表 plt.figure(figsize=(12, 6), dpi=120) # 每个测量点都包含时域位移信息 for point_name, info in intrusion_data.items(): if 'series' not in info: continue time_series = np.array(info['series']['time']) displacement_series = np.array(info['series']['displacement']) plt.plot(time_series, displacement_series, linewidth=1.5, label=point_name) plt.xlabel('Time (s)') plt.ylabel('Intrusion Displacement (mm)') plt.title('BASA - Key Cabin Points Intrusion Timeline') plt.grid(alpha=0.3) plt.legend(loc='upper left') plt.tight_layout() plt.savefig('../output/figures/intrusion_timeline.png', dpi=150) plt.show()绘制侵入量时间历程的好处是:我们可以判断变形是发生在碰撞瞬间,还是在碰撞后期依然持续增大。如果在车辆速度归零之后侵入量还在增长,说明结构存在“压溃后继续蠕变”的情况。
5.4 多工况对比思路
实际数据分析中,单次测试结果并不能说明问题。横向对比是很常用的手段,比如对比 40% 偏置碰撞与 100% 正面碰撞的加速度峰值。
你可以把多组数据放在同一个 DataFrame 中管理,然后用分组统计的方式计算峰值:
# 文件路径:scripts/multi_case_compare.py import pandas as pd # 假设已有多个工况的文件列表 file_list = [ ('40%_overlap', 'data/crash_40_overlap/telemetry.csv'), ('100%_frontal', 'data/crash_100_frontal/telemetry.csv'), ] rows = [] for case_name, file_path in file_list: df = pd.read_csv(file_path) # 只统计碰撞后 0~0.2 秒的数据 df_crash = df[(df['time'] >= 0.0) & (df['time'] <= 0.2)] peak_g = df_crash['accel_x_g'].min() rows.append({'case': case_name, 'peak_accel_g': round(peak_g, 2)}) summary = pd.DataFrame(rows) print(summary)这种横向对比输出非常适合用来快速观察不同工况的差异。
6. 常见报错与排查清单
在做碰撞数据分析的过程中,有一些高频问题经常出现。下面整理成表格,方便大家排查。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 读取 CSV 后提示 Column not found | 软件导出的列名与代码不一致 | 先打印 columns 字段,确认实际列名 |
| 时间轴不连续导致曲线断裂 | 模拟帧率波动或导出丢帧 | 使用 np.diff 检查时间间隔,做重采样处理 |
| 加速度曲线噪声非常大 | 未进行滤波或采样率过高 | 使用巴特沃斯低通滤波器,选择合适截止频率 |
| 速度曲线碰撞后没有归零 | 数据导出范围被截断 | 检查导出时间范围,延长碰撞后记录时间 |
| JSON 文件读取为字符串 | json.load 前没有正确打开文件 | 检查文件编码,使用 utf-8 打开 |
| matplotlib 中文乱码 | 系统缺少中文字体 | 绘图时设置 rc 参数指定中文字体 |
| 峰值加速度数值偏大 | 原始信号包含高频噪声 | 检查滤波截止频率和滤波器阶数 |
| 侵入量曲线不平滑 | 测量点接触算法抖动 | 使用滑动平均或增加数据导出频率 |
6.1 JSON 文件编码问题详解
Windows 系统下导出的 JSON 文件经常使用 UTF-8 编码,但也可能有部分工具使用 GBK。如果直接读取报编码错误,可以改成:
with open(file_path, 'r', encoding='utf-8-sig') as f: data = json.load(f)utf-8-sig 会自动去除带 BOM 的文件头,适合大多数从 Windows 导出的文件。
6.2 滤波器参数如何选择
SAE J211 是汽车碰撞试验中比较常用的滤波标准,不同通道对应不同频率等级。例如:
- 车身结构加速度通道通常使用 CFC 60
- 假人头部加速度通道使用 CFC 1000
- 假人颈部力通道使用 CFC 1000
本文只是 Python 演示,采用了简化的 100Hz 低通滤波思路。实际工程中应该严格按照对应法规标准来滤波。模拟数据对比时,要保证多组数据使用完全相同的滤波参数,否则结果没有可比性。
7. 碰撞测试项目中的工程建议
7.1 数据命名与版本管理
一次整车级别的碰撞分析会涉及非常多的数据文件。如果不做数据命名规范,过两周再回来看完全不知道每个文件对应什么工况。
建议命名规则:
日期_车辆代号_碰撞类型_速度_重复次数例如:
20250104_BruckellVRV_F40_64kmh_run01.csv这样可以避免文件重名、覆盖和漏算。开发中用到 Git 管理时,还要注意不要把上百 MB 的数据文件直接提交到代码仓库,建议采取数据文件与脚本分离的方式。
7.2 数据采集与备份策略
碰撞模拟虽然不会像真实试验那样一次撞毁几台车,但运行时如果忘记导出数据或软件崩溃,同样会导致数据丢失。
建议:
- 运行前检查输出路径是否存在。
- 每个工况运行完成后立即备份到独立目录。
- 碰撞关键通道建议使用冗余记录。
- 数据处理脚本输出统一存放,不要覆盖原始数据。
7.3 结果评价的安全边界
这里必须强调一点:模拟碰撞数据只能用于研发阶段的参数趋势分析,不能直接用来申报或证明某款车满足法规安全要求。不同软件版本、不同车辆模型参数之间的差异,可能导致结果偏差很大。
我们在公开平台讨论 Bruckell VRV 这类模拟测试时,也应该保持严谨的态度。不要使用“某车不安全”“某车垃圾”等绝对化表述。因为:
- 车辆型号属于虚构游戏车型,不是真实品牌车辆。
- 模拟测试中驾驶员模型、约束系统模型都做了一定简化。
- 一次碰撞测试结果无法代表车辆在真实复杂事故中的所有表现。
从数据分析的角度看,我们要关注的是方法流程是否规范、数据处理是否合理、结论是否可复现。
7.4 报告输出的最小要素
一份完整的碰撞数据分析报告,至少需要包含以下内容:
- 测试目的与工况说明
- 车辆参数与模型版本信息
- 数据通道列表与采样率说明
- 滤波方法与处理参数
- 关键结果图表
- 量化指标统计表
- 异常数据说明
- 结论与局限性分析
这样写出来的分析报告才具备可追溯性,也方便他人复核。
8. 完整脚本整合与运行
为了方便你直接复现,这里提供一份整合后的分析主脚本。把下面代码保存到 scripts/main_analysis.py,然后在项目根目录执行即可。
# 文件路径:scripts/main_analysis.py """ BASA - 2005 Bruckell VRV 正面 40% 碰撞测试数据分析示例 仅用于模拟环境数据分析学习,不代表真实车辆安全评价。 """ import json import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy.signal import butter, filtfilt # ========== 全局配置 ========== DATA_DIR = '../data/crash_40_overlap' OUTPUT_DIR = '../output/figures' # ========== 1. 读取遥测数据 ========== telemetry_path = f'{DATA_DIR}/telemetry.csv' df = pd.read_csv(telemetry_path) # 数据清洗与排序 if df.isnull().sum().sum() > 0: df = df.interpolate(method='linear', limit_direction='both') time_diff = np.diff(df['time'].values) if (time_diff <= 0).any(): df = df.sort_values(by='time').reset_index(drop=True) # ========== 2. 读取侵入点数据 ========== intrusion_path = f'{DATA_DIR}/intrusion_points.json' with open(intrusion_path, 'r', encoding='utf-8-sig') as f: intrusion_data = json.load(f) def lowpass_filter(data, cutoff_hz, sample_rate_hz, order=4): nyquist = 0.5 * sample_rate_hz normal_cutoff = cutoff_hz / nyquist b, a = butter(order, normal_cutoff, btype='low', analog=False) return filtfilt(b, a, data) # ========== 3. 计算碰撞阶段统计指标 ========== df_crash = df[(df['time'] >= 0.0) & (df['time'] <= 0.2)].copy() dt = np.median(np.diff(df['time'].values)) sample_rate = 1.0 / dt filtered_accel = lowpass_filter(df_crash['accel_x_g'].values, cutoff_hz=100, sample_rate_hz=sample_rate) peak_accel = np.min(filtered_accel) speed_min = df_crash['speed_kmh'].min() print('碰撞阶段分析结果:') print(f'估算采样率: {sample_rate:.0f} Hz') print(f'滤波后纵向峰值加速度: {peak_accel:.2f} g') print(f'碰撞阶段最低速度: {speed_min:.2f} km/h') # ========== 4. 侵入点位移统计 ========== print('\n关键测量点侵入量统计:') for point_name, info in intrusion_data.items(): initial = info.get('initial', {}) final = info.get('final', {}) displacement = ( (final['x'] - initial['x']) ** 2 + (final['y'] - initial['y']) ** 2 + (final['z'] - initial['z']) ** 2 ) ** 0.5 print(f'{point_name}: {displacement:.2f} mm') # ========== 5. 绘制关键图表 ========== fig, axes = plt.subplots(2, 2, figsize=(14, 10), dpi=120) # 子图1:速度曲线 ax = axes[0][0] df_full_time = df[(df['time'] >= -0.1) & (df['time'] <= 0.3)] ax.plot(df_full_time['time'], df_full_time['speed_kmh'], color='#E74C3C', linewidth=2) ax.set_xlabel('Time (s)') ax.set_ylabel('Speed (km/h)') ax.set_title('Vehicle Speed vs Time') ax.grid(alpha=0.3) # 子图2:加速度曲线 ax = axes[0][1] ax.plot(df_crash['time'], df_crash['accel_x_g'], alpha=0.3, color='#BDC3C7') ax.plot(df_crash['time'], filtered_accel, color='#2980B9', linewidth=2) ax.axhline(y=peak_accel, color='#E74C3C', linestyle=':') ax.set_xlabel('Time (s)') ax.set_ylabel('Acceleration (g)') ax.set_title('Longitudinal Acceleration (Filtered)') ax.grid(alpha=0.3) # 子图3:侵入量时间历程 ax = axes[1][0] for point_name, info in intrusion_data.items(): if 'series' not in info: continue series_time = np.array(info['series']['time']) series_disp = np.array(info['series']['displacement']) ax.plot(series_time, series_disp, linewidth=1.5, label=point_name) ax.set_xlabel('Time (s)') ax.set_ylabel('Displacement (mm)') ax.set_title('Key Points Intrusion Timeline') ax.grid(alpha=0.3) ax.legend(fontsize=8) # 子图4:侵入量条形图 ax = axes[1][1] points = [] disp_values = [] for point_name, info in intrusion_data.items(): initial = info.get('initial', {}) final = info.get('final', {}) displacement = ( (final['x'] - initial['x']) ** 2 + (final['y'] - initial['y']) ** 2 + (final['z'] - initial['z']) ** 2 ) ** 0.5 points.append(point_name) disp_values.append(displacement) ax.bar(points, disp_values, color='#16A085') ax.set_xlabel('Measure Points') ax.set_ylabel('Total Displacement (mm)') ax.set_title('Final Intrusion by Measure Point') plt.setp(ax.get_xticklabels(), rotation=30, ha='right') plt.tight_layout() plt.savefig(f'{OUTPUT_DIR}/basa_summary.png', dpi=150) plt.show() print('\n图表已输出到', OUTPUT_DIR)执行方式:
cd scripts python main_analysis.py如果你使用 Jupyter Notebook,也可以直接按单元格拆分运行。这样更容易观察每一步的数据形状和中间结果。
9. 总结与下一步学习建议
通过这次《【BASA】2005 Bruckell VRV 正面40%碰撞测试》的数据分析,我们完整走了一遍“场景理解 - 数据读取 - 数据清洗 - 速度分析 - 加速度分析 - 侵入量分析 - 可视化呈现”的项目流程。
从技术层面可以掌握这几个关键点:
- 正面 40% 偏置碰撞测试的基本原理和分析维度
- 碰撞数据常见的 CSV、JSON 文件读取和处理方法
- 速度曲线、加速度波形、侵入量数据在 Python 中的可视化方式
- 低通滤波在加速度信号处理中的应用
- 多工况对比分析的基本思路
- 模拟碰撞数据的局限性和工程合规边界
如果你是刚开始接触车辆数据分析,建议下一步从这几件事入手:
- 多跑几个不同速度、不同重叠率的碰撞场景,把数据放在同一套脚本里做横向对比。
- 尝试加入假人模型,重点关注头部加速度、胸部压缩量和颈部受力等伤害指标。
- 继续学习信号处理知识,深入理解不同滤波频率对结果的影响。
模拟环境中一次碰撞的完整数据,往往隐藏着大量值得挖掘的信息。先把数据读取和清洗这一步做扎实,后续不管是做结构优化、约束系统标定,还是生成评测报告,都会顺手很多。
如果文章里这段代码和排查思路对你有帮助,可以先收藏备用。后面有更多实战数据再继续补充分析细节。