路面类型识别 --- CNN-LSTM 完整流程
- [路面类型识别 --- CNN-LSTM 完整流程:基于车辆振动数据的端到端深度学习](#路面类型识别 — CNN-LSTM 完整流程:基于车辆振动数据的端到端深度学习)
路面类型识别 --- CNN-LSTM 完整流程:基于车辆振动数据的端到端深度学习
项目概述
本项目基于六分力传感器 采集的车辆振动数据(x/y/z 三轴加速度),使用 CNN-LSTM 深度学习框架实现路面类型识别 ,能够准确区分沥青路面 和湿滑路面。
核心特点
- CNN-LSTM 端到端学习:直接从原始振动信号中自动提取特征,无需手工设计特征
- 严格跨实验验证:用第1次实验数据训练 → 在第2次实验数据上测试(及反向),杜绝数据泄露
- 阈值自动调优:自动扫描最佳分类阈值,最大化 F1 分数
- 全面可视化:训练曲线、混淆矩阵、ROC 曲线、逐场景准确率热图
数据来源
| 实验 | 文件 | 说明 |
|---|---|---|
| 第1次试验 | 合并_所有传感器_第一次实验.xlsx |
包含 沥青路面(加速/匀速) + 湿滑路面 |
| 第2次试验 | 合并_所有传感器_第二次实验.xlsx |
同上,独立采集 |
- 4个传感器位置:左前、右前、左后、右后
- 采样频率:1600 Hz
- 三轴加速度:x, y, z
模型架构
Input (T, 3) → Conv1D×3 → LSTM(64) → Dense → 二分类
T=256 时间步 提取局部特征 时序建模 分类头
目录结构
路面识别_CNN_LSTM_完整流程/
├── config.py # 配置文件(所有参数集中管理)
├── data_loader.py # 数据加载、预处理、增强
├── models.py # CNN-LSTM 模型定义
├── trainer.py # 训练器(含 Focal Loss)
├── evaluation.py # 评估指标与可视化
├── run_pipeline.py # ★ 主流程入口
├── requirements.txt # Python 依赖
├── README.md # 本文件
└── 实验结果/ # 自动生成
├── models/ # 训练好的模型文件 (.pth)
├── plots/ # 可视化图表
└── cross_experiment_metrics.csv # 评估指标 CSV
运行方法
1. 安装依赖
bash
pip install torch numpy pandas matplotlib seaborn scikit-learn openpyxl joblib
2. 准备数据
确保父目录 合并后的数据/ 中有以下两个文件:
合并_所有传感器_第一次实验.xlsx合并_所有传感器_第二次实验.xlsx
3. 运行主程序
bash
cd 路面识别_CNN_LSTM_完整流程
python run_pipeline.py
4. 查看结果
运行完成后,在 实验结果/ 目录下查看:
plots/--- 训练曲线、混淆矩阵、ROC 曲线、场景热图、对比图models/--- 保存的模型权重文件cross_experiment_metrics.csv--- 指标汇总
配置参数说明
编辑 config.py 可调整以下参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| WINDOW_SIZE | 256 | 滑动窗口大小(时间步数) |
| STRIDE | 128 | 滑动步长(50%重叠) |
| BATCH_SIZE | 128 | 训练批次大小 |
| EPOCHS | 100 | 最大训练轮数 |
| PATIENCE | 15 | EarlyStopping 容忍轮数 |
| LEARNING_RATE | 1e-3 | 初始学习率 |
| TUNE_THRESHOLD | True | 是否自动调优阈值 |
| USE_FOCAL_LOSS | False | 是否使用 Focal Loss |
| USE_AUGMENTATION | False | 是否使用数据增强 |
| POSITION_SPECIFIC | False | 是否按位置分别建模 |
实验结果摘要
| 方向 | 准确率 | F1分数 | 最佳阈值 |
|---|---|---|---|
| Exp1→Exp2 | ~94% | ~89% | 0.8~0.9 |
| Exp2→Exp1 | ~97% | ~85% | 0.8~0.95 |
关键发现:CNN-LSTM 在严重不平衡场景(Exp2→Exp1,沥青:湿滑 ≈ 10.6:1)下显著优于传统 MLP,证明了其从原始时序信号中自动学习有效特征的能力。
技术亮点
1. 数据预处理流程
python
# 滑动窗口切分
def sliding_window(data, window_size=256, stride=128):
"""将长序列切分为固定长度的窗口"""
windows = []
for i in range(0, len(data) - window_size + 1, stride):
windows.append(data[i:i+window_size])
return np.array(windows)
2. CNN-LSTM 模型架构
python
import torch
import torch.nn as nn
class CNNLSTM(nn.Module):
def __init__(self, input_dim=3, hidden_dim=64, num_classes=2):
super(CNNLSTM, self).__init__()
# CNN 特征提取
self.conv1 = nn.Conv1d(input_dim, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv1d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv1d(64, 128, kernel_size=3, padding=1)
self.pool = nn.MaxPool1d(2)
# LSTM 时序建模
self.lstm = nn.LSTM(128, hidden_dim, batch_first=True, bidirectional=True)
# 分类头
self.fc = nn.Sequential(
nn.Linear(hidden_dim*2, 64),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(64, num_classes)
)
def forward(self, x):
# x: (batch, seq_len, input_dim)
x = x.transpose(1, 2) # -> (batch, input_dim, seq_len)
# CNN 处理
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = self.pool(torch.relu(self.conv3(x)))
# 转回时序格式
x = x.transpose(1, 2) # -> (batch, new_seq_len, 128)
# LSTM 处理
lstm_out, _ = self.lstm(x)
# 取最后一个时间步
out = lstm_out[:, -1, :]
# 分类
return self.fc(out)
3. 跨实验验证策略
python
# 严格的数据划分策略
def cross_experiment_split(data_exp1, labels_exp1, data_exp2, labels_exp2):
"""训练集和测试集来自不同实验,确保无数据泄露"""
# 方案1:Exp1训练,Exp2测试
X_train, y_train = data_exp1, labels_exp1
X_test, y_test = data_exp2, labels_exp2
# 方案2:Exp2训练,Exp1测试(反向验证)
# X_train, y_train = data_exp2, labels_exp2
# X_test, y_test = data_exp1, labels_exp1
return X_train, y_train, X_test, y_test
可视化结果
训练过程监控
项目自动生成以下可视化图表:
- 训练曲线:损失和准确率随 epoch 变化
- 混淆矩阵:模型在测试集上的分类表现
- ROC 曲线:不同阈值下的性能表现
- 场景热图:不同驾驶场景下的准确率分布
- 模型对比图:CNN-LSTM vs 传统方法
性能对比
#mermaid-svg-mHHaxLgZb6101jkN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mHHaxLgZb6101jkN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mHHaxLgZb6101jkN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mHHaxLgZb6101jkN .error-icon{fill:#552222;}#mermaid-svg-mHHaxLgZb6101jkN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mHHaxLgZb6101jkN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mHHaxLgZb6101jkN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mHHaxLgZb6101jkN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mHHaxLgZb6101jkN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mHHaxLgZb6101jkN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mHHaxLgZb6101jkN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mHHaxLgZb6101jkN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mHHaxLgZb6101jkN .marker.cross{stroke:#333333;}#mermaid-svg-mHHaxLgZb6101jkN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mHHaxLgZb6101jkN p{margin:0;}#mermaid-svg-mHHaxLgZb6101jkN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mHHaxLgZb6101jkN .cluster-label text{fill:#333;}#mermaid-svg-mHHaxLgZb6101jkN .cluster-label span{color:#333;}#mermaid-svg-mHHaxLgZb6101jkN .cluster-label span p{background-color:transparent;}#mermaid-svg-mHHaxLgZb6101jkN .label text,#mermaid-svg-mHHaxLgZb6101jkN span{fill:#333;color:#333;}#mermaid-svg-mHHaxLgZb6101jkN .node rect,#mermaid-svg-mHHaxLgZb6101jkN .node circle,#mermaid-svg-mHHaxLgZb6101jkN .node ellipse,#mermaid-svg-mHHaxLgZb6101jkN .node polygon,#mermaid-svg-mHHaxLgZb6101jkN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mHHaxLgZb6101jkN .rough-node .label text,#mermaid-svg-mHHaxLgZb6101jkN .node .label text,#mermaid-svg-mHHaxLgZb6101jkN .image-shape .label,#mermaid-svg-mHHaxLgZb6101jkN .icon-shape .label{text-anchor:middle;}#mermaid-svg-mHHaxLgZb6101jkN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mHHaxLgZb6101jkN .rough-node .label,#mermaid-svg-mHHaxLgZb6101jkN .node .label,#mermaid-svg-mHHaxLgZb6101jkN .image-shape .label,#mermaid-svg-mHHaxLgZb6101jkN .icon-shape .label{text-align:center;}#mermaid-svg-mHHaxLgZb6101jkN .node.clickable{cursor:pointer;}#mermaid-svg-mHHaxLgZb6101jkN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mHHaxLgZb6101jkN .arrowheadPath{fill:#333333;}#mermaid-svg-mHHaxLgZb6101jkN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mHHaxLgZb6101jkN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mHHaxLgZb6101jkN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mHHaxLgZb6101jkN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mHHaxLgZb6101jkN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mHHaxLgZb6101jkN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mHHaxLgZb6101jkN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mHHaxLgZb6101jkN .cluster text{fill:#333;}#mermaid-svg-mHHaxLgZb6101jkN .cluster span{color:#333;}#mermaid-svg-mHHaxLgZb6101jkN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mHHaxLgZb6101jkN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mHHaxLgZb6101jkN rect.text{fill:none;stroke-width:0;}#mermaid-svg-mHHaxLgZb6101jkN .icon-shape,#mermaid-svg-mHHaxLgZb6101jkN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mHHaxLgZb6101jkN .icon-shape p,#mermaid-svg-mHHaxLgZb6101jkN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mHHaxLgZb6101jkN .icon-shape .label rect,#mermaid-svg-mHHaxLgZb6101jkN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mHHaxLgZb6101jkN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mHHaxLgZb6101jkN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mHHaxLgZb6101jkN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始振动数据
特征提取
方法选择
传统手工特征
CNN-LSTM 自动特征
MLP 分类器
端到端分类
准确率: ~85%
准确率: ~94-97%
应用场景
- 智能驾驶系统:实时路面状态感知
- 车辆健康监测:路面异常检测
- 道路养护管理:路面状况评估
- 自动驾驶决策:根据路面类型调整控制策略
总结与展望
本项目展示了 CNN-LSTM 在车辆振动数据分析中的强大能力,主要优势包括:
- 端到端学习:无需复杂的手工特征工程
- 时序建模:LSTM 有效捕捉振动信号的时序依赖
- 强泛化性:跨实验验证证明了模型的鲁棒性
- 实用性强:完整的工程化实现,开箱即用
未来改进方向:
- 增加更多路面类型(水泥路、砂石路等)
- 融合多传感器数据(陀螺仪、GPS等)
- 在线学习与自适应更新
- 部署到嵌入式设备实时推理
资源下载
完整代码已开源,欢迎 Star 和 Fork:
- GitHub 仓库 :路面识别-CNN-LSTM
- 数据集:包含两次实验的完整振动数据
- 预训练模型:可直接用于推理的模型权重
参考文献
- Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.
- Hochreiter, S., & Schmidhuber, J. (1997). Long short-term memory. Neural computation.
- LeCun, Y., Bengio, Y., & Hinton, G. (2015). Deep learning. Nature.
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
联系方式:如有问题或建议,欢迎在评论区留言或通过 GitHub Issues 反馈。