换挡策略与扭矩需求
本页除了介绍按转速判据自动换挡的方法,还提供一个不依赖 CARLA 的离线原型:
src/ground/gear_shift_policy。它接受一条纵向速度剖面,输出挡位序列、每帧扭矩需求与四条断言结论,
可在没有仿真器、没有 GPU 的机器上直接复现。
这一页解决什么问题
车辆纵向的「该用几挡、要多少扭矩」通常是靠标定表与经验,难以复核。把两件事讲清楚就能复核:
- 换挡时机:由发动机转速决定,升挡线与降挡线之间留迟滞带,再加最短保持时间;
- 扭矩需求:按纵向力平衡反推,与发动机外特性比对,超了就说明这条剖面在这台车上做不到。
自主实现:gear_shift_policy
换挡判据
rpm(v, g) = v · ratio_g · final · 60 / (2π · r)
升挡:rpm > upshift_rpm 且 g < 4
降挡:rpm < downshift_rpm 且 g > 1
限制:距上次换挡时间 ≥ min_dwell
默认传动比 3.55 / 2.10 / 1.40 / 1.00、主减速比 4.10、车轮半径 0.31 m,于是各挡的换挡车速为:
| 挡位 | 升挡车速(2600 rpm) | 降挡车速(1500 rpm) |
|---|---|---|
| 1 → 2 | 5.799 m/s | 3.346 m/s |
| 2 → 3 | 9.803 m/s | 5.656 m/s |
| 3 → 4 | 14.705 m/s | 8.483 m/s |
| 4 → — | 20.586 m/s | 11.877 m/s |
两条线之间的距离(约 2.45 m/s)就是迟滞带。
扭矩需求
F = m·a + ½ρ·Cd·A·v² + m·g·Crr # 纵向力平衡
Tw = F · r # 车轮扭矩
Te = Tw / (ratio_g · final · η) # 发动机扭矩,负值截到 0
判定:Te ≤ max_torque(rpm)
max_torque(rpm) 用简化外特性:峰值转速以下近似平台(0.60→1.00 线性),峰值以上抛物线衰减,两端留 35% 底。
四条断言
| 断言 | 判据 |
|---|---|
挡位覆盖 |
至少用到 4 个挡 |
换挡平顺 |
相邻换挡间隔 ≥ min_dwell,且 2×min_dwell(至少 1 s)窗口内无 A→B→A |
转速区间 |
全程转速 ∈ [0.5×怠速, 红线] |
扭矩上限 |
每帧扭矩需求 ≤ max_torque(rpm) |
参数
| 参数 | 默认 | 含义 |
|---|---|---|
policy/upshift_rpm |
2600.0 | 升挡转速线 |
policy/downshift_rpm |
1500.0 | 降挡转速线 |
policy/min_dwell |
0.50 | 换挡后最短保持时间 s |
policy/idle_rpm / policy/redline_rpm |
800.0 / 6500.0 | 怠速与红线 |
profile/accel / profile/max_speed / profile/hold_seconds / profile/decel |
1.00 / 30.00 / 2.00 / 2.00 | 速度剖面四要素 |
engine/peak_torque / engine/peak_rpm |
220.0 / 4200.0 | 发动机外特性 |
gearbox/final_drive / gearbox/efficiency |
4.10 / 0.92 | 主减速比与效率 |
gearbox/ratio_1..4 |
3.55 / 2.10 / 1.40 / 1.00 | 各挡传动比 |
vehicle/mass / vehicle/wheel_radius |
1500.0 / 0.31 | 质量与车轮半径 |
sim/step / rate/publish_hz |
0.05 / 10.0 | 步长与频率 |
话题
| 话题 | 类型 | 内容 |
|---|---|---|
/gear/current |
std_msgs/Int32 |
末帧挡位 |
/gear/report |
std_msgs/String |
完整文字报告 |
/gear/curve |
std_msgs/String |
挡位条带 + 扭矩柱 |
/gear/shift |
std_msgs/String |
换挡事件表 |
运行
roslaunch gear_shift_policy main.launch
默认剖面的真实输出(1.0 m/s² 加速到 30 m/s、保持 2 s、2.0 m/s² 减速):
帧数:941,时长:47.00 s,最高车速 30.00 m/s,最高转速 3789 rpm
换挡 6 次:
t=5.80 s 1 -> 2 (5.80 m/s,2600 -> 1538 rpm)
t=9.85 s 2 -> 3 (9.85 m/s,2612 -> 1742 rpm)
t=14.75 s 3 -> 4 (14.75 m/s,2608 -> 1863 rpm)
t=41.10 s 4 -> 3 (11.80 m/s,1490 -> 2086 rpm)
t=42.80 s 3 -> 2 (8.40 m/s,1485 -> 2228 rpm)
t=44.20 s 2 -> 1 (5.60 m/s,1485 -> 2511 rpm)
断言:4 通过 / 0 失败(共 4 条)
结论:PASSED
四个反例(每条断言都能单独翻红)
| 命令 | 结果 |
|---|---|
max_speed:=5.0 |
[FAIL] 挡位覆盖 用到的挡位 [1],另一侧只跑 9.5 s |
redline_rpm:=2000 |
[FAIL] 转速区间 越界 631 帧(实测最高 3789) |
accel:=3.0 |
[FAIL] 扭矩上限 超限 162 帧(峰值需求 419.2 N·m) |
upshift_rpm:=1600 downshift_rpm:=1500 |
换挡 26 次、[FAIL] 换挡平顺 ……1.0 s 内来回换挡 20 次 |
另有两条「改了但依然合规」的对照组:upshift_rpm:=4000(升挡更晚,最高转速 3996)与
min_dwell:=2.5(换挡更保守,最短保持 2.50 s),两者都是 4 通过 / 0 失败。
离线回归测试
cd <仓库根目录>
python3 tests/test_gear_shift_policy_local.py
只用 Python 标准库,不需要 ROS、不需要 CARLA、不需要显示器;pytest 下也会真失败(不是假绿)。
与已有模块的关系
| 模块 | 算什么 | 与本页的分工 |
|---|---|---|
扭转控制 |
速度指令 → 转角 / 轮速 | 只做运动学换算 |
速度曲线规划 |
加速/匀速/减速怎么排 | 输出速度剖面,本页接它的剖面 |
刹车距离核算 |
从看见到停住要几米 | 只算制动侧 |
转向执行器滞后 |
转向执行器的滞后与回正 | 管横向执行器 |
| 本页 | 该用几挡、需要多少扭矩、够不够 | 把动力可行性变成四条断言 |
常见问题
Q:为什么用转速判据而不是车速判据? A:同样车速下不同挡位的转速差好几倍;转速判据能自动适配主减速比与轮胎半径的变化。
Q:为什么减速段扭矩是 0? A:减速由制动器承担,发动机倒拖时模型把负扭矩截到 0,避免把制动功算成发动机功。
Q:Melodic 上出现 UnicodeEncodeError?
A:Python 2.7 的 rosgraph 对非 ASCII unicode 会调 str();节点内 _log() 已统一转 byte str。
使用大模型辅助代码编写、测试编写与文档整理,提交内容由本人负责。
闭环执行(默认模式)
上面一节是开环算法:车速剖面由参数给定,反推需要多少扭矩。从 1.0.0 起节点
默认改成闭环,把「执行」这一环也模拟进去:
目标车速 ──► 比例控制器 ──► 油门/刹车 ──► 扭矩请求
│
发动机外特性限制 ◄─┘
│
牵引力 − 阻力 − 制动力
│
车速积分 ──► 转速 ──► 换挡决策 ──┐
▲ │
└────────────────────────────┘
换挡因此不再是「事后统计」,而是实时影响加速度:四挡扭矩不足就加速慢, 车速跟不上目标,跟踪误差变大。默认工况下:
- 帧数 941,时长 47.00 s,行驶 734.59 m,最高车速 29.99 m/s
- 首次升挡在 t = 6.25 s(5.80 m/s),比开环晚 0.45 s —— 就是滞后
- 跟踪误差 最大 0.90 m/s、平均 0.57 m/s,最差点出现在减速末尾 (目标 0.20 m/s、实到 1.10 m/s,因为刹车还有惯性)
- 结论
PASSED
四条断言换成:挡位覆盖 / 换挡平顺 / 跟踪误差 / 转速区间。
把红线压到 2000 rpm 可以看到闭环的另一种失败:
roslaunch gear_shift_policy main.launch redline_rpm:=2000
[FAIL] 转速区间 越界 631 帧(怠速 800,红线 2000,实测最高 3788)
结论:FAILED
想回到开环对比:roslaunch gear_shift_policy main.launch closed_loop:=false。