RIG-Arm 开源文档与开发教程
本文面向希望快速熟悉、二次开发和调试 RIG-Arm 的嵌入式开发者。项目来源为 LuwuDynamics/rig_omni,RIG-Arm 是该仓库中基于 ESP32-S3 的五轴桌面机械臂形态。
1. 项目定位
RIG-Omni 是一套统一的 ESP32-S3 机器人固件框架,用同一套应用层支持不同机器人形态。当前代码中主要包含:
- RIG-Puppy:5 自由度机器狗
- RIG-Hover:1 舵机 + 2 FOC 轮电机的双轮自平衡机器人
- RIG-Arm:5 自由度桌面机械臂
RIG-Arm 的价值不只是"能按指令动起来",而是把语音交互、表情显示、Wi-Fi 配网、MCP 远程控制、IMU 姿态采集和 5 轴闭链逆运动学(CLIK)控制放在同一个 ESP32-S3 固件工程里。对开发者来说,它是一个完整的"桌面机器人固件骨架"。
2. 系统架构
RIG-Arm 采用高度集成的单芯片架构,所有感知、控制与 AI 逻辑均由一颗 ESP32-S3 独立完成,无需额外的协处理器。这种设计在保证高性能的同时,极大地降低了硬件成本与开发复杂度。
本项目开源了原理图、3D模型、固件代码及固件,组装教程等,适合用于学习机器人运动控制、大模型、多模态,物联网通信等技术。
2.1 硬件抽象层
系统硬件通过标准总线协议与主控通信,实现了模块化解耦:
- 主控核心:ESP32-S3-WROOM-1-N16R8 (双核 240MHz, 2.4G Wi-Fi + BLE 5.0)

- 运动总线 (UART):采用 TX/RX 串行总线控制 5 个 SCS009 串口舵机,支持 ID 寻址与状态回读,5 路舵机并联在同一信号总线上。
- 视觉链路 (DVP/SPI):GC0308 摄像头通过 DVP 接口采集图像,GC9A01 240×240 圆屏通过 SPI 接口实时刷新表情。


- 音频系统 (I²S):
- 输入:INMP441 MEMS 数字麦克风(高灵敏度拾音)。
- 输出:MAX98357A 功放驱动 8Ω 2W 扬声器。


- 姿态感知 (I²C):板载 6 轴 IMU (QMI8658C) 用于实时姿态检测与运动辅助。
3. RIG-Arm 硬件组成
| 模块 | 代码入口 | 说明 |
|---|---|---|
| 主控 | ESP32-S3 | 运行 ESP-IDF、FreeRTOS、Wi-Fi、音频、显示和运动控制 |
| 屏幕 | GC9A01 240x240 圆屏 | 使用 SPI + EmoteDisplay + EAF 表情动画 |
| 舵机 ×5 | scs009 总线舵机 | UART 串口协议,ID 1~5,支持 ID 寻址与离合保护 |
| 姿态传感器 | QMI8658C IMU | I2C 读取 roll、pitch、yaw,用于碰撞姿态检测 |
| 音频 | I2S Mic / Speaker | 语音唤醒、ASR/TTS、提示音 |
| 摄像头 | GC0308 | DVP 接口,30 万像素,可选拍照工具 |
| 按键 / 触摸 | Boot GPIO0 + Touch GPIO3 | 配网、聊天开关、长按清除 NVS、标定 |
RIG-Arm 共用引脚集中在 main/boards/common/config.h,Arm 专属引脚在 main/boards/arm/board_config.h:
XGO_UART_TX → GPIO 46
XGO_UART_RX → GPIO 38
LASER_GPIO → GPIO 3
TOUCH_GPIO → GPIO 3
IMU_I2C_SDA → GPIO 14
IMU_I2C_SCL → GPIO 48
4. 代码结构速览
重点目录如下:
main/boards/arm/
├── arm_board.cc ← 主逻辑(初始化、按键、MCP 注册)
├── board_config.h ← 舵机/IMU/触摸引脚定义
├── ik.h / ik.cc ← 逆运动学求解器(CLIK)
├── xgo.h / xgo.cc ← 舵机串口协议 + 控制主循环
├── xgo_action.h / xgo_action.cc ← 预设动作定义
├── ble_remote_control.h / ble_remote_control.cc ← 蓝牙遥控
├── arm_startup.ogg ← 开机音效
├── emoji/ ← EAF 表情动画文件
└── wakenet/ ← 唤醒词模型
最值得先读的 4 个文件:
main/boards/arm/arm_board.cc— 理解板级初始化、按键逻辑、MCP 工具注册main/boards/arm/xgo.cc— 理解舵机协议、控制主循环、IK 调用main/boards/arm/ik.cc— 理解逆运动学求解器的参数和算法main/Kconfig.projbuild— 理解板型选择与编译配置
5. 构建和烧录
准备环境
- ESP-IDF v5.5.3 或更高版本
- Python 3.8+
- ESP32-S3 开发环境
- 建议使用 16MB Flash + PSRAM 的 ESP32-S3 模组
拉取代码
git clone https://github.com/Xgorobot/RIG-Omni.git
cd RIG-Omni
加载 ESP-IDF
Windows PowerShell(替换路径中的用户名):
. C:\Users\用户名\esp\esp-idf\Export.ps1
macOS / Linux:
source ~/esp/esp-idf/export.sh
使用 VS Code + Espressif IDF 扩展时,打开项目后环境会自动激活。
设置目标芯片
idf.py set-target esp32s3
选择 RIG-Arm
idf.py menuconfig
在配置菜单中:RIG-Omni → Board Type → (X) RIG-Arm,按 Q 退出并保存。
也可以直接在 sdkconfig 中确认:
CONFIG_BOARD_TYPE_ARM=y
构建、烧录和串口监视
idf.py -p COM3 build flash monitor
Windows 下端口通常类似:
idf.py -p COM5 flash monitor
将 COM3/COM5 替换为实际串口号:
- Windows:在"设备管理器" → "端口"中查看
- macOS:
/dev/tty.usbserial-XXXX - Linux:
/dev/ttyUSB0
烧录卡住的解决方法
终端持续显示 Connecting... 时:
- 按住主板背面 Boot 键
- 按一下旁边的 RST 键
- 先松开 RST,再松开 Boot
- 终端将立即开始下载
成功标志
终端显示:
Writing at 0x00010000... (100%)
Leaving...
Hard resetting via RTS pin...
设备屏幕亮起并播放开机动画,表示烧录成功。
生成发布固件包
python tools/gen_bin_package.py
RIG-Arm 的输出固件名由 main/CMakeLists.txt 设置为:
rig-arm
6. RIG-Arm 启动链路
RIG-Arm 的板级类是 ArmBoard,定义在 arm_board.cc,最后通过:
DECLARE_BOARD(ArmBoard);
注册给通用 Board 框架。
构造函数中的初始化顺序大致是:
InitializeSpi()
InitializeLcdDisplay()
InitializeButtons()
InitializeTools()
InitializeCamera()
InitializeUart()
InitializeLaser()
InitZeroPos()
ReadServoVoltage(1)
EnableStallDetection(true)
InitializeBootButton()
imu_init()
rig_arm_ik_init(&arm_ik, 0.05)
创建 xgo_task
创建 xgo_rx_task
这里有三个核心 FreeRTOS 任务:
| 任务 | 间隔 | 所在核心 | 作用 |
|---|---|---|---|
| xgo_task | 2 ms (XGO_TASK_INTERVAL_MS) | Core 0 | 调用 xgo_control(),执行 IK 求解并下发舵机角度指令 |
| xgo_rx_task | 20 ms (XGO_RX_TASK_INTERVAL_MS) | Core 1 | 调用 xgo_rx() + imu_read_once(),接收舵机反馈并更新 IMU 姿态 |
| 应用任务 | 由 application.cc 管理 | — | 处理语音交互、网络通信、表情播放等上层逻辑 |
开发时建议先把启动日志跑通,确认这些日志能看到:
Initialize UART
XGO control tasks created
Initialization complete
7. 运动控制核心
RIG-Arm 的运动控制集中在 main/boards/arm/xgo.cc 和 main/boards/arm/ik.cc。
7.1 逆运动学求解器
RIG-Arm 采用 CLIK(Closed-Loop Inverse Kinematics)算法,在 ESP32-S3 上完成从末端目标位姿到 5 个关节角度的实时求解。
关键参数(ik.cc 内):
| 常量 | 值 | 含义 |
|---|---|---|
| H0 | 0.053 m | 底座高度 |
| L1 | 0.090 m | 大臂长度 |
| L2 | 0.090 m | 小臂长度 |
| EE_X | 0.035 m | 末端工具 X 偏移 |
| EE_Y | -0.01 m | 末端工具 Y 偏移 |
| EE_Z | 0.025 m | 末端工具 Z 偏移 |
| DEFAULT_LAM | 0.05 | DLS 阻尼系数 |
| MAX_ITER_WARM | 40 | 热启动最大迭代次数 |
| MAX_ITER_COLD | 60 | 冷启动最大迭代次数 |
关节限位(弧度制):
| 关节 | 下界 | 上界 | 约合角度 |
|---|---|---|---|
| J1 底座旋转 | -2.62 | 2.62 | ±150° |
| J2 大臂俯仰 | -1.57 | 1.57 | ±90° |
| J3 小臂俯仰 | -0.50 | 2.50 | -28.6° ~ 143.2° |
| J4 手腕旋转 | -1.50 | 1.50 | ±86° |
| J5 夹爪张合 | -1.20 | 1.30 | -68.8° ~ 74.5° |
7.2 关键全局变量
xgo.h 中定义了控制所需的状态变量:
| 变量 | 含义 |
|---|---|
| arm_x, arm_y, arm_z | 末端目标位置(米),由 MCP 工具或遥控修改 |
| arm_roll, arm_pitch, arm_yaw | 末端目标姿态(弧度),由 MCP 工具修改 |
| arm_w | 位置/姿态权重(0~1),0 为全姿态优先,1 为全位置优先 |
| arm_angle[5] | 由 IK 求解得到的各关节目标角度(度) |
| calibrate_mode | 0 为正常运行,1 为标定模式(舵机断电) |
| Action_ID | 非零时播放预设动作,零时走 IK 控制 |
7.3 控制流程
xgo_control() 的每周期逻辑:
- 等待 init_flag 置位
- 若 Action_ID == 0:调用 arm_ik_update(),根据 arm_x/y/z/roll/pitch/yaw 求解 arm_angle[]
- 若 Action_ID != 0:调用 xgo_action() 播放预设动作
- 若非标定模式:SetMotorAngle(arm_angle, motor_speed) 下发舵机指令
- 周期性:读取舵机反馈状态、检测三击按键、读取电池电压
arm_ik_update() 调用链路:
MCP tool / remote → arm_x, arm_y, arm_z, arm_roll, arm_pitch, arm_yaw
→ rig_arm_ik_solve(&arm_ik, x, y, z, roll, pitch, yaw, w, q_out, ...)
→ fkChain (正运动学)
→ jacobian (解析雅可比)
→ dlsStep (阻尼最小二乘求解)
→ arm_angle[0..4] = q_out (角度制)
7.4 舵机通信协议
scs009 舵机使用 XGO 串口协议,UART2,波特率 500 kbps,5 个舵机并联在同一信号总线上,通过 ID 寻址:
| 参数 | 值 |
|---|---|
| 波特率 | 500000 |
| UART 编号 | UART_NUM_2 |
| TX 引脚 | GPIO 46 |
| RX 引脚 | GPIO 38 |
关键协议函数:
SetMotorAngle(angle[], vel)— 设置 5 路舵机目标角度ReadMotorState(ID)— 读取单路舵机反馈(位置、速度、扭矩)InitZeroPos()/WriteZeroPos()— 零点位置初始化与写入EnableAllMotor(mode)— 全局舵机使能/失能EnableStallDetection(bool)— 堵转检测开关
7.5 标定流程
标定用于设定每个舵机的"自然零点"——让机械臂处于标准直立姿态后记录当前舵机位置。
- 触摸传感器单击进入标定模式(舵机断电,可手动调整姿态)
- 屏幕显示标定表情(calibration)
- 手动将各关节摆至标准直立位置
- 再次触摸传感器退出标定(舵机恢复供电,写入零点)
标定数据保存于 Flash(地址 0xFFF000),上电时通过 InitZeroPos() 读回。
8. MCP 远程控制接口
RIG-Arm 在 arm_board.cc 的 InitializeTools() 中注册了多个机器人控制工具。
8.1 已注册的 MCP 工具
| MCP 工具名 | 参数 | 功能 |
|---|---|---|
| self.arm.node | 无 | 机械臂上下点头 |
| self.arm.shake | 无 | 机械臂左右摇头 |
| self.arm.x | x: -3~3 | 末端前后移动(厘米) |
| self.arm.y | y: -2~2 | 末端左右移动(厘米) |
| self.arm.z | z: -5~5 | 末端上下移动(厘米) |
| self.arm.yaw | yaw: -60~60 | 末端左右旋转(度) |
| self.arm.pitch | pitch: -30~30 | 末端俯仰旋转(度) |
| self.arm.roll | roll: -30~30 | 末端歪头旋转(度) |
| self.dog.calibrate | mode: 0/1 | 进入/退出标定模式 |
9. 推荐学习路线
第 1 天:跑通工程
目标:固件能编译、烧录、串口有日志。
阅读:
- README_CN.md
- main/Kconfig.projbuild
- main/CMakeLists.txt 中 CONFIG_BOARD_TYPE_ARM 分支
实验:
- 克隆代码,按第 5 节步骤完成编译烧录
- 观察串口启动日志
检查点:
- 串口能看到 ARM 标签日志
- LCD 能播放启动表情(launch)
- 按键/触摸能触发配网或聊天开关
第 2 天:理解板级初始化
目标:知道每个外设在哪里初始化。
阅读:
- arm_board.cc(ArmBoard 构造函数和 Initialize* 方法)
- board_config.h
- boards/common/config.h(显示、音频公共引脚)
- boards/common/wifi_board.cc
实验:
- 修改开机表情:OnStartup() 中
display_->SetEmotion("launch")改为其他表情 - 修改长按 NVS 清除时间:
kLongPressResetMs常量 - 打印 IMU 姿态到日志
第 3 天:理解舵机协议和控制循环
目标:知道如何给舵机发命令,如何读取反馈。
阅读:
- xgo.h(Motor 结构体、协议宏定义)
- xgo.cc 中 SendMotorCommand()、ReadMotorState()、SetMotorAngle()
- xgo_rx() 反馈处理
实验顺序:
- 在非标定模式下观察 5 路舵机 ID 1~5 的反馈位置
- 手动修改 arm_angle 下发固定角度
- 观察堵转检测触发条件
第 4 天:理解逆运动学
目标:知道机械臂末端坐标如何映射到 5 个关节角度。
阅读:
- ik.cc(重点:fkChain、jacobian、dlsStep、geometricSeed)
- ik.h(RigArmIK 结构体,rig_arm_ik_solve 接口)
- xgo.cc 中 arm_ik_update()
理解关键量:
- arm_x/arm_y/arm_z — 末端位置目标(世界坐标,原点在底座中心下方)
- arm_roll/arm_pitch/arm_yaw — 末端姿态目标
- arm_w — 位置/姿态折中权重
- arm_angle[5] — 求解得到的各关节角度(度)
实验:
- 通过 MCP 工具的 self.arm.x / self.arm.y / self.arm.z 观察末端位置变化
- 观察超出工作空间时 IK 的未收敛日志
第 5 天:开发新动作
目标:通过 MCP 注册一个新动作工具。
示例:新增"转头看左右"动作。
在 xgo_action.cc 中添加实现,在 arm_board.cc 的 InitializeTools() 中注册 MCP 工具:
mcp_server.AddTool("self.arm.look_around",
"在和用户聊天时,如果用户问"周围有什么"、"看看四周","
"让机械臂缓慢左右转动观察环境",
PropertyList({}),
[this](const PropertyList& properties) -> ReturnValue {
look_around();
return true;
});
更好的做法是做成带参数工具,让 AI 可以指定幅度和速度。
10. 调参建议
调 RIG-Arm 时,先在无负载状态下验证舵机方向,再调整运动参数。
10.1 调试前检查
- 标定是否完成:上电后触摸传感器确认不在标定模式(屏幕显示 neutral 表情)
- 舵机 ID 是否正确:ID 15 分别对应 J1J5
- IMU 是否正常:通过
GetDeviceStatusJson查看 roll/pitch/yaw 反馈 - 机械限位:每个关节手动转动确认无卡死
10.2 关节限位调整
编辑 ik.cc 中 LIM 数组。单位均为弧度,换算公式:弧度 = 角度 × π ÷ 180。
修改角度范围时需确保不超过机械结构的物理极限。建议修改前先手动测试最大可转动角度。
10.3 IK 求解参数
| 参数 | 位置 | 建议范围 | 作用 |
|---|---|---|---|
| DEFAULT_LAM | ik.cc | 0.01 ~ 0.2 | DLS 阻尼,值越大求解越保守 |
| MAX_ITER_WARM | ik.cc | 20 ~ 80 | 热启动迭代上限,增大可提升精度 |
| arm_w | xgo.h (运行时) | 0.3 ~ 0.7 | 位置 vs 姿态权重 |
如果 IK 频繁不收敛(日志出现 Not converged),可以:
- 增大 DEFAULT_LAM(0.05 → 0.1)
- 检查目标位置是否超出工作空间
- 确认机械臂尺寸参数(H0/L1/L2/EE_*)与实际一致
10.4 安全范围
- 末端移动:x 范围约 -8cm ~ +8cm,y 范围约 -5cm ~ +5cm,z 范围约 5cm ~ 23cm
- 末端旋转:yaw ±90°,pitch ±60°,roll ±60°
- MCP 工具的增量步长已限制,大幅修改常量前建议先做小范围测试
11. 常见开发任务
11.1 修改 RIG-Arm 引脚
改 main/boards/arm/board_config.h:
#define XGO_UART_TX_PIN GPIO_NUM_46
#define XGO_UART_RX_PIN GPIO_NUM_38
#define IMU_I2C_SDA GPIO_NUM_14
#define IMU_I2C_SCL GPIO_NUM_48
显示、音频引脚在 main/boards/common/config.h:
#define DISPLAY_MOSI_PIN GPIO_NUM_20
#define DISPLAY_CLK_PIN GPIO_NUM_19
#define DISPLAY_DC_PIN GPIO_NUM_47
#define DISPLAY_RST_PIN GPIO_NUM_21
#define DISPLAY_CS_PIN GPIO_NUM_45
重点检查:
- UART TX/RX 是否与舵机总线一致
- IMU I2C 是否和摄像头 SCCB 分开(Arm 使用不同 I2C 端口)
- Touch GPIO 是否被其他外设复用(Arm 与激光共用 GPIO3)
11.2 新增表情
放 EAF 文件入 main/boards/arm/emoji/,然后在需要的地方调用:
if (display_) {
display_->SetEmotion("your_emotion_name");
}
文件名(不含 .eaf 扩展名)即为表情名称。
如果是新的 EAF 资源,还需要检查 CMakeLists.txt 中的资源包含配置。
11.3 修改唤醒词模型
RIG-Arm 当前唤醒词为"小陆同学",模型路径在 main/boards/arm/wakenet/。
构建系统中对应 main/CMakeLists.txt 的 CONFIG_BOARD_TYPE_ARM 分支。
11.4 增加新的预设动作
推荐做法:
- 在
xgo_action.cc中实现动作函数 - 在
xgo_action.h中声明 - 在
arm_board.cc的InitializeTools()中注册为 MCP 工具
动作通过关键帧方式定义——指定每个关节在特定时间点的目标角度,系统自动在帧间平滑插值。
void draw_circle() {
const int time = 3000;
const float radius = 0.03f;
for (int t = 0; t <= time; t += 10) {
float phase = 2.0f * M_PI * t / 3000.0f;
float x = 0.10f + radius * cos(phase);
float y = radius * sin(phase);
float q_out[5];
rig_arm_ik_solve(&arm_ik, x, y, 0.15f, 0, -0.3f, 0, 0.65f, q_out, NULL, NULL);
SetMotorAngle(q_out, 10);
vTaskDelay(pdMS_TO_TICKS(10));
}
}
MCP 回调中只设置目标量或触发动作函数,不要做长时间阻塞。不要在 MCP 回调里长时间 vTaskDelay()。
11.5 修改开机行为
arm_board.cc 的 OnStartup() 中可修改开机表情和音效:
virtual void OnStartup() override {
display_->SetEmotion("launch"); // 替换为其他表情名
Application::GetInstance().PlaySound(ARM_STARTUP_SOUND); // 替换音效
}
OnInitializationComplete() 在 Wi-Fi 连接和服务器注册完成后调用,可在此添加初始化完成后的行为。
11.6 修改按键行为
在 InitializeButtons() 中定义按键回调:
boot_button_.OnClick()— 单击切换聊天状态 / 进入配网模式boot_button_.OnDoubleClick()— 双击切换 AEC 开关boot_button_.OnPressDown()/OnPressUp()— 长按检测(1 秒显示 nvs_reset 表情,3 秒清除 NVS 并重启)touch_button_.OnClick()— 触摸进入/退出标定
11.7 适配修改后的机械结构
更换外壳或修改臂长后,编辑 ik.cc 中的尺寸常量:
constexpr float H0 = 0.053f; // 底座高度(米)
constexpr float L1 = 0.090f; // 大臂长度(米)
constexpr float L2 = 0.090f; // 小臂长度(米)
constexpr float EE_X = 0.035f; // 末端 X 偏移(米)
constexpr float EE_Y = -0.01f; // 末端 Y 偏移(米)
constexpr float EE_Z = 0.025f; // 末端 Z 偏移(米)
修改后 IK 算法将自动适配新尺寸。同时检查 LIM 数组是否需要调整角度限位。
12. 代码阅读地图
main/CMakeLists.txt ← 板型分支、固件名
└── Kconfig.projbuild ← 板型选项定义
└── arm_board.cc ← ArmBoard 类
├── common/config.h ← 显示/音频/摄像头公共引脚
├── board_config.h ← 舵机/IMU/触摸专属引脚
├── xgo.cc ← 舵机协议 + 控制主循环
│ ├── ik.cc ← CLIK 逆运动学
│ └── xgo_action.cc ← 预设动作
├── common/button.cc ← 按键驱动
├── common/imu.cc ← IMU 驱动
├── common/wifi_board.cc ← Wi-Fi 板级基类
├── ble_remote_control.cc ← 蓝牙遥控
├── display/emote_display.cc ← 表情显示
└── mcp_server.cc ← MCP 工具框架
13. 二次开发注意事项
- RIG-Arm 是 5 轴串联机械臂,任何舵机 ID 错乱、零点错误、旋转方向相反都会导致运动异常或结构碰撞。
- 调新动作时,先让单个关节慢速运动,确认方向正确,再组合多关节动作。
- 不要在 xgo_control() 实时控制路径中加入日志刷屏、动态内存分配或耗时网络操作——该函数每 2 ms 执行一次。
- 运动目标应通过 arm_x/arm_y/arm_z/arm_roll/arm_pitch/arm_yaw 间接影响 IK 求解,而不是绕过 IK 直接写舵机角度。
board_config.h中中文注释可能在某些编辑器中出现编码异常,不影响编译,但建议统一保存为 UTF-8。- 固件烧录后首次上电通常需要标定(屏幕显示 calibration 表情),触摸传感器完成标定后才能正常运动。
- 改完
menuconfig后如果出现编译异常,执行idf.py fullclean后再编译。
14. 参考链接
- 项目仓库:LuwuDynamics/RIG-Omni
- Arm 板级入口:main/boards/arm/arm_board.cc
- Arm 控制代码:main/boards/arm/xgo.cc
- Arm IK 求解:main/boards/arm/ik.cc
- Arm 引脚配置:main/boards/arm/board_config.h
- 构建配置:main/Kconfig.projbuild
- ESP-IDF 文档:Espressif ESP-IDF Programming Guide
15. 项目信息
- 代码仓库 (GitHub):https://github.com/Xgorobot/RIG-Omni
- 文档知识库 (语雀):https://www.yuque.com/luwudynamics/pet — 包含详细 API 文档、进阶开发教程
- 3D 模型 (MakerWorld):https://makerworld.com.cn/zh/@LuwuDynamics — 包含全套结构件 STL/3MF 文件
只需一台3D打印机,就能拥有一个会听、会动、有“灵魂”的伙伴。
技术支持:如有深度开发需求,欢迎加入官方开发者微信群

