# iM.Master SDK
### 用于逆向工程的 BLE 机器人的 Python SDK。
**使用 Python 驾驶 iM.Master 机器人。**
无需官方应用,无需配对,无需连接云端。
如果你愿意,可以把方向盘交给本地 LLM,并给它装上眼睛。
[](LICENSE)
[](https://www.python.org/downloads/)
[](docs/BLUETOOTH.md)
[](docs/BLUETOOTH.md)
[](CONTRIBUTING.md)
## 目录
- [这是什么?](#what-is-this)
- [核心亮点](#highlights)
- [安装](#install)
- [快速入门:使用 Python 驾驶](#quickstart-drive-from-python)
- [教程:AI 扩展插件完整流程](#tutorial-the-ai-addon-start-to-finish)
- [选择你的 LLM 后端](#choosing-your-llm-backend)
- [支持哪些机器人?](#which-robots-does-this-work-with)
- [SDK 逐模块详解](#the-sdk-module-by-module)
- [通信协议](#the-protocol)
- [项目结构](#project-layout)
- [测试](#testing)
- [故障排除](#troubleshooting)
- [贡献指南](#contributing)
- [安全与负责任地使用](#safety-and-responsible-use)
- [许可证](#license)
## 这是什么?
iM.Master 是一款玩具机器人,出厂时只提供了一个手机应用。没有遥控器,
没有 API,也没有文档。本 SDK 剖析了其应用使用的蓝牙通信机制,并在 Python 中重建了
整个控制链路,让你可以通过代码驾驶机器人:
```
from immaster.robot import Robot
with Robot() as bot:
bot.for_duration("forward", 2.0) # forward for 2 seconds, then auto-stop
bot.spin_cw() # spin in place
bot.stop()
```
所以这里的核心是一个纯粹的 Python 机器人接口。核心代码没有任何
依赖,完全使用纯粹的标准库。
此外还有一个可选的 AI 扩展插件。比如,将一部旧手机作为摄像头对准机器人,
在 Pi 上运行目标检测,然后让一个轻量级的本地 LLM 决定如何
移动。机器人可以自己去找一个杯子并开过去,而所有这些都直接在
设备上运行。
## 核心亮点
- 🔓 **真正的逆向工程协议。** 蓝牙低功耗 (BLE) 传统广播,company id
`0x53A6`,带有 popcount 校验和的 14 字节差速驱动帧。解码自
真实的抓包数据,并完整记录在
[docs/BLUETOOTH.md](docs/BLUETOOTH.md) 中。
- 🐍 **纯粹的 Python API。** `Robot().forward()`、`spin_cw()`、`for_duration(...)`。
支持命名动作、直接车轮控制以及连续驾驶。
- 🛡️ **安全看门狗。** 如果你的控制循环卡死或崩溃,机器人会自动停止,
而不是一头撞到墙上。
- 🧩 **零依赖核心。** 协议与机器人控制仅使用标准
库。AI 扩展功能均为按需启用。
- 🔌 **可插拔组件。** LLM 后端、摄像头和检测器分别是一个
微型接口,因此你可以接入 Ollama 或 llama.cpp,使用手机或 Pi Camera。
- 💻 **无硬件也能运行。** `DryRobot` 和 `IMMASTER_DRY_RUN=1` 让你能
在笔记本电脑上运行整个流程,甚至包括 LLM 循环。

(freed bot is a happy-spinny bot!)
## 安装
你可以使用 pip:(请在 [PyPI](https://pypi.org/project/immaster-sdk/0.1.0/) 上确认版本)
```
pip install immaster-sdk
```
或者克隆本代码仓库。
```
git clone https://github.com/2alf/immasterSDK.git
cd immasterSDK
pip install -e . # core SDK: pure standard library, no dependencies
pip install -e ".[vision]" # optional AI addon (Pillow + numpy for camera/detection)
pip install -e ".[dev]" # tests
```
## 快速入门:使用 Python 驾驶
### 1. 构建和解码数据帧(在任何地方,无需硬件)
```
from immaster.protocol import build_frame, decode_frame, Wheel
build_frame(Wheel.FWD, Wheel.FWD).hex() # 'ae4a0700000000c50000c504c399' (forward)
decode_frame(bytes.fromhex('ae4a0700000000c90000c904c399')) # (Wheel.REV, Wheel.FWD) (spin)
```
### 2. 驾驶机器人(在 Pi 上,以 root 身份)
```
from immaster.robot import Robot
with Robot() as bot:
bot.for_duration("forward", 2.0) # scripted: forward for 2s, then auto-stop
bot.spin_cw() # continuous: keeps spinning...
bot.keepalive() # ...refresh the watchdog to hold it
bot.set_wheels(1, 0) # raw per-wheel control (left fwd, right stop)
bot.stop()
```
请以 root 身份运行,因为原始 BLE 需要它:
```
sudo -E python3 my_script.py
```
移动是带状态且连续的。像 `spin_cw()` 这样的调用会设置当前的
指令并立即返回,同时后台线程会持续广播该指令。如果在大约 1.5 秒内没有任何指令刷新,看门狗就会让机器人停止。
命名动作包括 `forward`、`reverse`、`spin_cw`、`spin_ccw`、`veer_left`、
`veer_right`、`back_left`、`back_right` 和 `stop`。每个轮子只有开启、关闭或
反转状态,没有比例速度。这是机器人的协议决定的,
而不是 SDK 的限制。
### 3. 使用本地 LLM 驾驶
不需要摄像头或任何视觉硬件也能让模型来驾驶。这是完整的 AI 循环,只需大约 15 行代码,使用 [Ollama](https://ollama.com) 或任何其他
后端:
```
import json
from immaster.robot import Robot
from immaster.llm import make_llm
from immaster import agent
llm = make_llm("ollama") # or "hailo" / "openai"
messages = [{"role": "system", "content": agent.SYSTEM_PROMPT},
{"role": "user", "content": "Goal: drive forward briefly, then spin. First action?"}]
with Robot() as bot:
for _ in range(8):
reply = llm.chat(messages)
action = agent.parse_action(reply) # pull one JSON action out of the reply
if not action:
continue
result = agent.dispatch_action(bot, action) # execute it on the robot
messages += [{"role": "assistant", "content": reply},
{"role": "user", "content": f"Result: {json.dumps(result)}. Next action, or stop."}]
if action.get("action") == "stop" or action.get("tool") == "stop":
break
```
在此基础上添加摄像头和检测器,你就可以实现下一节中完整的“感知-思考-行动”循环。
## 教程:AI 扩展插件完整流程
这是本项目进行过的完全端到端的实际设置:将一部旧手机作为机器人的
眼睛,在 Pi 上进行目标检测,由一个轻量级的本地 LLM 负责选择动作。
**你需要准备什么**
- iM.Master 机器人 🤖
- 运行 Raspberry Pi OS (64 位) 的 Raspberry Pi。机器人控制仅限 Linux 系统。
- 一个摄像头,例如带有 IP 摄像头应用的旧手机(Android 或 iOS)。本项目使用
Android 上的 [IP Webcam](https://play.google.com/store/apps/details?id=com.pas.webcam) 构建。
- 本地 LLM 运行环境:[Ollama](https://ollama.com)(最简单)或 Hailo-Ollama。
- 可选,用于目标检测的:Hailo AI HAT+2 (Hailo-10H) 并安装了 Hailo SDK。
没有它,你仍然可以像 [快速入门步骤 3](#quickstart-drive-from-python) 中那样进行仅 LLM 驾驶。
**步骤 1. 将代码下载到 Pi 上**
```
git clone https://github.com/2alf/immasterSDK.git
cd immasterSDK
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[vision]"
```
**步骤 2. 确认你可以驾驶机器人(尚不涉及 AI)**
```
sudo -E python3 -c "from immaster.robot import Robot; b=Robot(); b.for_duration('forward',1.5); b.close()"
```
机器人应该会向前行驶约 1.5 秒然后停止。如果没有,请在添加任何其他东西之前查阅
[故障排除](#troubleshooting)。
**步骤 3. 将手机变成机器人的眼睛**
1. 安装一个 IP 摄像头应用并启动其服务器。记下快照 URL。对于 IP
Webcam,地址为 `http://
:8080/shot.jpg`(如果你设置了登录,请添加 `user:pass@`)。
2. 将手机安装在机器人上,面向前方。
3. 从 Pi 测试视频流:
```
export IPCAM_URL="http://user:pass@:8080/shot.jpg"
python3 -c "from immaster.camera import IPCamera; print('got', len(IPCamera().capture() or b''), 'bytes')"
```
返回的字节数不为零说明视觉系统正常。
**步骤 4. 在 Pi 上部署大脑 (Ollama)**
```
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5:1.5b
```
更倾向于在 Hailo NPU 上运行模型?请参考
[选择你的 LLM 后端](#choosing-your-llm-backend)。
**步骤 5. 在 Hailo NPU 上进行目标检测(可选)**
YOLO 检测使用 HailoRT (`hailo_platform`) 外加一个 YOLO `.hef` 模型。它们都包含
在 Hailo SDK 中(`sudo apt install hailo-h10-all`)。默认的模型路径是
`/usr/share/hailo-models/yolov11m_h10.hef`(可通过 `HAILO_HEF` 覆盖)。这是
唯一一个不在 PyPI 上的组件。没有 Hailo 硬件?请改用
[快速入门步骤 3](#quickstart-drive-from-python) 中的仅 LLM 驾驶。
**步骤 6. 干跑整个循环(不产生实际移动)**
```
IMMASTER_DRY_RUN=1 python3 -m examples.find_and_go --backend ollama "find a cup"
```
你将看到检测结果和模型选择的动作打印在终端上,
而不会碰到机器人。这是在机器人真正开动之前,检查摄像头、检测器和
LLM 是否能顺利通信的好方法。
**步骤 7. 真正运行驾驶**
```
sudo -E python3 -m examples.find_and_go --backend ollama --voice --monitor \
"find a cup and go to it"
```
- 在手机上打开 `http://:8090` 并点击 *Enable voice* 听机器人
进行语音播报。
- `Ctrl+C` 随时可以干净地停止程序,并且机器人最终一定会处于停止状态。
就这样:一个能够看见、决策并自动驾驶的廉价离线玩具。
## 选择你的 LLM 后端
大脑是可以替换的。你不会被锁定在一个运行环境上。使用 `--backend`(或
`LLM_BACKEND` 环境变量)进行选择:
```
python3 -m examples.find_and_go --backend ollama "find a cup" # real Ollama (CPU/GPU)
python3 -m examples.find_and_go --backend hailo "find a cup" # a model on the Hailo NPU
python3 -m examples.find_and_go --backend openai "find a cup" # llama.cpp / LM Studio / vLLM
```
在代码中也是同样的逻辑:
```
from immaster.llm import make_llm
llm = make_llm("hailo") # or "ollama", or "openai"
reply = llm.chat([{"role": "user", "content": "..."}])
```
| 后端 | 目标平台 | 默认 endpoint | 环境变量覆盖 |
|---------|---------|------------------|--------------|
| `ollama` | 真实的 Ollama (CPU/GPU) | `http://localhost:11434` | `OLLAMA_HOST`, `LLM_MODEL` |
| `hailo` | Hailo-Ollama(模型在 NPU 上) | `http://localhost:8000` | `HAILO_HOST`, `LLM_MODEL` |
| `openai` | llama.cpp / LM Studio / vLLM | `http://localhost:11434/v1` | `LLM_BASE`, `LLM_MODEL`, `LLM_API_KEY` |
Ollama(CPU 或 GPU)和 Hailo-Ollama(NPU 上的模型)都享有同等的优先支持。本
项目最初在 Hailo-Ollama 上运行,后来也在真实的 Ollama 上运行过,两者均可正常工作。`openai`
涵盖了任何兼容 OpenAI 的本地服务器。需要未列出的运行环境?
在 [`immaster/llm.py`](immaster/llm.py) 中继承 `LLMBackend` 类,实现 `chat()`,
并进行注册即可。
## 支持哪些机器人?
该协议是从某一台实体机器人中解码出来的,所以这里如实说明其适用范围:
- **相同产品型号:几乎肯定兼容。** 控制方案存在于
机器人的固件中。它具有固定的 BLE company id(`0x53A6`)、固定的帧结构,
以及无需配对、无设备地址的无连接广播。任何使用相同固件的
机器人都会对相同的数据帧做出反应。这里的任何内容都与具体设备的序列号或 MAC 地址无关。
- **一个副作用。** 由于这是无目标的广播,它会同时驱动 BLE 范围内的
该型号的所有机器人。无法做到只与其中一台通信。这是
该玩具本身的工作原理,而非此处的 bug。
- **不同个体间的差异。** 车轮方向,即哪个电机是“左”,
以及前进方向是否真的是向前。如果你的机器人行驶方向相反或镜像,请翻转
[`immaster/protocol.py`](immaster/protocol.py) 中 `COMMANDS` 表的映射。
- **其他或贴牌的 iM.Master 变体:未经测试。** 不同的硬件
版本可能会使用不同的 company id 或帧格式。如果你的设备没有
响应,请抓取其流量并与
[docs/BLUETOOTH.md](docs/BLUETOOTH.md) 进行比对,该文档也可作为移植指南。
因此:本项目针对的是当初被解码的那台 iM.Master,应该兼容
同型号的其他设备,但目前仅在作者自己的机器人上得到确认。
## SDK 逐模块详解
**核心部分,负责移动机器人(纯标准库):**
| 模块 | 运行位置 | 用途 |
|--------|-----------|---------|
| [`protocol.py`](immaster/protocol.py) | 任何地方 | 纯粹的帧/校验和逻辑。不涉及 BLE。可进行单元测试。 |
| [`driver.py`](immaster/driver.py) | Pi (root) | 原始 HCI 传输以及连续广播的广播器线程。 |
| [`robot.py`](immaster/robot.py) | Pi (root) | 高级 `Robot` API:命名动作和安全看门狗。 |
| [`testing.py`](immaster/testing.py) | 任何地方 | `DryRobot` 替身,用于脱离硬件的测试。 |
**可选的 AI 插件,允许本地模型进行驾驶。** 每一个动态部分都是微型接口背后的
可插拔组件,因此你可以直接加入自己的实现:
| 模块 | 接口 → 内置实现 | 用途 |
|--------|----------------------|---------|
| [`llm.py`](immaster/llm.py) | `LLMBackend` → Ollama / Hailo-Ollama / OpenAI-API | 大脑。选择一个后端或编写你自己的。 |
| [`camera.py`](immaster/camera.py) | `Camera` → `IPCamera`, `StaticImage` | 眼睛的输入。手机网络摄像头、文件或你自己的来源。 |
| [`detect.py`](immaster/detect.py) | `ObjectDetector` → `Detector` (Hailo YOLO) | 将一帧画面检测结果。可替换为任何模型。 |
| [`agent.py`](immaster/agent.py) | | JSON-action 工具规范、prompt、解析器和调度器。 |
| [`personas.py`](immaster/personas.py) | | 位于严格控制契约之上的 Persona prompt 层。 |
| [`vision.py`](immaster/vision.py) | | 图像辅助功能:亮度以及“我卡住了吗?”的帧变化提示。 |
| [`voice.py`](immaster/voice.py) | | 语音输出(浏览器音频输出)。 |
每个接口都只有一个方法。要添加大脑,请继承 `LLMBackend` 并实现
`chat(messages) -> str`。要添加摄像头,请继承 `Camera` 并实现
`capture() -> bytes`。要添加检测器,请继承 `ObjectDetector` 并实现
`detect(jpeg) -> list[dict]`。循环调用的是接口,因此任何实现
都可以在不修改循环本身的情况下直接工作。
保持代码整洁的规则是:`protocol.py` 绝不触碰硬件,因此你可以在
任何机器上构建和解码帧。所有与 BLE 交互的内容都位于
`driver.py` 之下。
## 通信协议
机器人不是通过 GATT 控制的。它是通过持续发送的 BLE manufacturer-specific
advertising packets 来驱动的。只有在数据包持续发送的情况下,车轮才会转动。
每个轮子只有开启、关闭或反转状态。协议中任何地方都
没有比例速度。这就是整个控制面,但它仍然能实现干净的差速
驱动:前进、后退、原地旋转和弧线转弯。
完整的详细文档在 [docs/BLUETOOTH.md](docs/BLUETOOTH.md) 中。
## 项目结构
```
immaster/ the SDK
protocol.py pure frame/checksum logic (core, no BLE)
driver.py raw HCI + continuous-advertising broadcaster (core, Pi)
robot.py high-level Robot API + watchdog (core, Pi)
testing.py DryRobot for off-hardware runs
llm.py LLM backends ┐
camera.py Camera sources (IPCamera, ...) │
detect.py ObjectDetector + Hailo YOLO │
agent.py JSON-action LLM dispatch ├─ optional addon
personas.py persona prompt layer │
vision.py image cues (brightness, stuck) │
voice.py voice out ┘
examples/
find_and_go.py the working see-think-act loop (camera + detection + local LLM)
docs/
BLUETOOTH.md full protocol reverse-engineering write-up
assets/ README diagrams (SVG)
tests/ pure-logic unit tests (protocol, agent, llm, camera; no hardware)
```
## 测试
```
pip install -e ".[dev]"
pytest -q
```
测试涵盖了协议、LLM 调度逻辑、后端选择和
摄像头层。全都是纯软件的,不依赖硬件,并且在 Python 3.10 到 3.12 的 CI 中全部通过。
## 故障排除
| 症状 | 原因及修复方法 |
|---------|---------------|
| `PermissionError: raw HCI requires root` | 使用 `sudo -E python3 ...` 运行。原始 BLE 需要 root 权限,而 `-E` 会保留你的环境变量。 |
| `RuntimeError: HciBroadcaster only runs on Linux` | 你不在 Linux 系统上。机器人只能从 Pi 或其他 Linux 设备上控制。解码帧、LLM 循环和测试均可在任何地方运行。 |
| 脚本运行,**机器人没有移动** | (1) 范围内的另一个 iM.Master 也做出了反应。这是广播,所以属于预期行为。(2) 你的设备车轮是镜像的;请调整 [`immaster/protocol.py`](immaster/protocol.py) 中的 `COMMANDS` 表。(3) 确保没有其他程序占用 `hci0`。 |
| `camera unreachable` / `capture()` 返回 `None` | 检查 `IPCAM_URL`,确保手机的摄像头应用正在运行,确保两者在同一个 LAN 中,并检查 URL 中是否包含任何 `user:pass@` 登录信息。 |
| LLM `Connection refused` / 超时 | 启动运行环境:针对 `--backend ollama` 运行 `ollama serve` 和 `ollama pull `,或者针对 `--backend hailo` 在 `:8000` 上运行 Hailo-Ollama 服务器。检查 `OLLAMA_HOST` / `HAILO_HOST`。 |
| `ModuleNotFoundError: hailo_platform` 或 `Detector()` 失败 | 未安装 HailoRT。安装 Hailo SDK (`sudo apt install hailo-h10-all`),或者跳过检测并使用 [快速入门步骤 3](#quickstart-drive-from-python) 中的仅 LLM 驾驶。 |
| 模型未返回动作或重复自身 | 小模型的怪癖;循环会对其稍作推动。尝试不同的 `LLM_MODEL` 或更少的步骤。 |
| 动作被缩短 | 如果没有刷新指令,看门狗会在大约 1.5 秒后停止。使用 `for_duration()`,它会自动为你刷新指令,或者在你的循环中调用 `keepalive()`。 |
## 贡献指南
欢迎各种贡献:新的示例行为、对其他基于广播驱动的 BLE 机器人的支持、新的后端、摄像头或检测器,以及感知方面的
改进。请查阅 [CONTRIBUTING.md](CONTRIBUTING.md) 和
[行为准则](CODE_OF_CONDUCT.md)。
## 安全与负责任地使用
本说明文档描述了用于你自己拥有的机器人的互操作控制路径,仅供学习和
研究。请记住,广播会驱动范围内的该型号的所有机器人。
请在开阔场地驾驶,保持看门狗开启,并时刻留意自主运行状态。
## 许可证
[MIT](LICENSE)。你可以随心所欲地使用,但不提供任何担保。本 SDK 不附属于
iM.Master 机器人的制造商,也未得其认可。该名称仅用于标识
本 SDK 所通信的硬件。