4. 决策规划基础理论:FSM、行为树、MDP 简介
各位同学,今天我们来聊聊决策规划的核心骨架。
说实话,很多刚入行的朋友容易把决策规划想得太玄乎。其实说白了,它就是一套「在什么情况下,该做什么事」的规则集合。我做了这么多年自动驾驶,见过太多项目在决策模块上翻车——不是逻辑太死板,就是状态乱跳。所以今天咱们把三个最经典的理论模型掰开揉碎讲清楚。
4.1 有限状态机(FSM)—— 最朴素的决策模型
FSM 是我个人最早接触的决策模型。它的思想很简单:系统在任何时刻都处于一个「状态」中,当满足某些条件时,就跳转到另一个状态。
核心三要素:
- 状态(State):比如「直行」、「左转」、「停车」
- 事件(Event):比如「前方有车」、「绿灯变红」
- 转移(Transition):状态 + 事件 → 新状态
我在项目中遇到过这样一个坑:用 FSM 做车道保持时,状态转移条件写得太敏感。结果车辆经过一个井盖,传感器抖了一下,状态直接从「车道保持」跳到了「紧急避让」——方向盘猛打,差点出事。后来我加了一个「状态保持时间」的滤波,才稳住。
下面是一个简单的 FSM 代码示例,模拟车辆在十字路口的决策:
// 伪代码:FSM 决策引擎
enum State {
STRAIGHT,
TURN_LEFT,
TURN_RIGHT,
STOP
};
State currentState = STRAIGHT;
void updateState(SensorData data) {
switch(currentState) {
case STRAIGHT:
if (data.trafficLight == RED) {
currentState = STOP;
} else if (data.intent == LEFT) {
currentState = TURN_LEFT;
}
break;
case STOP:
if (data.trafficLight == GREEN) {
currentState = STRAIGHT;
}
break;
// ... 其他状态处理
}
}
我的经验:FSM 适合状态数量少(一般不超过 10 个)、转移逻辑清晰的场景。一旦状态超过 15 个,代码就会变成一团乱麻。你想想看,每个状态都要考虑从其他所有状态转移过来的可能性——那维护成本就太高了。
4.2 行为树(Behavior Tree)—— 更灵活的决策框架
行为树是游戏 AI 领域传过来的好东西。它用树形结构组织决策逻辑,每个节点代表一个行为或一个条件判断。
为什么说它比 FSM 灵活?因为行为树天然支持「组合」和「优先级」。你可以把「避障」作为一个子树,挂在「正常行驶」下面。当障碍物出现时,自动打断当前行为去执行避障。避障完成后,自动回到原来的行为。
我记得第一次在实车上跑行为树时,有个同事问我:「这玩意儿跟 if-else 有啥区别?」我说:「区别大了。if-else 是硬编码的,改一个条件要动整个函数。行为树是模块化的,你可以单独替换某个叶子节点,不影响其他逻辑。」
行为树的节点类型主要有:
| 节点类型 | 作用 | 返回值 |
|---|---|---|
| Sequence(序列) | 依次执行子节点,全部成功才返回成功 | Success / Failure |
| Selector(选择器) | 依次尝试子节点,找到一个成功的就返回 | Success / Failure |
| Condition(条件) | 检查某个条件是否成立 | Success / Failure |
| Action(动作) | 执行具体行为 | Running / Success / Failure |
注意:行为树的「Running」状态是它最大的优势。FSM 的状态转移是瞬间完成的,而行为树可以持续执行一个动作(比如「减速到 30km/h」),直到条件满足才切换。我曾经因为没处理好 Running 状态的超时,导致车辆在弯道里一直减速到 0——嗯,那场面挺尴尬的。
4.3 马尔可夫决策过程(MDP)—— 带不确定性的决策
前面两种方法都是「确定性」的——条件满足就转移。但现实世界充满不确定性:你无法 100% 确定前车会不会急刹,也无法 100% 知道行人会不会突然横穿。
MDP 就是用来处理这种不确定性的。它把决策建模成一个「状态-动作-奖励」的循环:
- 状态(State):当前环境的情况
- 动作(Action):你可以做的操作
- 转移概率(Transition Probability):执行动作后,到达下一个状态的概率
- 奖励(Reward):每个状态-动作对带来的即时收益
说白了,MDP 就是在问:「如果我做这个动作,未来可能得到多少奖励?」然后选那个期望奖励最大的动作。
我在项目中用过 MDP 做换道决策。举个例子:
// 简化版 MDP 换道决策
// 状态:当前车道、目标车道、前后车距离
// 动作:保持车道、向左换道、向右换道
// 奖励:安全 +10,效率 +5,碰撞 -100
double expectedReward(State s, Action a) {
double reward = 0;
// 考虑所有可能的下一状态
for (State s_next : allStates) {
double prob = transitionProbability(s, a, s_next);
reward += prob * (immediateReward(s, a, s_next) +
discountFactor * bestFutureReward(s_next));
}
return reward;
}
关键点:MDP 的难点在于「转移概率」怎么定。我刚开始做的时候,直接拍脑袋设了 0.8 和 0.2。结果仿真跑出来,车辆在路口犹豫不决——因为概率设得太平均了。后来我改用蒙特卡洛方法,从真实路采数据里统计概率,效果才正常。
4.4 三种方法的对比与选择
你可能会问:「那我该用哪个?」我的建议是:
- 简单场景(如停车场泊车):用 FSM,够用且容易调试
- 复杂场景(如城市道路):用行为树,方便扩展和维护
- 不确定性高的场景(如无保护左转):用 MDP,或者结合行为树做上层框架、MDP 做底层决策
下面这张图是我自己总结的三种方法的关系:
避坑指南:我曾经在一个项目里,上来就用 MDP 做全部决策。结果模型训练了两个月,效果还不如同事用行为树写的一周版本。后来我学乖了:先用手写规则把功能跑通,再用数据驱动的方法去优化瓶颈部分。记住,简单有效比花哨重要。
好了,今天的内容就到这里。这三种方法没有绝对的好坏,关键看你的场景和团队能力。下次遇到决策问题,不妨先问问自己:「这个场景的确定性有多高?状态数量有多少?我需要处理不确定性吗?」——答案自然就有了。