这篇在系列中的位置
上一篇我们先搭好了 Mac 工作端、树莓派硬件端和 SSH 远程连接。这一篇开始进入第一个真正的小工程: 不碰整机控制,先把一个 IMU 传感器读懂、看见、调顺。
这篇的目标不是让读者一下子学会机器人控制,而是建立一个重要习惯: 复杂系统先拆成小工具,小工具先做到可观测、可验证、可复盘。
为什么先做这个工具
Qmini 的复刻不是一上来就把所有系统接起来。对 IMU 这类传感器,最稳妥的方式是先做一个独立最小工程: 能读到数据、能确认坐标系、能直观看到姿态变化,再把它接入机器人主体。
这次我们选择轮趣 N100 IMU,先做了一个 Python + Flask + Three.js 的 Web 姿态显示器。它运行在树莓派上,Mac 浏览器通过局域网访问。
IMU 是什么
IMU 的英文全称是 Inertial Measurement Unit,中文一般叫“惯性测量单元”。这个名字听起来有点硬核,但它做的事很直观: 帮机器感知“我现在怎么动、怎么歪、朝哪里倾斜”。
你可以把 IMU 想象成一个很小的“身体感觉器官”。人闭上眼睛时,仍然能感觉自己有没有歪、有没有转圈、有没有突然加速,这是因为身体里有平衡感。机器人没有这种天然感觉,所以需要 IMU。
常见 IMU 里通常会有这些传感器:
- 加速度计: 感知加速度,也能感知重力方向。手机横过来自动旋转屏幕,就和它有关。
- 陀螺仪: 感知旋转速度。比如设备正在绕某个方向转得快不快。
- 磁力计: 感知地磁方向,有点像电子指南针。
IMU 能做什么:
- 判断设备是否水平。
- 判断设备向前倾、向后仰、向左歪、向右歪。
- 辅助机器人保持平衡。
- 辅助无人机、云台、相机防抖、运动相机和手机判断姿态。
你身边很多东西都有类似 IMU 的传感器:
- 手机和平板: 自动横竖屏、计步、游戏体感控制。
- 无人机: 判断机身姿态,保持悬停和平稳飞行。
- 运动相机和云台: 判断画面是否倾斜,用来防抖。
- 汽车和机器人: 感知转弯、倾斜、震动和姿态变化。
- VR 手柄和头显: 感知手和头的转动。
我们这次用的 N100,就是一个可以通过 USB 串口输出姿态和传感器数据的小 IMU 模块。
IMU 实际读出来的是什么
我们打开 Web 页面后,会看到一些看起来很“工程”的数据: 加速度、角速度、磁场、姿态角、四元数。它们不是玄学,每一类都有很明确的含义。
加速度计: 感知“被推”和“重力在哪”
加速度计读出来的是加速度,常见单位是 m/s²。我们页面里显示的是:
X 轴加速度
Y 轴加速度
Z 轴加速度
如果你拿着 IMU 静止不动,它其实仍然会读到一个很大的加速度,接近 9.8 m/s²。这不是因为它在跑,而是因为它感受到了重力。
可以这样理解:
- 静止放在桌面上时,IMU 会知道“重力主要压在哪个轴上”。
- 把某个轴朝天或朝地时,那个轴的数值会接近
9.8或-9.8。 - 快速晃动 IMU 时,加速度数值会变得更乱,因为这时既有重力,也有你手的运动加速度。
所以加速度计特别适合回答这些问题:
- 设备现在是不是水平?
- 哪个方向朝上?哪个方向朝下?
- 机器人是不是向前倾、向后仰、向左歪、向右歪?
这也是为什么我们最后选择用重力加速度来驱动 3D 模型。它简单、直观,而且非常适合校验俯仰和横滚方向。
陀螺仪: 感知“正在怎么转”
陀螺仪读出来的是角速度,常见单位是 rad/s,也就是“每秒转过多少角度”。页面里显示的是:
X 轴角速度
Y 轴角速度
Z 轴角速度
如果加速度计像是在回答“我现在歪向哪里”,陀螺仪更像是在回答“我正在绕哪个轴旋转,转得有多快”。
举个例子:
- 你绕 X 轴转动 IMU,X 轴角速度会明显变化。
- 你绕 Y 轴转动 IMU,Y 轴角速度会明显变化。
- 你绕 Z 轴转动 IMU,Z 轴角速度会明显变化。
陀螺仪很灵敏,适合捕捉快速转动。但它也有一个问题: 如果只靠陀螺仪长期累加角度,误差会慢慢积累,就像你闭着眼走路,刚开始方向还对,走久了就会偏。
所以实际姿态估计通常会把加速度计、陀螺仪,有时还有磁力计结合起来。
磁力计: 像一个电子指南针
磁力计读的是磁场强度,常见单位可能是 mG。它可以帮助设备判断大致朝向,类似指南针。
但磁力计很容易受环境影响。比如电机、大电流线缆、磁铁、金属结构都会干扰它。所以在机器人里,磁力计可以作为参考,但不能盲目信任。
对我们这个阶段来说,磁力计不是最核心的数据。我们主要用它确认 N100 是否正常输出完整传感器数据。
姿态角: 人类更容易看懂的角度
姿态角通常会写成:
- Roll: 横滚,可以理解成左右歪。
- Pitch: 俯仰,可以理解成前后点头。
- Yaw: 航向,可以理解成在水平面上转头。
它们很适合给人看,因为“倾斜了 10 度”比“四元数某个分量变了 0.03”更直观。
不过姿态角有一个麻烦: 当角度接近某些特殊位置时,会出现数学上的表达问题,常被称为“万向节锁”。你不需要现在深入理解,只要先记住: 姿态角适合人看,但程序内部不一定最喜欢用它。
四元数: 给程序用的姿态表达
四元数看起来像四个数字:
w, x, y, z
第一次看到它会有点吓人,因为它不像角度那样直观。但可以把四元数理解成一种“给程序看的旋转说明书”。
如果姿态角像是在说:
先横滚多少度,再俯仰多少度,再航向多少度
四元数更像是在说:
绕某个空间方向,一次性旋转多少
它的好处是:
- 适合计算机连续计算姿态。
- 不容易遇到姿态角那种特殊角度问题。
- 很适合 3D 图形、机器人和飞行器内部使用。
在我们的项目里,N100 会输出 AHRS 四元数,但为了把坐标系先调清楚,我们暂时没有直接用它驱动 3D 模型,而是先用重力加速度驱动。这样做的好处是: 新手更容易把“我怎么摆放传感器”和“画面怎么转”对应起来。
这些数据怎么用在本文工具里
当前 N100 Web Viewer 的使用方式可以总结成这样:
- 加速度计: 用来驱动 3D 模型的俯仰和横滚显示。
- 陀螺仪: 用来观察当前是否正在绕某个轴旋转。
- 磁力计: 用来确认磁场数据是否正常输出。
- 姿态角: 用来给人快速观察 Roll、Pitch、Yaw。
- 四元数: 保留在数据面板里,作为 N100 原始姿态输出,后续可用于更完整的姿态融合和机器人控制。
如果你是第一次调 IMU,建议先盯住加速度和陀螺仪:
- 慢慢改变摆放姿态,看加速度主分量怎么变。
- 绕某个轴转动,看对应轴的角速度是否明显变化。
- 再观察 3D 模型是否和真实动作一致。
这套系统怎么连起来
从读者角度看,最终只是打开浏览器,看见一个 3D 模型跟着真实 N100 动。但背后其实有几层:
- N100 IMU<br/>输出加速度、角速度、姿态数据
简单说:
- N100 负责测量。
- 树莓派负责读取 N100。
- Python 负责解析数据。
- Flask 负责把数据变成网页能访问的接口。
- 浏览器负责显示数字和 3D 模型。
当前成果
- 树莓派通过
/dev/ttyUSB0读取 N100,波特率921600。 - 后端用 Python 解析 N100 数据帧,并通过
/api/snapshot提供聚合状态。 - 前端用 Three.js 显示 N100 结构模型。
- 页面支持串口选择、波特率配置、连接/断开、设置初始位置。
- 数值面板保留 N100 串口直读原始数据。
- 3D 姿态显示使用重力加速度驱动,用于校验俯仰、横滚和坐标轴方向。
NED 和 NWU 是什么
调 IMU 最容易卡住的地方,不是“有没有数据”,而是“这些数据到底朝哪个方向算正”。
坐标系可以理解成一套约定: 我们说 X、Y、Z 三个方向时,到底哪个方向是正方向。
NED 是 North-East-Down:
- N: North,北,X 轴指向北。
- E: East,东,Y 轴指向东。
- D: Down,下,Z 轴指向地面。
NWU 是 North-West-Up:
- N: North,北,X 轴指向北。
- W: West,西,Y 轴指向西。
- U: Up,上,Z 轴指向天空。
可以用一个生活化比喻:
- NED 像“航空视角”: 飞机常常关心向下高度、俯冲、下降,所以 Z 正方向可以定义为向下。
- NWU 像“地面机器人视角”: 我们站在地上,更习惯说天空是上,所以 Z 正方向定义为向上。
这两个坐标系都不是错的,它们只是约定不同。真正危险的是混着用: 一会儿按 NED 理解,一会儿按 NWU 理解,3D 模型就会反着转、歪着转,甚至看起来像“传感器坏了”。
N100 手册里提到: 如果使用上位机或系统引脚直接读取数据,参考坐标系是 NED;如果通过 ROS SDK 读取,参考坐标系是 NWU。我们这次是 Python 串口直读,所以最终决定: 数值面板和 API 保留 N100 原始 NED 数据,不擅自改成 NWU。
关键经验
1. 先保留原始数据
N100 手册里有一个容易踩坑的点: 上位机或系统引脚直读数据采用北东地 NED 坐标系,而 ROS SDK 读取数据采用北西天 NWU 坐标系。
我们当前是串口直读,所以最终决定 Web API 和数值面板只显示 N100 原始 NED 数据,不在后端混入临时转换字段。
2. 用实物姿态校验坐标系
只看代码很难把坐标系想清楚。更可靠的方法是把 IMU 摆到几个明确姿态,再看加速度主分量落在哪个轴上。
最终确认的 N100 无外壳模块方向:
- 正面视角 USB 在下时,
+X向上。 +Y向右。+Z指向板内。- 模块正面朝上平放时,
+Z指向地面。
3. 把模糊反馈变成测试用例
“绕 X 轴反了”这类反馈还不够精确,因为观察方向会影响顺时针/逆时针判断。后来我们把问题改写成明确用例:
当原始 Y 从 0 增至约 +9.8 时,图形应沿 X 轴、面对 X- 逆时针旋转。
这个用例可以直接转成向量模拟,并最终固定了前端显示层的旋转方向。
4. 实时页面要避免请求堆积
一开始前端同时高频请求多个 API,浏览器出现过 net::ERR_INSUFFICIENT_RESOURCES。后来改成单一 /api/snapshot 聚合接口,并加上请求防重入。
3D 模型卡顿也不是单纯的 WebGL 问题。我们把数据更新和渲染分开: 数据层只更新目标四元数,渲染层在 requestAnimationFrame 中用 slerp 平滑追踪,观感明显更顺。
我们是怎么和 Agent 协作完成的
如果你也想用自己的 AI Agent 复刻类似工具,不要一开始就说“帮我做一个完整机器人系统”。更好的沟通方式是把目标拆小,并不断把真实观察反馈给 Agent。
这次我们的协作大致是这样:
先定义最小目标 “我想用 Python 读取 N100 IMU 数据,并用 Web 页面显示出来。”
补充硬件事实 “树莓派地址是
raspberrypi.local,N100 插在 USB 上,可能是/dev/ttyUSB0,波特率用921600。”先要需求文档,再写代码 先让 Agent 生成需求和方案,确认功能范围: 手动选择端口、手动设置波特率、Flask/FastAPI 选择、Three.js 3D 显示、第一版不做 CSV。
让 Agent 做最小工程 不急着接 Qmini 主工程,先做
n100-web-viewer这样一个独立项目。用真实现象反馈问题 比如:
- “网页打不开。”
- “只能看到加载页面。”
- “水平放桌面时 Z 是负数。”
- “绕 X 轴方向反了。”
- “当 Y 从 0 到 9.8 时,面对 X- 应该逆时针。”
把模糊反馈改成可测试语句 “不对”当然可以说,但更有效的是描述清楚:
- 我现在怎么摆放 IMU。
- 哪个轴朝天。
- 数值大概是多少。
- 画面应该怎么转。
- 实际画面怎么转。
要求 Agent 保留文档 每次关键决策都写入项目日志和方案文档。后面你忘了为什么这么改,可以回头复盘。
可以直接复制下面这种提示词给自己的 Agent:
我正在复刻 Qmini 机器人,先做一个 N100 IMU 最小调试工具。
请先不要接入完整机器人系统,而是创建一个独立 Python Web 工程。
要求:
1. 树莓派读取 N100 串口数据。
2. Web 页面显示加速度、角速度、姿态信息。
3. 用 Three.js 显示一个 3D 模型。
4. 支持手动选择串口和波特率。
5. 保留原始数据,不要随意改坐标系。
6. 每次坐标系修正都写入项目日志。
我会用真实摆放姿态和传感器读数来反馈你继续调整。
启动方法
ssh [email protected]
cd $PROJECT_DIR/n100-web-viewer
. .venv/bin/activate
python app.py --port /dev/ttyUSB0 --baud 921600 --host 0.0.0.0 --web-port 5000浏览器访问:
http://raspberrypi.local:5000/
开发其他 IMU 或陀螺仪功能时,默认保持这个服务关闭,避免占用 /dev/ttyUSB0 和 5000 端口。
下一篇可以写什么
- 如何整理 Qmini 的官方资料和版本基线。
- 如何从 BOM 开始拆解采购清单。
- 如何规划树莓派端和开发/仿真机的职责边界。
