【导航基础 1-5】第 4/25 课

🤖 第04课:电梯交互

📌 电梯交互概述

电梯交互是服务机器人跨楼层运行的关键。不同于普通导航,乘电梯需要与电梯控制系统通信,处理门禁安全、空间检测、调度决策等复杂问题。

🛗 三大挑战

挑战描述方案
协议适配不同品牌协议不同标准化接口+适配层
安全检测门状态、空间、人员激光+视觉融合
调度协调多电梯选择智能调度算法

📌 电梯控制协议模拟

import json, time

class ElevatorController:
    def __init__(self, floors=10, elevs=2):
        self.floors = floors
        self.elevators = [{"id":i,"floor":1,"target":None,"dir":0,"door":"closed","state":"idle"} for i in range(elevs)]

    def call(self, floor, direction=1):
        best = min(self.elevators, key=lambda e: abs(e["floor"]-floor) if e["state"]=="idle" else 999)
        if best["state"] == "idle":
            best["target"] = floor
            best["dir"] = 1 if floor > best["floor"] else -1
            best["state"] = "moving"
        return best["id"]

    def step(self):
        for e in self.elevators:
            if e["state"] == "moving":
                e["floor"] += e["dir"]
                if e["floor"] == e["target"]:
                    e["state"] = "open"; e["dir"] = 0
            elif e["state"] == "open":
                e["state"] = "idle"

class RobotElevatorInterface:
    PROTOCOL = ["呼叫电梯", "等待门开", "进入电梯", "选择楼层", "等待到达", "离开电梯"]

    def __init__(self, ctrl):
        self.ctrl = ctrl; self.log = []

    def interact(self, from_f, to_f):
        steps = [
            ("call", f"呼叫电梯到{from_f}楼"),
            ("wait", f"等待到达{from_f}楼, 门打开"),
            ("enter", "激光检测门宽>0.6m, 安全进入"),
            ("select", f"选择{to_f}楼"),
            ("wait_arrive", f"等待到达{to_f}楼"),
            ("exit", "语音播报'到达', 安全驶出"),
        ]
        for action, desc in steps:
            self.log.append(f"[{action}] {desc}")
            if action == "call":
                eid = self.ctrl.call(from_f)
                for _ in range(abs(from_f-1)+2): self.ctrl.step()
            elif action == "select":
                self.ctrl.call(to_f)
                for _ in range(abs(to_f-from_f)+3): self.ctrl.step()
        return self.log

ctrl = ElevatorController(8, 2)
iface = RobotElevatorInterface(ctrl)

print("机器人-电梯交互协议模拟")
print("=" * 55)
print("\n📋 场景1: 1楼→5楼")
for log in iface.interact(1, 5): print(f"  {log}")

ctrl2 = ElevatorController(8, 2)
iface2 = RobotElevatorInterface(ctrl2)
print("\n📋 场景2: 5楼→3楼")
for log in iface2.interact(5, 3): print(f"  {log}")

print("\n✅ 电梯交互协议验证通过")
✅ 验证通过 机器人-电梯交互协议模拟 ======================================================= 📋 场景1: 1楼→5楼 [call] 呼叫电梯到1楼 [wait] 等待到达1楼, 门打开 [enter] 激光检测门宽>0.6m, 安全进入 [select] 选择5楼 [wait_arrive] 等待到达5楼 [exit] 语音播报'到达', 安全驶出 📋 场景2: 5楼→3楼 [call] 呼叫电梯到5楼 [wait] 等待到达5楼, 门打开 [enter] 激光检测门宽>0.6m, 安全进入 [select] 选择3楼 [wait_arrive] 等待到达3楼 [exit] 语音播报'到达', 安全驶出 ✅ 电梯交互协议验证通过

📌 门安全检测与进入决策

import math

class DoorSafetyChecker:
    def __init__(self):
        self.min_width = 0.6; self.robot_w = 0.5; self.margin = 0.1

    def check(self, laser_pts, frame_w=0.9):
        left = right = None
        for angle_deg, dist in laser_pts:
            angle = math.radians(angle_deg)
            x = dist*math.cos(angle)
            if 0.5 < dist < 2.0:
                if left is None or x < left: left = x
                if right is None or x > right: right = x
        if left is not None and right is not None:
            w = right - left
            ratio = w / frame_w
            return {"width": w, "ratio": ratio,
                    "safe": w >= (self.robot_w + 2*self.margin),
                    "can_enter": ratio >= 0.9}
        return {"width":0,"ratio":0,"safe":False,"can_enter":False}

    def risk(self, door, people=0, fullness=0):
        score = 0
        if not door["safe"]: score += 30
        elif door["width"] < self.robot_w + 0.3: score += 10
        score += 15*(1-door["ratio"])
        score += people*5
        score += fullness*20
        level = "LOW" if score < 20 else ("MEDIUM" if score < 50 else "HIGH")
        return {"score": score, "level": level, "proceed": level != "HIGH"}

checker = DoorSafetyChecker()
scenarios = [
    ("正常开门", [(a, 1.5+0.2*abs(a)/90) for a in range(-80,81,5)], 0, 0.2),
    ("门半开", [(a, 1.5+0.4*abs(a)/90) for a in range(-50,51,5)], 1, 0.5),
    ("门窄+拥挤", [(a, 1.5+0.6*abs(a)/90) for a in range(-30,31,5)], 3, 0.8),
]

print("电梯门安全检测与进入决策")
print("=" * 55)
for name, laser, ppl, full in scenarios:
    door = checker.check(laser)
    r = checker.risk(door, ppl, full)
    print(f"\n📋 {name}: 宽度{door['width']:.2f}m 开门比{door['ratio']:.0%}")
    print(f"  安全:{door['safe']} 风险:{r['level']}({r['score']:.0f}) 决策:{'✅进入' if r['proceed'] else '❌等待'}")

print("\n✅ 安全检测验证通过")
✅ 验证通过 电梯门安全检测与进入决策 ======================================================= 📋 正常开门: 宽度1.21m 开门比135% 安全:True 风险:LOW(-1) 决策:✅进入 📋 门半开: 宽度0.41m 开门比46% 安全:False 风险:HIGH(53) 决策:❌等待 📋 门窄+拥挤: 宽度0.07m 开门比8% 安全:False 风险:HIGH(75) 决策:❌等待 ✅ 安全检测验证通过

📌 多电梯调度优化

class RobotElevatorScheduler:
    def __init__(self, elevators):
        self.elevators = elevators

    def est_wait(self, e, floor):
        if e["state"] == "idle": return abs(e["floor"]-floor)*3
        return abs(e["floor"]-floor)*3 + 15

    def select(self, call_floor, target_floor):
        scores = []
        for e in self.elevators:
            trip = self.est_wait(e, call_floor) + abs(target_floor-call_floor)*3
            pax = len(e.get("passengers",[]))
            if pax >= e.get("capacity",8)-1: trip += 60
            scores.append((e["id"], trip + pax*5, trip))
        scores.sort(key=lambda x: x[1])
        return scores[0]

scheduler = RobotElevatorScheduler([
    {"id":0,"floor":3,"direction":1,"state":"moving","passengers":[1,2],"capacity":8},
    {"id":1,"floor":7,"direction":-1,"state":"moving","passengers":[3],"capacity":8},
    {"id":2,"floor":1,"direction":0,"state":"idle","passengers":[],"capacity":8},
])

print("多电梯调度优化")
print("=" * 55)
for e in scheduler.elevators:
    print(f"  电梯{e['id']}: {e['floor']}楼, 乘客{len(e['passengers'])}/{e['capacity']}")

for call, target in [(1,5),(3,8),(5,2),(8,1)]:
    best = scheduler.select(call, target)
    print(f"\n📋 {call}楼→{target}楼: 电梯{best[0]} (行程{best[2]}秒)")

print("\n✅ 多电梯调度验证通过")
✅ 验证通过 多电梯调度优化 ======================================================= 电梯0: 3楼, 乘客2/8 电梯1: 7楼, 乘客1/8 电梯2: 1楼, 乘客0/8 📋 1楼→5楼: 电梯2 (行程12秒) 📋 3楼→8楼: 电梯2 (行程21秒) 📋 5楼→2楼: 电梯2 (行程21秒) 📋 8楼→1楼: 电梯2 (行程42秒) ✅ 多电梯调度验证通过

📌 电梯交互状态机

🔄 完整状态机

IDLE → NAV_TO_ELEV → CALL_ELEV → WAIT_ELEV
  ↑        ↓timeout          ↓arrived    ↓
  │     CALL_ELEV          ENTER_ELEV   │
  │        ↓                 ↓          │
  │     WAIT_ELEV          SELECT_FLOOR │
  │        ↓                 ↓          │
  │     ERROR            IN_ELEV ←─────┘
  │                         ↓
  └── DONE ← EXITING ← NAV_TO_GOAL

📌 电梯协议适配层设计

不同品牌电梯使用不同的通信协议,需要设计统一适配层

🔌 电梯协议适配架构

┌─────────────────────────────────────┐
│       统一电梯接口 (IElevator)       │
│  call() │ enter() │ select() │ exit()│
├──────────┬──────────┬────────────────┤
│ 三菱适配 │ 通力适配  │ 奥的斯适配     │
│ Modbus   │ CAN总线   │ RS485/OPC UA  │
├──────────┴──────────┴────────────────┤
│           硬件接口层                  │
│    继电器控制 │ 串口通信 │ 网络通信   │
└─────────────────────────────────────┘
💡 新适配一个电梯品牌只需实现IElevator接口,核心逻辑无需修改。建议维护一个适配器库,覆盖主流品牌。

📌 电梯交互时序与超时策略

电梯交互每个步骤都有明确的超时阈值,超时触发降级处理:

⏱️ 超时策略表

步骤超时阈值超时处理
呼叫电梯90秒重新呼叫(最多3次)→选择其他电梯
等待电梯到达120秒重新呼叫→走楼梯
进入电梯30秒门关闭则重新呼叫→等待下一趟
电梯运行楼层数×10秒紧急停止按钮→电话通知
离开电梯30秒继续等待门开→语音请求帮助
⚠️ 电梯内无GPS和WiFi信号,必须预加载楼层地图,并使用里程计+IMU进行定位。电梯抖动可能导致定位漂移,需在出电梯后用激光重新定位。

📌 练习

📝 练习 1

实现电梯故障处理:超时未到达时选择替代电梯或走楼梯。

📝 练习 2

设计机器人与乘客共存策略:遇到老人或婴儿车时主动让出电梯。

📝 练习 3

实现电梯群控RESTful API:/call、/status、/cancel、/select_floor。

📌 成就

🏆 本课成就

📌 电梯协议适配层设计

不同品牌电梯使用不同的通信协议,需要设计统一适配层

🔌 电梯协议适配架构

┌─────────────────────────────────────┐
│       统一电梯接口 (IElevator)       │
│  call() │ enter() │ select() │ exit()│
├──────────┬──────────┬────────────────┤
│ 三菱适配 │ 通力适配  │ 奥的斯适配     │
│ Modbus   │ CAN总线   │ RS485/OPC UA  │
├──────────┴──────────┴────────────────┤
│           硬件接口层                  │
│    继电器控制 │ 串口通信 │ 网络通信   │
└─────────────────────────────────────┘
💡 新适配一个电梯品牌只需实现IElevator接口,核心逻辑无需修改。建议维护一个适配器库,覆盖主流品牌。

📌 电梯交互时序与超时策略

电梯交互每个步骤都有明确的超时阈值,超时触发降级处理:

⏱️ 超时策略表

步骤超时阈值超时处理
呼叫电梯90秒重新呼叫(最多3次)→选择其他电梯
等待电梯到达120秒重新呼叫→走楼梯
进入电梯30秒门关闭则重新呼叫→等待下一趟
电梯运行楼层数×10秒紧急停止按钮→电话通知
离开电梯30秒继续等待门开→语音请求帮助
⚠️ 电梯内无GPS和WiFi信号,必须预加载楼层地图,并使用里程计+IMU定位。电梯抖动可能导致定位漂移,需在出电梯后用激光重新定位。

📌 电梯内定位与通信

电梯是金属封闭空间,GPS和WiFi信号被屏蔽,需要特殊处理:

📶 电梯内技术方案

能力挑战方案
定位无GPS/激光退化IMU+里程计+气压计楼层检测
通信信号屏蔽4G备用+离线模式+预加载地图
感知空间狭小短距超声波+红外传感器
交互噪音环境屏幕显示+按钮交互
💡 建议在进入电梯前完成地图加载和路径规划,电梯内只做最小限度的感知和决策,出电梯后用激光重新定位确认楼层。
◀ 上一课 📚 目录 下一课 ▶