共计 2821 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点:为什么施工区域是自动驾驶的难题
施工区域对自动驾驶系统来说是个典型的『长尾场景』,我刚开始接触 Apollo 时,最头疼的就是这类动态变化的道路环境。根据实际项目经验,主要面临三大挑战:

-
几何结构突变:临时围栏或锥桶会突然改变车道宽度,甚至完全封闭原有车道。有次测试时,我们的车辆就因为没及时检测到倾斜放置的锥桶,差点蹭到施工人员
-
感知目标特殊:施工标志牌样式千奇百怪,有些区域会用简易反光条代替标准锥桶。更麻烦的是移动设备——我就遇到过识别系统把工人手持的警示灯误判成交通信号灯的情况
-
决策复杂度高:需要同时考虑交通规则(如限速 30km/h)和动态避障。某次实车测试中,决策模块在连续变道时产生了『乒乓效应』——车辆在两条可行路径间反复摇摆
技术方案解析
感知模块:施工元素检测实战
Apollo 的感知子系统采用多传感器融合方案,这里分享下我们调优后的配置:
-
相机检测:基于 YOLOv5 的施工标志检测(代码片段见后文),关键是要增强数据集中非标准标志的样本
-
激光雷达处理:
# 锥桶点云聚类示例 def cluster_cones(points): # 使用 DBSCAN 处理地面分割后的点云 clustering = DBSCAN(eps=0.3, min_samples=5).fit(points) # 过滤掉过大的聚类(可能是其他障碍物)return [c for c in clusters if 0.5 < c.volume < 2.0] -
跨模态校验:当相机和雷达检测结果冲突时,我们设置了置信度加权策略。比如夜间场景下会适当提高雷达的权重
决策模块:状态机设计与路径规划
施工区域通行本质上是个有限状态机问题,我们的实现方案:
stateDiagram
[*] --> NORMAL_DRIVING
NORMAL_DRIVING --> DETECTION: 识别到施工标志
DETECTION --> LANE_CHANGE: 需要变道绕行
LANE_CHANGE --> TRAVERSE: 进入施工区域
TRAVERSE --> NORMAL_DRIVING: 通过施工区
关键点在于 LANE_CHANGE 状态的退出条件判断:
- 使用 Frenet 坐标系计算横向偏移量
- 当偏移量持续 3 秒小于 0.2m 时确认变道完成
- 加入历史路径平滑处理防止抖动
控制模块:速度控制策略
施工区域的速度调整不是简单的全局限速,我们的策略是:
-
基于安全距离的动态调整:
double adjustSpeed(const Obstacle& nearest_cone) { const double safe_distance = 5.0; // 米 double deceleration = (current_distance - safe_distance) * 0.3; return std::max(min_speed, current_speed + deceleration); } -
通过 MPC 控制器实现加速度平滑,避免急刹车导致乘客不适
代码实现关键片段
Python 版施工标志检测
class ConstructionSignDetector:
def __init__(self, model_path):
self.model = load_yolov5_model(model_path)
# 特别注意以下类别 ID 可能因训练集不同而变化
self.target_classes = [3, 8] # 3: 警告标志 8: 施工设备
def filter_results(self, detections):
"""处理 YOLO 原始输出,过滤非施工相关目标"""
valid_dets = []
for det in detections:
if det.cls in self.target_classes and det.conf > 0.6:
# 施工标志通常出现在特定区域
if self._is_road_side(det.bbox):
valid_dets.append(det)
return valid_dets
C++ 路径重规划核心逻辑
bool reroute_path(const std::vector<Cone>& cones) {
// 构建虚拟边界
std::vector<Point> virtual_boundary;
for (const auto& cone : cones) {if (cone.type == ConeType::LEFT_BOUNDARY) {virtual_boundary.push_back(cone.position);
}
}
// 在 Frenet 坐标系下生成参考线
auto frenet_path = FrenetOptimalTrajectory::plan(
current_pose,
virtual_boundary,
SPEED_LIMIT);
// 碰撞检查
return !check_collision(frenet_path, cones);
}
性能优化实战技巧
识别准确率提升
- 数据增强秘诀:
- 对施工标志做模拟贴图增强,特别是反光材质的效果模拟
-
收集雨雾天气下的锥桶数据(这类场景误检率通常高 30%)
-
时序滤波:
# 使用卡尔曼滤波稳定检测结果 kf = KalmanFilter(dim_x=4, dim_z=2) for det in raw_detections: kf.predict() kf.update(det.position) if kf.converged: stable_detections.append(kf.x)
实时性保障
- 在激光雷达处理中采用体素网格下采样(voxel size 建议 0.1m)
- 对决策模块进行异步化改造,路径规划周期从 100ms 优化到 50ms
新手避坑指南
误检问题排查清单
遇到误检时,建议按以下顺序检查:
- 传感器标定是否偏移(特别是雷达 - 相机外参)
- 训练数据是否存在标注漏标(常见于夜间低亮度图像)
- 动态物体过滤阈值是否合理(如飘动的警示旗)
路径震荡解决方案
我们项目中的有效方法:
- 在 Frenet 轨迹规划中增加历史路径代价项
- 变道决策设置最小持续时间(建议≥2 秒)
- 控制模块增加一阶滞后滤波:
new_steering = 0.7 * current_cmd + 0.3 * last_cmd
实践工具链推荐
调试利器
- Dreamviewer:可视化查看感知结果和决策状态
- CyberRT:使用
cyber_monitor观察模块通信延迟
测试用例设计
建议构建以下典型场景:
- 渐进式锥桶摆放(测试系统反应距离)
- 部分遮挡的施工标志(测试感知鲁棒性)
- 雨雾天气下的夜间场景(综合压力测试)
写在最后
施工区域处理能力的提升往往需要大量路测迭代。建议新手先从仿真环境入手,使用 Apollo 的 LGSVL 适配器进行大规模场景测试。我们团队的经验是:当系统能在仿真中连续 100 次无干预通过随机生成的施工场景时,再上实车测试会更稳妥。
遇到问题时,多利用 Apollo 社区的资源——很多坑其实前辈们已经踩过。比如锥桶聚类的最佳参数组合,在 GitHub 的 issue 里就有详细讨论。记住,好的自动驾驶系统不是一次写对的,而是在不断试错中进化出来的。
