本页目录

二次开发

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)

1761199541515-3133feff-08b1-47c3-bc5c-dd5ff96617fd.png

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

1761199627677-4240928b-d4fe-4e4f-b0cc-386a93a0d926.png1761199609236-47117d25-f55b-4838-99e0-d589c325d800.png

  • 音频系统 (I²S)
    • 输入:INMP441 MEMS 数字麦克风(高灵敏度拾音)。
    • 输出:MAX98357A 功放驱动 8Ω 2W 扬声器。
  • 1761199572481-b9f00d04-b3ab-4c6b-8b43-d5c3dbc816a6.png1761199572555-d902e69a-add1-4aff-a4dc-2e6850bff3ad.png
  • 姿态感知 (I²C):板载 6 轴 IMU (QMI8658C) 用于实时姿态检测与运动辅助。

3. RIG-Arm 硬件组成

模块代码入口说明
主控ESP32-S3运行 ESP-IDF、FreeRTOS、Wi-Fi、音频、显示和运动控制
屏幕GC9A01 240x240 圆屏使用 SPI + EmoteDisplay + EAF 表情动画
舵机 ×5scs009 总线舵机UART 串口协议,ID 1~5,支持 ID 寻址与离合保护
姿态传感器QMI8658C IMUI2C 读取 roll、pitch、yaw,用于碰撞姿态检测
音频I2S Mic / Speaker语音唤醒、ASR/TTS、提示音
摄像头GC0308DVP 接口,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-OmniBoard 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... 时:

  1. 按住主板背面 Boot 键
  2. 按一下旁边的 RST 键
  3. 先松开 RST,再松开 Boot
  4. 终端将立即开始下载

成功标志

终端显示:

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_task2 ms (XGO_TASK_INTERVAL_MS)Core 0调用 xgo_control(),执行 IK 求解并下发舵机角度指令
xgo_rx_task20 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.ccmain/boards/arm/ik.cc

7.1 逆运动学求解器

RIG-Arm 采用 CLIK(Closed-Loop Inverse Kinematics)算法,在 ESP32-S3 上完成从末端目标位姿到 5 个关节角度的实时求解。

关键参数(ik.cc 内):

常量含义
H00.053 m底座高度
L10.090 m大臂长度
L20.090 m小臂长度
EE_X0.035 m末端工具 X 偏移
EE_Y-0.01 m末端工具 Y 偏移
EE_Z0.025 m末端工具 Z 偏移
DEFAULT_LAM0.05DLS 阻尼系数
MAX_ITER_WARM40热启动最大迭代次数
MAX_ITER_COLD60冷启动最大迭代次数

关节限位(弧度制):

关节下界上界约合角度
J1 底座旋转-2.622.62±150°
J2 大臂俯仰-1.571.57±90°
J3 小臂俯仰-0.502.50-28.6° ~ 143.2°
J4 手腕旋转-1.501.50±86°
J5 夹爪张合-1.201.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_mode0 为正常运行,1 为标定模式(舵机断电)
Action_ID非零时播放预设动作,零时走 IK 控制

7.3 控制流程

xgo_control() 的每周期逻辑:

  1. 等待 init_flag 置位
  2. 若 Action_ID == 0:调用 arm_ik_update(),根据 arm_x/y/z/roll/pitch/yaw 求解 arm_angle[]
  3. 若 Action_ID != 0:调用 xgo_action() 播放预设动作
  4. 若非标定模式:SetMotorAngle(arm_angle, motor_speed) 下发舵机指令
  5. 周期性:读取舵机反馈状态、检测三击按键、读取电池电压

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 标定流程

标定用于设定每个舵机的"自然零点"——让机械臂处于标准直立姿态后记录当前舵机位置。

  1. 触摸传感器单击进入标定模式(舵机断电,可手动调整姿态)
  2. 屏幕显示标定表情(calibration)
  3. 手动将各关节摆至标准直立位置
  4. 再次触摸传感器退出标定(舵机恢复供电,写入零点)

标定数据保存于 Flash(地址 0xFFF000),上电时通过 InitZeroPos() 读回。


8. MCP 远程控制接口

RIG-Arm 在 arm_board.ccInitializeTools() 中注册了多个机器人控制工具。

8.1 已注册的 MCP 工具

MCP 工具名参数功能
self.arm.node机械臂上下点头
self.arm.shake机械臂左右摇头
self.arm.xx: -3~3末端前后移动(厘米)
self.arm.yy: -2~2末端左右移动(厘米)
self.arm.zz: -5~5末端上下移动(厘米)
self.arm.yawyaw: -60~60末端左右旋转(度)
self.arm.pitchpitch: -30~30末端俯仰旋转(度)
self.arm.rollroll: -30~30末端歪头旋转(度)
self.dog.calibratemode: 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() 反馈处理

实验顺序

  1. 在非标定模式下观察 5 路舵机 ID 1~5 的反馈位置
  2. 手动修改 arm_angle 下发固定角度
  3. 观察堵转检测触发条件

第 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.ccInitializeTools() 中注册 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.ccLIM 数组。单位均为弧度,换算公式:弧度 = 角度 × π ÷ 180。

修改角度范围时需确保不超过机械结构的物理极限。建议修改前先手动测试最大可转动角度。

10.3 IK 求解参数

参数位置建议范围作用
DEFAULT_LAMik.cc0.01 ~ 0.2DLS 阻尼,值越大求解越保守
MAX_ITER_WARMik.cc20 ~ 80热启动迭代上限,增大可提升精度
arm_wxgo.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.txtCONFIG_BOARD_TYPE_ARM 分支。

11.4 增加新的预设动作

推荐做法:

  1. xgo_action.cc 中实现动作函数
  2. xgo_action.h 中声明
  3. arm_board.ccInitializeTools() 中注册为 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.ccOnStartup() 中可修改开机表情和音效:

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. 参考链接


15. 项目信息

只需一台3D打印机,就能拥有一个会听、会动、有“灵魂”的伙伴。

技术支持:如有深度开发需求,欢迎加入官方开发者微信群

1767280434454-34af4c62-194c-45ad-b07b-2e2645935c6b.jpeg