【智能车】电磁组进阶之路(六):攻克“赛道之王”——环岛七状态机完全解析
前言
如果说弯道是物理题,那环岛就是一道复杂的 逻辑题。
很多车在环岛路口像无头苍蝇一样打转,或者干脆拒不入环。本质原因是:环岛是一个 “时空干扰项” 。在入环、环内、出环的瞬间,传感器采集到的磁场信号极其相似。只用简单的
if-else是搞不定的。本文将分享一套成熟的 “七状态机” 策略,让小车清楚地知道自己每一步该干什么。
一、为什么环岛必须用状态机?
环岛难在 “信号畸变”。
在入环路口,直道磁场和环形磁场叠加,会导致信号发散甚至瞬间归零。如果你指望 PID 自动把你带进去,大概率会冲出赛道,或是世界忽略环岛。
🛠️ 状态机的作用
将环岛拆解为 7 个独立的逻辑阶段(看环 → 进环 → 跑环 → 出环)。
在每个阶段,我们不再完全依赖不稳定的 PID,而是 强制注入 人工设定好的转向补偿 (Steer_Offset),“强行”接管 小车的动作。
二、环岛七阶段详解 (核心逻辑)
我们将环岛过程拆解为以下 7 个状态:
1. 🕵️ 阶段 0:空闲寻找 (RING_IDLE)
- 状态描述:系统的默认扫描赛道。小车在正常循迹的同时,后台静默扫描环岛特征。
- 触发条件:
- 纵向差异:左/右纵向电感差值巨大(如
rightshu - leftshu > 45)。 - 信号强度:横向信号总值足够强(防止假阳性误判)。
- 纵向差异:左/右纵向电感差值巨大(如
- ⚡ 核心动作:锁定环岛方向(左环/右环),标记
Direction_Factor。
2. 👁️ 阶段 1:发现环岛 (RING_DETECTED)
- 状态描述:刚看到环岛。此时绝对不能猛转,否则会直接撞上内侧路牙。
- ⚡ 核心动作:
给一个 微小的初始补偿 (Steer_Offset),引导车头 微微向环岛口靠拢,调整姿态准备切入。
3. ⚠️ 阶段 2:入环引导 (RING_ENTER_GUIDE) —— [最危险时刻]
- 状态描述:车头抵达分叉口,磁场信号严重畸变甚至消失。
- 触发条件:横向信号剧变或纵向信号瞬间归零。
- ⚡ 核心动作:强制“强攻”!
彻底丢掉 PID 的控制权,直接给一个 较大的强制转向增益(如Steer_Offset = -10),把车头硬生生 “拽” 进环里。
4. 🔄 阶段 3:环内循迹 (RING_INSIDE)
- 状态描述:进来了!此时车身已回正。
- ⚡ 核心动作:
- 清除补偿:恢复正常 PID 跑线。
- 开启雷达:死盯着 出口特征(对应侧纵向电感再次大幅抬升)。
5. 🎯 阶段 4:出口检测 (RING_EXIT_DETECT)
-
状态描述:锁定出口,准备离场。
-
⚡ 核心动作:
给一个 反向的小补偿(Reverse Offset)。🤔 为什么要反向?
以右环为例,当车来到出口时,传感器采集到的磁场信号特征 极其类似一个左转弯信号。- 风险:如果此时完全信赖 PID,车子可能会被误导向内侧猛打,或者直接冲出赛道。
- 对策:必须人为给一个 “反向力”(即反向的转向补偿),抵消掉这个错误的弯道特征,帮车头摆正。
6. ⚓ 阶段 5:出环进行时 (RING_EXITING)
- 状态描述:正在切出环岛。
- ⚡ 核心动作:
维持反向补偿(增加阻尼),强迫车身快速拉正。目的:防止车尾甩尾,或者因为信号余波再次被吸回环里。
7. ❄️ 阶段 6:完成清理 (RING_DONE)
- 状态描述:完事收工。
- ⚡ 核心动作:
- 清空所有补偿,重置标志位。
- 开启 500ms 冷却期:在这段时间内,物理屏蔽环岛判定。这是防止“二次进环”的最强保障。
三、C 代码框架参考
typedef enum {
RING_IDLE, // 0巡线
RING_DETECTED, // 1发现
RING_ENTER_GUIDE, // 2强制进入
RING_INSIDE, // 3环内
RING_EXIT_DETECT, // 4瞄准出口
RING_EXITING, // 5强行出环
RING_DONE // 6冷却
} ring_state_t;
// ---------------------------------------------------------
// ⚔️ 主逻辑(建议放在 10ms 定时中断或主循环中)
// ---------------------------------------------------------
if (Ring_Delay > 0) {
Ring_Delay--; // ⏳ 状态锁:在延时期间,状态机“冻结”,维持当前动作
// ⚠️ 注意:若中断周期为10ms,Ring_Delay设为10就是冻结0.1s
}
else if (ring_cool_down_counter > 0) {
ring_cool_down_counter--; // 出环冷却
}
else {
switch (Ring_State) {
// ---------------- [阶段 0] 寻找特征 ----------------
case RING_IDLE:
// 右环判定:右纵强于左纵(差值>45),且横向总信号强度够(>120)
if ((rightshu_adc - leftshu_adc > 45) && (left_adc + right_adc > 120)) {
Ring_State = RING_DETECTED;
Direction_Factor = 1; // 1为右环,-1为左环
Ring_Delay = 50; // 滤震防抖
}
// (左环判定逻辑同理,省略...)
break;
// ---------------- [阶段 1] 引导段 ----------------
case RING_DETECTED:
// 动作:微微靠拢环岛内侧
Steer_Offset = -Direction_Factor * 10;
// 跳转:给予 250ms 的调整时间,然后进入下一阶段
Ring_Delay = 25;
Ring_State = RING_ENTER_GUIDE;
break;
// ---------------- [阶段 2] 强攻入环 (最关键) ----------------
case RING_ENTER_GUIDE:
// 触发:横向差剧变(跨线) OR 纵向瞬间变弱(路口中空)
if (fabsf(right_adc - left_adc) > 30 || (rightshu_adc + leftshu_adc < 30)) {
// 动作:不再信 PID,直接给死命令“拽”进去
Steer_Offset = -Direction_Factor * 15;
Ring_Delay = 30; // 盲开 300ms,利用物理惯性进环
Ring_State = RING_INSIDE;
}
break;
// ---------------- [阶段 3] 环内巡航 ----------------
case RING_INSIDE:
Steer_Offset = 0; // 恢复正常 PID
// 触发:寻找出口特征(纵向再次突增)
if ((rightshu_adc > 50) && (leftshu_adc + rightshu_adc > 70)) {
// 找到出口给反向补偿,防止冲出
Steer_Offset = -Direction_Factor * 13;
Ring_State = RING_EXIT_DETECT;
}
break;
// ---------------- [阶段 4] 瞄准出口 ----------------
case RING_EXIT_DETECT:
// 触发:横向电感恢复对称,说明对准了直道
if ((fabsf(left_adc - right_adc) < 40) && (leftshu_adc + rightshu_adc >= 60)) {
// 动作:反向打角(阻尼),防止甩尾
Steer_Offset = 3 * Direction_Factor;
Ring_Delay = 30;
Ring_State = RING_EXITING;
}
break;
// ---------------- [阶段 5] 彻底离场 ----------------
case RING_EXITING:
Steer_Offset = 3 * Direction_Factor; // 维持反向补偿
// 触发:信号变弱,说明已经远离环岛磁场
if (left_adc + right_adc < 50) {
Ring_State = RING_DONE;
}
break;
// ---------------- [阶段 6] 冷却清理 ----------------
case RING_DONE:
Steer_Offset = 0; // 清零补偿!重要!
Ring_State = RING_IDLE; // 重置状态
ring_cool_down_counter = 50; // 🔥 开启 500ms 绝对屏蔽期
break;
}
}
// ---------------------------------------------------------
// 🚀 最终执行:将补偿叠加到 PID 计算结果上
// ---------------------------------------------------------
AD_Bias += Steer_Offset;
四、实战心法:代码背后的“潜规则”
1. ⚔️ 心法一:环岛必须“强攻”
❓ 痛点:关于“盲区”的强制补偿
在进入环岛的交汇处(路口),就算以最笔直的姿态进入,磁场线也会发生严重的重叠和畸变。
- 现象:若是小车此时在抖动,信号更是乱成一锅粥。此时
AD_Bias算出来的结果往往是发散的,甚至是瞬间归零的。 - 结论:不要指望 PID 能带你跑完环岛。与其处理复杂信号,不如在检测到复杂信号时,直接给一个确定的命令。
🛠️ 调整心得
这时候强制补偿 Steer_Offset 就是让小车 “闭眼强转”。
- 调参对象:你要调的不是 PID 参数,而是这个 补偿值的大小。
💡 实战建议
- 给小了:入环“切”得不够深,容易撞到内环岛心。
- 给大了:入环直接打死方向,导致车尾甩出赛道。
- 最佳策略:建议从一个小值开始往上加,直到小车能丝滑地“滑”进环里。
- 参考值:补偿增益不是乱给的。建议输出一下正常过弯道时的
AD_Bias,环岛的Steer_Offset通常和这个值差不多。
2. ⏳ 心法二:软件定时器 (Ring_Delay) 的“滤震”
-
⚡ 信号真相
赛道上的电感信号绝非教科书里完美的曲线,而是夹杂着大量高频毛刺的“脏数据”。 -
💥 致命后果:微秒级“假动作”
如果没有状态锁定(Delay),微控制器的运算速度远快于机械结构的响应速度。- 软件层:状态机可能在几毫秒内连续跳变,瞬间完成
发现->入环->出环的逻辑闭环。 - 硬件层:舵机和电机根本来不及反应(物理惯性)。
💀 结局:
你的代码以为自己已经跑完了一整个环岛,但现实中,车子因为**“过得太快,硬件没反应过来”**,直接无视环岛冲了过去。 - 软件层:状态机可能在几毫秒内连续跳变,瞬间完成
🛠️ 调整心得:给惯性留时间
这些延时是为了给 物理惯性 留时间。
你要保证在当前状态下,小车至少运行一段距离后,再去判断下一个状态。
⚠️ 注意:如果你的车速提了,这些 Ring_Delay 记得 按比例减小,否则出环会变得“迟钝”。
3. ❄️ 心法三:“冷却期”:最简单却最有用的保护
❓ 痛点:磁场的“镜像陷阱”
很多新手会忽略一个物理事实:环岛的出口,在电磁特征上几乎就是入口的“镜像”。
- 场景复现:当你出环的那一刻,车头摆正,左右电感差值减小,横向信号增强——这组信号特征,和你刚打算进环时几乎一模一样。
- 致命后果:如果系统反应太快,刚出环 0.1秒,状态机就会惊呼:“天哪,又来一个环!”,于是车子猛地掉头,再次撞向环岛(这就是经典的“回头望月”炸机现场)。
🛠️ 解决方案:设置出环冷静期(Cool Down)
所谓的冷却期,本质上是对传感器进行**“物理禁言”。
在 RING_DONE 状态触发后的 X 毫秒内,无论传感器读到了多么像环岛的信号,代码逻辑一律无视**。
🔧 调车 Tip
如果你发现你的车总是 “多次进环” 或 “出不去环”:
- ❌ 别去调灵敏度。
- ✅ 直接把这个冷却计数器 (Cool Down) 翻倍。
本文地址:https://www.vps345.com/24839.html











