案例6:智能小车视觉感知
案例6:智能小车视觉感知
1. 项目简介
本项目基于昇腾 310B 平台,构建一个智能小车的视觉感知系统。系统结合 经典计算机视觉和深度学习两种方法:使用 OpenCV 的 Canny 边缘 检测 + Hough 变换进行车道线检测,使用 ResNet18 卷积神经网络在 NPU 上 进行驾驶场景分类。两种方法互补——几何特征(车道线)用经典 CV 提取, 语义理解(场景类型)交给深度学习。
与旧版案例6不同,本项目不涉及机器人硬件(底盘、电机、激光雷达等), 而是聚焦于昇腾 310B 最擅长的部分:摄像头画面的实时 AI 理解。这 也是真实自动驾驶系统中"感知层"的核心功能。
与已有案例的关系
| 案例 | 领域 | NPU 任务 | 方法特点 |
|---|---|---|---|
| 案例2 | 目标检测 + 跟踪 | SSD 检测 | 纯深度学习 |
| 案例6 | 智能小车感知 | ResNet18 场景分类 | 经典 CV + 深度学习混合 |
| 案例7 | 智能相册 | ResNet50 特征提取 | 批量索引 + FAISS 检索 |
| 案例8 | 手势识别 | MobileNetV3 分类 | 训练 -> 转换 -> 部署 |
案例6是全书第一个显式结合经典 CV 与深度学习的案例,展示了在实际 工程中如何选择合适的技术方案——不是所有问题都需要神经网络。
2. 内容大纲
2.1. 硬件准备
本项目只需要:
- 昇腾 310B 开发者套件:核心计算单元,运行场景分类模型
- USB 摄像头(可选):用于实时画面采集,也可以用测试图片
- 显示屏(可选):用于浏览器访问 Gradio 界面
无需任何机器人硬件。车道线检测和场景分类都是纯视觉任务,在 310B + 摄像头组合上即可完整运行。
硬件架构如图所示:
2.2. 软件环境
操作系统与框架
| 层级 | 软件 | 版本 | 用途 |
|---|---|---|---|
| 操作系统 | Ubuntu | 20.04 / 22.04 LTS | 运行环境 |
| CANN | Ascend Toolkit | 7.0+ | NPU 驱动与 ATC 转换工具 |
| Python | CPython | 3.8 ~ 3.10 | 应用开发语言 |
| 深度学习 | PyTorch + torchvision | 2.0+ | ResNet18 模型 / CPU 推理回退 |
| 图像处理 | OpenCV | 4.8+ | 车道线检测 + 图像预处理 |
| Web | Gradio | 4.0+ | 感知结果展示界面 |
环境准备
# 一键安装依赖并导出模型
bash setup.shsetup.sh 依次完成:系统包安装 -> Python 依赖安装 -> ONNX 模型导出(如 有 CANN 则继续转换为 OM)。
如果只想在开发机上导出 ONNX:
python3 prepare_models.py --onnx-onlyPython 依赖说明
| 包 | 用途 | 必需? |
|---|---|---|
gradio | Web 界面框架 | 是 |
torch + torchvision | ResNet18 模型定义、CPU 推理回退 | 是 |
opencv-python | 车道线检测(Canny/Hough)、图像缩放 | 是 |
numpy | 数值计算、softmax | 是 |
Pillow | Gradio 图像格式转换 | 是 |
onnx | ONNX 模型校验(仅 prepare_models.py) | 仅转换 |
acl (CANN 自带) | NPU 推理,随 CANN 安装 | 仅推理 |
2.3. 车道线检测原理
车道线检测是一个经典的计算机视觉问题。车道线本质上是图像中的直线 或平滑曲线,具有明显的边缘特征和固定的几何约束(位于画面下半部 分,大致呈八字形)。这些特征使得经典 CV 方法——Canny 边缘检测 + Hough 直线变换——比深度学习更直接有效。
为什么不用深度学习做车道线检测
| 方法 | 优势 | 劣势 |
|---|---|---|
| 经典 CV (Canny + Hough) | 无需训练数据、速度快、可解释、参数可调 | 对复杂场景(阴影、弯道)敏感 |
| 深度学习 (UNet / LaneNet) | 对复杂场景鲁棒 | 需要标注数据、模型大、OM 转换复杂 |
对于昇腾 310B 上的智能小车应用,经典 CV 是更合理的选择:不需要额外 的标注数据,不占用 NPU 资源(NPU 留给场景分类),处理速度极快。
检测流水线
每一步的实现都在 lane_detector.py 的 LaneDetector 类中。关键步骤说明:
Canny 边缘检测:检测图像中亮度剧烈变化的位置(车道线通常是白色或 黄色的,与深色路面形成强烈对比)
ROI 蒙版:只保留图像下半部分(通常是路面区域),过滤掉天空、建筑 物等干扰
Hough 直线变换:将边缘图中的点映射到参数空间,找到共线的点群——即 直线段。概率 Hough 变换 (
HoughLinesP) 直接返回线段端点,比标准 Hough 变换更高效斜率过滤:根据斜率的正负号分离左右车道线(左车道斜率为负,右车道 斜率为正),并过滤掉接近水平的线段
车道线拟合:用加权线性回归将多个短线段合并为一条完整的车道线。 以 y 为自变量(车道线接近垂直),以线段长度为权重
叠加绘制:用
cv2.addWeighted将车道线半透明叠加到原图上,并在 两条车道线之间填充绿色区域标记可行驶车道
2.4. 驾驶场景分类
车道线检测告诉小车"路在哪里",但还需要知道"是什么路"——高速公路还是 城市街道?前方是否有交叉口?这需要语义层面的理解。
本项目使用 ResNet18 对 5 种典型驾驶场景进行分类:
| # | 场景 | 特征 | 驾驶建议 |
|---|---|---|---|
| 0 | 高速公路 | 多车道、护栏、路牌 | 保持车道,注意车速 |
| 1 | 城市道路 | 建筑、行人、路边停车 | 注意行人和交叉口 |
| 2 | 交叉路口 | 路口、交通灯、斑马线 | 减速观察 |
| 3 | 停车场 | 车位线、密集车辆 | 低速行驶 |
| 4 | 隧道 | 昏暗、墙壁、灯光 | 开启车灯 |
为什么选择 ResNet18
| 模型 | 参数量 | 310B 推理 | 适用性 |
|---|---|---|---|
| ResNet18 | 11.7M | ~12ms | 推荐:轻量、torchvision 内置 |
| ResNet50 | 25.6M | ~25ms | 更准但更慢,案例7已使用 |
| MobileNetV3-Small | 2.5M | ~5ms | 案例8已使用,精度略低 |
| EfficientNet-B0 | 5.3M | ~10ms | 也可用 |
选择 ResNet18 的理由:
- 与已有案例差异化:案例7用 ResNet50 做特征提取,案例8用 MobileNetV3-Small 做手势分类,这里用 ResNet18 做场景分类——三种 不同的模型架构
- 5 分类任务足够:不需要 ResNet50 的 2048 维特征,ResNet18 的 512 维全连接层足够
- torchvision 内置:与案例7相同,权重自动从 PyTorch CDN 获取
场景分类流程
预处理步骤与案例7、案例8完全一致:缩放、色彩转换、ImageNet 归一化、 维度重排。
2.5. 模型转换与昇腾部署
步骤 1:导出 ONNX
python3 prepare_models.py --onnx-only这一步构建 ResNet18 模型,将原本 1000 类的 ImageNet 分类头替换为 5 类 驾驶场景分类头,然后用固定输入形状 (1, 3, 224, 224) 导出 ONNX。
步骤 2:ATC 转换
# 在昇腾设备上运行
python3 prepare_models.py等效的 atc 命令:
atc --model=models/resnet18_scene.onnx \
--framework=5 \
--output=models/resnet18_scene \
--soc_version=Ascend310B4 \
--input_format=NCHW \
--input_shape=input:1,3,224,224关于训练权重
本项目的 prepare_models.py 会生成一个基线 .pth 文件(ImageNet 骨干
- 随机初始化的 5 类分类头)。这个基线模型的预测是随机的——要获得有 意义的场景分类结果,需要在驾驶场景数据集上训练。
这是因为:
- 手动标注 5 类驾驶场景数据集超出了本书的范围
- 训练流程已在案例8中完整演示(HaGRID + MobileNetV3-Small)
- 读者可以参考案例8的
train.py模式,用驾驶场景数据集(如 BDD100K、 KITTI)自行训练
CPU 回退模式下,SceneClassifier 会自动检测 .pth 文件,加载训练好的 权重。如果只有基线模型,会在启动时打印警告。
2.6. Web 界面
两个页签
道路感知:上传道路图像(或使用示例图片),系统同时执行车道线检测 和场景分类。左侧显示输入图像,右侧显示感知报告(车道检测状态、场景 标签、置信度进度条、Top-3 预测、驾驶建议),下方显示车道线叠加效果图。
系统信息:展示当前推理后端(NPU/CPU)、模型文件状态、5 类场景列 表及驾驶建议、车道线检测参数(Canny 阈值、Hough 阈值等)。
双后端
与案例7、案例8相同,SceneClassifier 使用懒加载 + 自动后端检测:
_classifier = None
def get_classifier():
global _classifier
if _classifier is None:
_classifier = SceneClassifier() # 自动检测 NPU/CPU
return _classifier2.7. 用户手册
2.7.1 部署
- 将项目代码拷贝到昇腾 310B 设备
- 运行
bash setup.sh安装依赖并导出模型 - 启动服务:
python3 app.py - 浏览器打开
http://<设备IP>:7860
2.7.2 使用
- 在「道路感知」页签上传一张包含道路的图像
- 观察车道线检测结果——绿色线条应覆盖路面上的车道标线
- 查看右侧的场景分类结果和驾驶建议
- 如果车道线检测不理想,可以在 config.py 中 调整参数
2.7.3 调整车道线参数
| 参数 | 默认值 | 调高效果 | 调低效果 |
|---|---|---|---|
CANNY_LOW/HIGH | 50 / 150 | 减少边缘噪点 | 检测更多边缘 |
HOUGH_THRESHOLD | 50 | 只保留强直线 | 检测弱直线 |
HOUGH_MIN_LINE_LEN | 40 | 过滤短线段 | 保留短线段 |
HOUGH_MAX_LINE_GAP | 100 | 容忍更大断连 | 不连接断裂线 |
ROI_TOP | 0.6 | ROI 更小(忽略远处) | ROI 更大(包含远处) |
2.7.4 故障排除
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 车道线检测不到 | 图像中无清晰车道线 | 使用有标线的道路图像,调整 Canny 阈值 |
| 车道线位置偏差大 | 弯道 / 虚线 | Hough 直线模型对弯道敏感,可以降低 HOUGH_THRESHOLD |
| 场景分类随机 | 未加载训练权重 | 需要在驾驶场景数据集上训练,或使用案例8的模式训练 |
| NPU 初始化失败 | CANN 环境未配置 | source /usr/local/Ascend/ascend-toolkit/set_env.sh |
| ATC 转换失败 | soc_version 不匹配 | npu-smi info 查看版本 |
2.7.5 如何训练场景分类模型
参考案例8的 train.py 模式:
- 准备驾驶场景数据集(按 5 个类别分文件夹存放图像)
- 修改案例8的
train.py,将模型替换为 ResNet18,类别数改为 5 - 训练完成后将
.pth文件放到models/resnet18_scene.pth - 运行
python3 prepare_models.py --force重新导出 ONNX/OM
2.7.6 与案例2的配合
案例2提供了完整的 SSD 目标检测 + DeepSORT 跟踪。读者可以将案例2的 ssdlite/ 模块集成到本案中,实现:
- 车道线检测(本案,经典 CV)
- 目标检测(案例2,SSD on NPU)——检测行人、车辆、交通标志
- 场景分类(本案,ResNet18 on NPU)——判断道路类型
- 目标跟踪(案例2,DeepSORT)——跟踪移动物体
这四项共同构成一个完整的智能小车视觉感知栈。
3. 源代码结构
case6/
├── app.py # Gradio Web 界面入口
├── lane_detector.py # 经典 CV 车道线检测
├── scene_classifier.py # NPU/CPU 双后端场景分类
├── config.py # 配置常量
├── prepare_models.py # ONNX 导出 & ATC OM 转换
├── setup.sh # 一键环境安装
├── requirements.txt # Python 依赖列表
├── data/
│ └── .gitkeep
├── models/ # 模型文件目录 (.pth / .onnx / .om)
└── README.md # 快速开始指南模块间的调用关系:
与之前案例的继承关系:
AscendResource/AscendModel沿用案例8的同一套模式config.py的结构与案例7/案例8一致(BASE_DIR+os.path.join)app.py的 Gradio 懒加载模式与案例7/案例8/案例9一致prepare_models.py的 ONNX -> ATC 流程与案例7/案例8一致LaneDetector是本书第一个纯经典 CV 类——无需任何模型文件,纯 OpenCV 实现- 本项目不需要训练脚本——场景分类头尚未训练,但推理框架完整,参考 案例8即可完成训练
4. 效果演示
预期效果
| 功能 | 预期表现 | 备注 |
|---|---|---|
| 车道线检测 | 清晰标线检测率 > 90% | 对弯道、虚线、阴影敏感 |
| 场景分类 (已训练) | 5 类准确率 > 85% | 需要训练,基线模型随机 |
| 端到端处理时间 | 车道线 <5ms + 推理 ~12ms (NPU) | 总计 ~20ms |
| 车道线检测范围 | 画面下部 40% 区域 | ROI 参数可调 |
性能指标
| 指标 | NPU (Ascend 310B) | CPU (PyTorch) |
|---|---|---|
| 车道线检测 | <5 ms | <5 ms |
| 场景分类 | 10-15 ms | 25-40 ms |
| 模型大小 (.om) | ~45 MB | N/A |
浏览器中的效果
Gradio 界面在浏览器中的预期布局:
┌──────────────────────────────────────────────────────┐
│ 智能小车视觉感知 │
│ 车道线检测 (经典 CV) + 驾驶场景分类 (ResNet18 on NPU) │
├──────────────────────────────────────────────────────┤
│ [ 道路感知] [ 系统信息] │
│ │
│ ┌──────────────────────┬────────────────────────┐ │
│ │ │ 感知结果 │ │
│ │ │ │ │
│ │ 输入道路图像 │ 车道线检测 │ │
│ │ (上传或拍摄) │ - 左车道: 是 右车道: 是 │ │
│ │ │ - Hough 线段数: 47 │ │
│ │ │ │ │
│ │ │ 驾驶场景分类 │ │
│ └──────────────────────┤ - 城市道路 (urban) │ │
│ │ 置信度: 87.3% │ │
│ ┌──────────────────────┤ ██████████████░░░ │ │
│ │ │ 注意行人和交叉口 │ │
│ │ 车道线叠加结果 │ │ │
│ │ (绿色线条 + 填充区域) │ Top-3 预测 │ │
│ │ │ -> 城市道路: 87.3% │ │
│ │ │ 高速公路: 8.1% │ │
│ │ │ 交叉路口: 3.2% │ │
│ │ │ │ │
│ │ │ 总耗时: 17.3 ms │ │
│ │ │ 推理后端: NPU │ │
│ └──────────────────────┴────────────────────────┘ │
└──────────────────────────────────────────────────────┘如何验证系统正常工作
- 打开浏览器访问
http://127.0.0.1:7860 - 切换到「道路感知」页签
- 上传一张包含清晰车道线的道路图像
- 确认车道线叠加图中出现绿色车道线标记
- 确认右侧感知报告中显示场景分类结果(如未训练则显示随机结果,这是 预期行为)
- 切换到「系统信息」页签,确认后端状态和参数显示正确
- 在昇腾设备上,确认后端显示"NPU (Ascend 310B)"
