7.0 KiB
7.0 KiB
激光雷达挡板避障设计
概述
VL53L0X 单点 ToF 激光测距传感器,返回沿光束方向的距离值(mm)。 当距离低于阈值时触发绕行:中线变形推向右侧,消失后保持衰减。
当前实现: 纯距离触发 + 预触发去抖,不检查视觉边界线。绕行方向固定朝右。
VL53L0X 驱动集成
驱动已编译(src/vl53l0x.cpp + lib/vl53l0x/ C API),在 CameraInit() 中初始化:
CameraInit():
if g_cfg.lidar_enable:
g_lidar_ok = g_lidar_sensor.init() // open /dev/stmvl53l0x_ranging
g_lidar_sensor.startMeasure() // 启动首次测量
else:
g_lidar_ok = false // 禁用
cameraDeInit():
if g_lidar_ok → g_lidar_sensor.stop()
硬件未连接时 init() 失败 → g_lidar_ok = false → lidar_avoid_process() 直接跳过,不影响正常行驶。
流水线位置
lidar_avoid_process() 位于 CameraHandler Step 4,在视觉巡线之后、模型推理之前:
Step 3 image_main() → left_line[], right_line[], mid_line[]
Step 4 lidar_avoid_process() → 修改 mid_line[] (绕行变形) ★
Step 5 run_model_inference()
Step 6c cone_detect_and_deform() → 也修改 mid_line[] (叠加在 lidar 之上)
lidar 先于 cone 修改 mid_line,优先级更高。cone 的 clamp 确保不超出边界。
核心算法
1. 读取激光距离(每 2 帧一次)
lidar_avoid_process():
if !g_lidar_ok || !g_cfg.lidar_enable → return
if ++lidar_skip < 2 → return // 每 2 帧一读 (~15Hz)
lidar_skip = 0
data = g_lidar_sensor.readResult() // 非阻塞读取上次测量结果
d_mm = data.RangeMilliMeter
g_lidar_sensor.startMeasure() // 立即启动下次测量 (后台 ~20ms)
2. 预触发去抖
if d_mm == 0: // 读失败, 跳过
skip
if d_mm >= g_cfg.lidar_pre: // 距离超出预触发窗口 (默认 800mm)
g_lidar_frames = max(0, frames-1) // 衰减但不归零
skip
// d_mm < lidar_pre → 进入触发判定
lidar_pre(默认 800mm)是预触发窗口:距离进入此范围就开始攒帧,但要 < lidar_thresh(默认 300mm)才真正确认。
3. 距离 → 图像行映射
row = lt_h - 1 - (d_mm - lidar_near) × (lt_h - 11) / (lidar_far - lidar_near)
row = clamp(row, 10, lt_h - 1)
| 参数 | 默认 | 含义 |
|---|---|---|
lidar_near |
50 mm | row = lt_h-1(最近行)对应的距离 |
lidar_far |
1200 mm | row = 10(最远有效行)对应的距离 |
4. 触发判定
g_lidar_frames++
g_lidar_obstacle_row = row
g_lidar_confirmed = (d_mm < lidar_thresh) && (g_lidar_frames >= lidar_min_frames)
注意: 不检查视觉边界线(near_valid / far_lost),纯距离触发。
配置中的 lidar_near_start、lidar_near_end、lidar_far_span 参数被加载但未使用(为设计预留)。
5. 确认 → 记录状态
if g_lidar_confirmed:
g_lidar_hold_ctr = 0
g_lidar_hold_src_row = g_lidar_obstacle_row
g_lidar_hold_dir = 1.0 // 固定朝右绕行
g_lidar_frames = 0 // 重置计数
方向固定为右(+1.0),不根据赛道左右空间动态判断。
中线变形
当 lidar_is_active()(确认中或保持衰减中)时,对 row 10 ~ 障碍行施加变形:
decay = (确认中) 1.0 : (1.0 - hold_ctr / hold_frames) // 保持期线性衰减
for row = 10 to g_lidar_hold_src_row:
t = clamp((row - 10) / lidar_avoid_range, 0.0, 1.0)
push = t × lidar_avoid_gain × half_w × dir × decay
mid_line[row] = clamp(mid_line[row] + push,
left_line[row] + 2.0,
right_line[row] - 2.0)
状态机
┌────────────────────────┐
│ │
▼ │
┌──────┐ 确认 ┌──────┐ 消失 ┌──────────┐ 保持结束
│NORMAL│ ─────► │AVOID │ ─────► │HOLD_DECAY│ ────────► NORMAL
│ │ │ │ │ (衰减) │
└──────┘ └──────┘ └──────────┘
▲ │
└────────────────────────────────┘ 测距恢复正常
NORMAL: 正常行驶,激光测距 > lidar_pre
AVOID: 挡板确认 → 中线变形绕行 (固定朝右)
HOLD_DECAY: 挡板消失 → 变形量线性衰减 (lidar_hold_frames 帧内归零)
衰减期间若再次检测到挡板 → 立即切回 AVOID
速度联动
motor_update() 中包含挡板减速代码(lidar_is_active() → speed × lidar_speed),
但当前已注释。绕行仅靠中线变形,不减速。如需启用,取消注释即可。
配置参数
| 文件 | 类型 | 默认 | 含义 |
|---|---|---|---|
./lidar_enable |
int(0/1) | 1 | 激光避障总开关(0=禁用,init 也不调用) |
./lidar_thresh |
int | 300 | 障碍判定距离阈值 (mm),低于此值触发确认 |
./lidar_pre |
int | 800 | 预触发窗口 (mm),低于此值开始攒帧 |
./lidar_near |
int | 50 | row=lt_h-1 对应的物理距离 (mm) |
./lidar_far |
int | 1200 | row=10 对应的物理距离 (mm) |
./lidar_min_frames |
int | 3 | 连续确认帧数 |
./lidar_avoid_gain |
double | 0.4 | 绕行中线推离力度 (归一化) |
./lidar_avoid_range |
int | 30 | 绕行斜坡陡峭度 (行数) |
./lidar_speed |
double | 0.4 | 绕行时速度倍率 (当前未生效) |
./lidar_hold_frames |
int | 30 | 挡板消失后变形保持帧数 |
./lidar_near_start |
int | 1 | (已加载但未使用,预留) |
./lidar_near_end |
int | 4 | (已加载但未使用,预留) |
./lidar_far_span |
int | 6 | (已加载但未使用,预留) |
边界情况 & 注意事项
- 激光硬件未连接 —
init()失败时g_lidar_ok = false,后续跳过,不阻塞。 - 弯道边墙误判 — 急弯处激光可能打到边墙产生近距读数。依赖
lidar_thresh+lidar_min_frames去抖抑制。 - 方向固定右绕 — 当前不判断挡板在赛道左侧还是右侧,一律朝右推中线。对大多数场景够用,但若挡板在右侧则绕行方向不理想。
- 标定建议 — 在平坦路段用 debug 模式打印
d_mm和row,确认lidar_near/lidar_far映射准确。 - VL53L0X 读取模式 — 非阻塞:先
startMeasure()启动后台测量 (~20ms),下次调用时readResult()取结果。每 2 帧一读不阻塞主循环。 - 多传感器优先级 — lidar 先于 cone 修改 mid_line。zebra/tl 的 block 刹车优先级最高(红灯/斑马线停车不绕行)。