重卡泊车 · 障碍物感知版

NN 输入含障碍物坐标 · 动态场景 · 方案B坐标拼接 · 最多5个障碍物

← 返回重卡泊车主页NN加速版(固定场景)原版 Hybrid A*

速度 1.0×
精确输入: X Y θ₁ θ₂

障碍物列表(最多5个)

牵引车朝向 θ₁
-
挂车朝向 θ₂
-
铰接角 β
-
求解时间
-
碰撞数
-
位置误差
-
NN推理
-
障碍物数
-
输入维度
29D
💡 拖拽卡车设置起点 · 点击「编辑障碍物」拖动障碍物 · 调节滑块设置朝向

障碍物感知 NN 泊车实验

本演示采用 障碍物感知神经网络:NN 输入不仅包含起点位姿(4D),还包含障碍物坐标(25D = 5个障碍物 × 5参数),共 29 维。网络能根据不同障碍物布局生成不同的泊车轨迹,而非仅适应固定场景。

障碍物感知 NN + 3 阶段同伦精修

v3 版本的核心改进是将障碍物信息直接编码到 NN 输入中(方案B 坐标拼接),使网络能根据不同障碍物布局生成不同轨迹。求解管线在 v2 的 NN 热启动基础上增加了碰撞拒绝机制——如果精修结果仍有碰撞,自动回退到完整同伦延拓;如果同伦也无法消解碰撞,则拒绝求解而非返回危险路径。

1. 完整求解管线

浏览器(拖拽起点 + 编辑障碍物)→ Nginx /api3/ → Flask :5782 ┌─ 起点碰撞预检 ────────────────────── 起点在障碍物内 → 直接拒绝 ─┐ │ │ ├─ ① NN 预测 (~10ms) ─────────────────────────────────────────┤ │ 输入: start(4D) + obstacles_flat(25D) = 29D │ │ 输出: controls(2×80) → RK4 积分 → x_nn(4×81) │ │ 可视化: 紫色虚线 │ │ │ ├─ ② IPOPT 3阶段 mini-homotopy 精修 (budget 1+1.5+2=4.5s) ───┤ │ Stage 0: tw=1e4, cw=0 → 快速拉终端姿态 │ │ Stage 1: tw=1e4, cw=5000 → 软碰撞惩罚 │ │ Stage 2: tw=1e6, cw=1e6 → 硬终端 + 硬碰撞约束 │ │ 可视化: 蓝色实线 │ │ │ ├─ ③ 碰撞检查 ───────────────────────────────────────────────┤ │ evaluate_solution(x_refined) → collisions == 0? │ │ ├─ 是 → 返回成功 ✓ │ │ └─ 否 → ④ 同伦兜底 (剩余时间预算) │ │ ├─ 同伦消解碰撞 → 返回成功 ✓ │ │ └─ 同伦仍有碰撞 → 拒绝返回错误 ✗ │ └───────────────────────────────────────────────────────────────┘

与 v2 的关键区别:v2 的 IPOPT 精修失败时直接返回不可行解(不触发兜底);v3 增加了碰撞检查 → 同伦兜底 → 碰撞拒绝的完整安全链。这是因为 v3 面对随机障碍物布局,NN 泛化不到的场景更多,必须有可靠的回退机制。

2. 输入编码:方案B 坐标拼接

采用方案B(坐标拼接):每个障碍物用 5 个参数 (cx, cy, w, h, θ) 描述,最多 5 个障碍物,不足补零。所有输入在送入网络前做 z-score 归一化(减均值除标准差),这在 v2 中未实现。

NN输入 (29D) = [x, y, θ₁, θ₂] (4D) ‖ [obs₁: cx,cy,w,h,θ] (5D) ‖ obs₂ (5D) ‖ … ‖ obs₅ (5D)
归一化: input = [(start − μ_start) / σ_start, (obs − μ_obs) / σ_obs]
分量维度含义
起点位姿4x, y, θ₁, θ₂ — 卡车起始位置和朝向
障碍物 1-525每个 5 参数:中心点(cx,cy)、宽高(w,h)、旋转角(θ)
填充0不足 5 个障碍物时补零
总计29固定长度,无需变长网络结构

3. 神经网络架构

与 v2 完全相同的 ResNet MLP 架构,仅输入维度从 4D 改为 29D:

输入: [start(4D) | obstacles(25D)] = 29D → z-score 归一化 │ ▼ Linear(29 → 256) + GELU [256] │ ▼ ResBlock × 5 (fc₁→GELU→LN→fc₂→残差相加→GELU) [256] │ ▼ Linear(256 → 160) → reshape(2, 80) │ δᵢ = tanh(·) × 0.40 rad ← 方向盘 │ vᵢ = tanh(·) × 2.0 m/s ← 速度 ▼ 输出: 80 步控制指令 [δ₁,v₁, ..., δ₈₀,v₈₀]
类型输入→输出参数量
① 输入映射Linear + GELU29 → 2567,680
② ResBlock × 5残差块(fc+LN+残差)256 → 256659,200
③ 输出映射Linear + tanh缩放256 → 16041,120
总计≈ 708,000 (0.71M)

残差块结构(与 v2 相同):h = GELU(fc₁(x)); h = LayerNorm(h); h = fc₂(h); out = GELU(x + h)。输入直接跳过两层加到输出(残差连接),保证梯度可以无损传回。

归一化差异:v3 在训练和推理时都使用 z-score 归一化(norm_stats_v3.json 存储了 start 和 obstacles 各自的均值/标准差)。v2 虽然训练时也计算了归一化统计量,但推理代码从未读取使用——这是 v3 相对 v2 的一个隐性改进。

4. IPOPT 3 阶段 mini-homotopy 精修

从 NN 预测出发,通过 3 个阶段逐步施加碰撞约束。每个阶段的解作为下一阶段的初始猜测:

阶段终端权重 tw碰撞权重 cw预算碰撞处理目标
Stage 010⁴01s无碰撞项NLP 更小更快,先把终端姿态拉到位
Stage 110⁴5,0001.5s软惩罚 (hinge²)soft penalty 把车身逐渐推离障碍物
Stage 210⁶10⁶2s硬约束 (g ≥ 0)cw ≥ 10⁶ 触发硬碰撞约束,得到零碰撞解

NLP 建模:决策变量 X(4×81) + U(2×80) = 484 维。运动学约束使用 RK4 积分(dt=0.5s, N=80, 总时窗 40s)。目标函数与 v1/v2 一致:J = 0.1·Σv² + 0.1·Σδ² + 0.5·Σ(Δδ)² + 0.5·Σ(Δv)² + tw·终端偏差。碰撞约束对每步 12 个车身采样点检查 AABB 有符号距离(含 0.5m 安全裕度)。

自适应通道约束y_front_max 根据起点距离三档切换——远点 (dist>14) 为 8.0、高近点 (y>0.5) 为 7.5、低点为 99.0(不限制),限制牵引车前端 y 坐标防止摆入上方墙。

5. 碰撞拒绝与同伦兜底

这是 v3 独有的安全机制。3 阶段精修完成后,求解器会评估最终轨迹的碰撞次数:

if evaluate_solution(x_refined).collisions > 0: │ 精修结果有碰撞 → 尝试完整同伦延拓(剩余时间预算,最多 10s) │ ├─ 同伦成功且 collisions == 0 → 使用同伦解 ✓ │ └─ 同伦仍有碰撞 → 拒绝求解,返回错误 ✗ (不返回危险路径,宁可不解也不碰) else: └─ 精修结果无碰撞 → 直接使用 ✓

同伦兜底使用与训练数据生成相同的完整 solve_homotopy_v3(4-6 阶段,按起点类型自适应),从 gen_guess_adaptive 4 段前向仿真出发求解。这是最慢但最可靠的路径——当 NN 热启动 + 3 阶段精修都无法消解碰撞时(通常是因为障碍物布局极端),回退到暴力求解。

设计哲学:v3 面对的是随机障碍物布局,NN 不可能在所有场景下都泛化到零碰撞。因此系统的首要目标不是"总能给出路径",而是"给出的路径一定安全"。碰撞拒绝机制确保用户不会收到一条会蹭到墙壁或邻车的轨迹。

6. 训练过程

项目数值说明
训练数据6,113 条6 组随机种子生成(seed 100-600),每条约 1000-2000 条
每条样本start(4D) + obstacles(25D) + X(4×81) + U(2×80)起点 + 障碍物编码 + 轨迹 + 控制序列
训练/验证比90% / 10%≈5500 训练 / ≈600 验证
损失函数L_ctrl + 0.5·L_traj + 1.0·L_coll与 v2 相同的三重约束(详见 v2 页面 §6)
优化器AdamW (lr=1e-3, wd=1e-4)CosineAnnealing 余弦退火
Batch / Epochs32 / 500梯度裁剪 max_norm=1.0
最佳模型epoch 100, val_loss=0.418保存验证集 loss 最低的 checkpoint

损失函数三项(与 v2 完全一致):
L_control(权重 1.0):MSE(预测控制序列, 真实控制序列)
L_trajectory(权重 0.5):将预测控制序列通过可微 RK4 积分成轨迹,与真实轨迹做 MSE——端到端学习
L_collision(权重 1.0):12 个车身采样点对当前样本的障碍物做 hinge² 惩罚——每条样本的障碍物不同,碰撞损失是 sample-specific 的

7. 训练数据生成

v3 的训练数据由 generate_data_v3.py 离线生成。每条样本的生成流程:随机障碍物布局 → 随机起点 → 完整同伦求解 → 质量筛选 → 保存

结构化障碍物布局random_obstacles())——不是纯随机撒点,而是模拟真实停车场结构:

障碍物生成方式位置范围尺寸范围
① 左侧邻车(必选)随机cx∈[−7,−4], cy∈[−12,−10]w∈[4,6], h∈[7,9]
② 右侧邻车(必选)随机cx∈[3,6], cy∈[−12,−10]w∈[4,6], h∈[7,9]
③ 对侧墙(n≥3 时)随机cx∈[−5,5], cy∈[8,9.5]w∈[20,35], h∈[1.5,2.5]
④ 额外车辆(0-2 个)安全区域随机左远/右远/左下/右下w∈[2,4], h∈[2,4]

起点采样:x∈[−20, 2], y∈[−2, 3], θ₁∈±20°, θ₂∈±15°,最多重试 200 次找到无碰撞起点。

求解器:使用完整 solve_homotopy_v3(CasADi/IPOPT),按起点距离自适应 4-6 阶段(远点 6 阶段 timeout=40s,高近点 4 阶段 timeout=25s,低点 4 阶段 timeout=20s)。

质量门:collisions==0 且 θ₁误差<0.5° 且 θ₂误差<0.5° 且 U_sol≠None 才保存。6113 条数据全部满足此标准。

8. 与 v2 的对比

特性v2(固定场景)v3(障碍物感知)
NN 输入4D (起点)29D (起点 + 障碍物)
输入归一化未使用(死代码)z-score 归一化 ✓
障碍物硬编码 3 个 (WALL+CAR_L+CAR_R)动态输入,最多 5 个,用户可编辑
IPOPT 精修2 阶段 mini-homotopy3 阶段 mini-homotopy
碰撞处理精修失败返回不可行解同伦兜底 + 碰撞拒绝 ✗
训练数据756 条,固定场景6,113 条,6组随机种子,随机障碍物
最佳 val_loss0.328 (epoch 225)0.418 (epoch 100)
网络参数0.70M (4→256→160)0.71M (29→256→160)
求解器端口:5781:5782
API 路径/api2/solve/api3/solve
为什么选方案B?对停车场场景(障碍物都是矩形车辆/墙壁),坐标拼接最实用:①网络结构几乎不变(只改输入维度 4→29);②训练数据生成速度快(同一套 IPOPT 求解器,只改障碍物随机化);③6113条数据(6组随机种子)训练,泛化良好;④推理开销可忽略(25维额外输入对 MLP 来说几乎免费)。
v3 的 val_loss (0.418) 比 v2 (0.328) 高?这是预期的——v3 的任务更难:NN 需要同时学习起点→轨迹映射障碍物→轨迹避让关系,29D 输入空间比 4D 大得多。但 v3 有碰撞拒绝兜底,实际部署精度不逊于 v2。