【任务执行 11-15】第 11/25 课

🤖 第11课:任务规划

📌 任务规划概述

任务规划是服务机器人的大脑,将高层指令分解为可执行的原子动作序列:

🧠 三种任务表示方法

方法特点适用
行为树层次化、可复用、反应式复杂任务组合
状态机明确状态转移、可验证确定性流程
任务图依赖关系、可并行多任务调度

📌 任务依赖与调度

import heapq, json

class Task:
    def __init__(self, tid, name, priority=0, deps=None, duration=30, location=None):
        self.id = tid; self.name = name; self.priority = priority
        self.deps = deps or []; self.duration = duration; self.location = location
        self.status = "pending"  # pending/running/done/failed

class TaskPlanner:
    def __init__(self):
        self.tasks = {}; self.completed = set()

    def add_task(self, task):
        self.tasks[task.id] = task

    def plan(self):
        """基于优先级和依赖的任务规划"""
        order = []; ready = []; remaining = set(self.tasks.keys())
        
        while remaining:
            for tid in list(remaining):
                task = self.tasks[tid]
                if all(d in self.completed for d in task.deps):
                    heapq.heappush(ready, (-task.priority, tid))
            
            if not ready:
                print("⚠️ 依赖循环,无法继续")
                break
            
            _, tid = heapq.heappop(ready)
            order.append(tid)
            self.completed.add(tid)
            remaining.remove(tid)
        
        return order

    def estimate_total_time(self, order):
        """估算总执行时间"""
        total = 0
        for tid in order:
            total += self.tasks[tid].duration
        return total

planner = TaskPlanner()
planner.add_task(Task("T1", "导航到咖啡机", 3, [], 45, "1楼茶水间"))
planner.add_task(Task("T2", "取咖啡", 2, ["T1"], 20, "1楼茶水间"))
planner.add_task(Task("T3", "导航到电梯", 3, ["T2"], 30, "1楼电梯口"))
planner.add_task(Task("T4", "乘电梯到3楼", 4, ["T3"], 60, "电梯"))
planner.add_task(Task("T5", "导航到会议室A", 3, ["T4"], 40, "3楼会议室A"))
planner.add_task(Task("T6", "递送咖啡", 5, ["T5"], 15, "3楼会议室A"))
planner.add_task(Task("T7", "语音确认送达", 2, ["T6"], 10, "3楼会议室A"))
planner.add_task(Task("T8", "返回待命点", 1, ["T7"], 50, "1楼前台"))

order = planner.plan()
total = planner.estimate_total_time(order)

print("任务规划 - 基于优先级和依赖")
print("=" * 55)
for i, tid in enumerate(order):
    t = planner.tasks[tid]
    deps = ",".join(t.deps) if t.deps else "无"
    print(f"  {i+1}. [{tid}] {t.name} (优先级P{t.priority}, 依赖:{deps}, {t.duration}s, {t.location})")
print(f"\n⏱️ 总预计时间: {total}秒 ({total/60:.1f}分钟)")
print("✅ 任务规划验证通过")
✅ 验证通过 任务规划 - 基于优先级和依赖 ======================================================= 1. [T1] 导航到咖啡机 (优先级P3, 依赖:无, 45s, 1楼茶水间) 2. [T2] 取咖啡 (优先级P2, 依赖:T1, 20s, 1楼茶水间) 3. [T3] 导航到电梯 (优先级P3, 依赖:T2, 30s, 1楼电梯口) 4. [T4] 乘电梯到3楼 (优先级P4, 依赖:T3, 60s, 电梯) 5. [T5] 导航到会议室A (优先级P3, 依赖:T4, 40s, 3楼会议室A) 6. [T6] 递送咖啡 (优先级P5, 依赖:T5, 15s, 3楼会议室A) 7. [T7] 语音确认送达 (优先级P2, 依赖:T6, 10s, 3楼会议室A) 8. [T8] 返回待命点 (优先级P1, 依赖:T7, 50s, 1楼前台) ⏱️ 总预计时间: 270秒 (4.5分钟) ✅ 任务规划验证通过

📌 行为树

行为树是游戏AI和服务机器人的主流任务表示方法,支持顺序/选择/装饰器等组合:

class BehaviorTreeNode:
    """行为树节点"""
    def __init__(self, name, node_type="sequence", children=None, action=None):
        self.name = name; self.type = node_type
        self.children = children or []; self.action = action
        self.status = "idle"

    def tick(self, context=None):
        if self.type == "sequence":
            for child in self.children:
                result = child.tick(context)
                if result != "success":
                    self.status = result
                    return result
            self.status = "success"
            return "success"
        
        elif self.type == "selector":
            for child in self.children:
                result = child.tick(context)
                if result != "failure":
                    self.status = result
                    return result
            self.status = "failure"
            return "failure"
        
        elif self.type == "action":
            result = self.action() if self.action else "success"
            self.status = result
            return result
        
        elif self.type == "decorator":
            if self.children:
                result = self.children[0].tick(context)
                self.status = "success" if result == "failure" else "failure"
                return self.status
            return "failure"

# 构建配送任务行为树
log = []
def act(name, result="success"):
    def f():
        log.append(f"执行: {name} → {result}")
        return result
    return f

# 配送任务行为树
root = BehaviorTreeNode("配送任务", "selector", [
    BehaviorTreeNode("正常流程", "sequence", [
        BehaviorTreeNode("导航到取物点", "action", action=act("导航到茶水间")),
        BehaviorTreeNode("取物品", "action", action=act("取咖啡")),
        BehaviorTreeNode("导航到电梯", "action", action=act("导航到电梯口")),
        BehaviorTreeNode("乘电梯", "selector", [
            BehaviorTreeNode("电梯正常", "sequence", [
                BehaviorTreeNode("呼叫电梯", "action", action=act("呼叫电梯")),
                BehaviorTreeNode("进入电梯", "action", action=act("进入电梯")),
                BehaviorTreeNode("到达目标层", "action", action=act("到达3楼")),
            ]),
            BehaviorTreeNode("走楼梯", "sequence", [
                BehaviorTreeNode("导航到楼梯", "action", action=act("导航到楼梯")),
                BehaviorTreeNode("爬楼梯", "action", action=act("爬楼梯到3楼")),
            ]),
        ]),
        BehaviorTreeNode("导航到目的地", "action", action=act("导航到会议室A")),
        BehaviorTreeNode("递送物品", "action", action=act("递送咖啡")),
    ]),
    BehaviorTreeNode("失败处理", "sequence", [
        BehaviorTreeNode("通知管理员", "action", action=act("通知管理员配送失败")),
        BehaviorTreeNode("返回待命点", "action", action=act("返回1楼前台")),
    ]),
])

print("行为树执行模拟")
print("=" * 55)
result = root.tick()
for entry in log:
    print(f"  {entry}")
print(f"\n最终状态: {result}")
print("✅ 行为树验证通过")
✅ 验证通过 行为树执行模拟 ======================================================= 执行: 导航到茶水间 → success 执行: 取咖啡 → success 执行: 导航到电梯口 → success 执行: 呼叫电梯 → success 执行: 进入电梯 → success 执行: 到达3楼 → success 执行: 导航到会议室A → success 执行: 递送咖啡 → success 最终状态: success ✅ 行为树验证通过

📌 状态机任务执行

有限状态机(FSM)是经典且可靠的任务执行模型:

class StateMachineTask:
    """状态机驱动的任务执行"""
    def __init__(self):
        self.states = {
            "IDLE": {"start": "NAVIGATE_PICKUP"},
            "NAVIGATE_PICKUP": {"arrived": "PICK_ITEM", "failed": "ERROR"},
            "PICK_ITEM": {"success": "NAVIGATE_ELEVATOR", "failed": "RETRY_PICK"},
            "RETRY_PICK": {"retry": "PICK_ITEM", "give_up": "ERROR"},
            "NAVIGATE_ELEVATOR": {"arrived": "CALL_ELEVATOR", "failed": "TRY_STAIRS"},
            "CALL_ELEVATOR": {"arrived": "ENTER_ELEVATOR", "timeout": "CALL_ELEVATOR", "failed": "TRY_STAIRS"},
            "ENTER_ELEVATOR": {"entered": "RIDE_ELEVATOR", "door_closed": "CALL_ELEVATOR"},
            "RIDE_ELEVATOR": {"arrived": "EXIT_ELEVATOR"},
            "EXIT_ELEVATOR": {"exited": "NAVIGATE_DEST"},
            "TRY_STAIRS": {"arrived": "NAVIGATE_DEST", "failed": "ERROR"},
            "NAVIGATE_DEST": {"arrived": "DELIVER", "failed": "ERROR"},
            "DELIVER": {"success": "CONFIRM", "failed": "RETRY_DELIVER"},
            "RETRY_DELIVER": {"retry": "DELIVER", "give_up": "NOTIFY_FAIL"},
            "CONFIRM": {"confirmed": "RETURN_HOME"},
            "RETURN_HOME": {"arrived": "IDLE"},
            "NOTIFY_FAIL": {"done": "RETURN_HOME"},
            "ERROR": {"reset": "IDLE"},
        }
        self.current = "IDLE"
        self.history = []

    def transition(self, event):
        state_trans = self.states.get(self.current, {})
        new_state = state_trans.get(event, self.current)
        self.history.append((self.current, event, new_state))
        self.current = new_state
        return new_state

    def get_available_events(self):
        return list(self.states.get(self.current, {}).keys())

sm = StateMachineTask()
print("状态机任务执行模拟")
print("=" * 55)

# 模拟正常流程
events = ["start","arrived","success","arrived","arrived","entered","arrived","exited","arrived","success","confirmed","arrived"]
for e in events:
    old = sm.current
    new = sm.transition(e)
    print(f"  {old} --{e}--> {new}")

print(f"\n状态历史: {len(sm.history)}次转换")
print("✅ 状态机验证通过")
✅ 验证通过 状态机任务执行模拟 ======================================================= IDLE --start--> NAVIGATE_PICKUP NAVIGATE_PICKUP --arrived--> PICK_ITEM PICK_ITEM --success--> NAVIGATE_ELEVATOR NAVIGATE_ELEVATOR --arrived--> CALL_ELEVATOR CALL_ELEVATOR --arrived--> ENTER_ELEVATOR ENTER_ELEVATOR --entered--> RIDE_ELEVATOR RIDE_ELEVATOR --arrived--> EXIT_ELEVATOR EXIT_ELEVATOR --exited--> NAVIGATE_DEST NAVIGATE_DEST --arrived--> DELIVER DELIVER --success--> CONFIRM CONFIRM --confirmed--> RETURN_HOME RETURN_HOME --arrived--> IDLE 状态历史: 12次转换 ✅ 状态机验证通过

📌 行为树 vs 状态机

📊 对比分析

维度行为树状态机
可读性树形结构直观状态转移清晰
可复用性子树可复用状态难复用
反应性天然支持需额外设计
状态爆炸不易状态多时易爆炸
调试可视化好转移追踪好
💡 推荐:主干用状态机保证流程正确,子任务用行为树实现灵活反应。

📌 练习

📝 练习 1

实现并行节点:行为树中添加Parallel节点,所有子节点同时执行,任一失败则整体失败。

📝 练习 2

为任务规划添加中断恢复:任务执行中被新任务打断时,保存当前进度,完成后恢复。

📝 练习 3

实现动态优先级:根据等待时间自动提升低优先级任务的优先级(饥饿问题)。

📌 成就

🏆 本课成就

◀ 上一课 📚 目录 下一课 ▶