本页目录

二次开发

RIG-Hover 开源文档与开发教程

本文面向希望快速熟悉、二次开发和调试 RIG-Hover 的嵌入式开发者。项目来源为 LuwuDynamics/rig_omni,RIG-Hover 是该仓库中基于 ESP32-S3 的双轮自平衡机器人形态。

1. 项目定位

RIG-Omni 是一套统一的 ESP32-S3 机器人固件框架,用同一套应用层支持不同机器人形态。当前代码中主要包含:

  • RIG-Puppy:5 舵机机器狗
  • RIG-Hover:1 个头部舵机 + 2 个串口 FOC 轮电机的双轮自平衡机器人

RIG-Hover 的价值不只是"能跑起来",而是把语音交互、表情显示、Wi-Fi 配网、MCP 远程控制、IMU 姿态采集、双轮平衡控制和 Web 调参放在同一个 ESP32-S3 固件工程里。对开发者来说,它是一个很好的"智能机器人固件骨架"。

2. 系统架构

RIG-Hover 采用高度集成的单芯片架构,所有感知、控制与 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 串行总线控制 1 个头部舵机(XGO 串口协议)与 2 个串口 FOC 轮电机,支持 ID 寻址与状态回读,实现差速驱动与自平衡。
  • 视觉链路 (DVP/SPI):GC9A01 240×240 圆屏通过 SPI 接口实时刷新表情,GC0308 摄像头通过 DVP 接口采集图像。

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-Hover 硬件组成

模块代码入口说明
主控ESP32-S3运行 ESP-IDF、FreeRTOS、Wi-Fi、音频、显示和运动控制
屏幕GC9A01 240x240 圆屏使用 SPI + LVGL + EAF 表情动画
姿态传感器QMI8658C IMUI2C 读取 roll、pitch、yaw,用于平衡控制
头部舵机XGO 串口协议,ID 3控制头部左右转动
左右轮电机串口 FOC 电机,ID 1/2差速驱动与自平衡力矩输出
音频I2S Mic/Speaker语音唤醒、ASR/TTS、提示音
摄像头ESP32 camera DVP可选拍照工具
按键/触摸Boot GPIO0 + Touch GPIO3配网、聊天开关、长按清除 NVS

RIG-Hover 专属引脚集中在 main/boards/hover/board_config.h

#define XGO_UART_TX_PIN GPIO_NUM_46
#define XGO_UART_RX_PIN GPIO_NUM_38
#define TOUCH_BUTTON_GPIO GPIO_NUM_3
#define IMU_I2C_SDA GPIO_NUM_14
#define IMU_I2C_SCL GPIO_NUM_48

4. 代码结构速览

重点目录如下:

rig_omni/
├── main/
│   ├── application.cc/.h              # 应用生命周期、状态机、音频和协议调度
│   ├── mcp_server.cc/.h               # MCP 远程工具框架
│   ├── ota.cc/.h                      # OTA 更新
│   ├── audio/                         # 音频、唤醒词、降噪处理
│   ├── display/                       # LVGL、LCD、EAF 表情显示
│   └── boards/
│       ├── common/                    # 通用板级能力:Wi-Fi、按钮、IMU、摄像头等
│       ├── puppy/                     # RIG-Puppy 专属实现
│       └── hover/                     # RIG-Hover 专属实现
│           ├── hover_board.cc         # Hover 板级总入口
│           ├── xgo.cc/.h              # 舵机/轮电机协议、平衡控制、BLE 协议入口
│           ├── hover_debug_server.cc  # Web 调参服务器
│           ├── board_config.h         # Hover 引脚与板级配置
│           ├── emoji/                 # EAF 表情资源
│           ├── 240_240/               # 表情布局与资源配置
│           └── wakenet/               # Hover 专属唤醒词模型
├── tools/                             # 资源打包、固件包生成脚本
├── partitions/16m.csv                 # 16MB Flash 分区表
└── sdkconfig.defaults*                # 默认构建配置

最值得先读的 4 个文件:

  1. main/boards/hover/hover_board.cc
  2. main/boards/hover/xgo.cc
  3. main/boards/hover/hover_debug_server.cc
  4. main/Kconfig.projbuild

5. 构建和烧录

准备环境:

  • ESP-IDF v5.5.3 或更高版本
  • Python 3.8+
  • ESP32-S3 开发环境
  • 建议使用 16MB Flash + PSRAM 的 ESP32-S3 模组

拉取代码:

git clone https://github.com/LuwuDynamics/rig_omni.git
cd rig_omni

加载 ESP-IDF:

source ~/esp/esp-idf/export.sh

设置目标芯片:

idf.py set-target esp32s3

选择 RIG-Hover:

idf.py menuconfig

进入:

RIG-Omni -> Board Type -> RIG-Hover

也可以直接在 sdkconfig 中确认:

CONFIG_BOARD_TYPE_HOVER=y

构建、烧录和串口监视:

idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

Windows 下端口通常类似:

idf.py -p COM5 flash monitor

生成发布固件包:

python tools/gen_bin_package.py

RIG-Hover 的输出固件名由 main/CMakeLists.txt 设置为:

rig-hover

6. RIG-Hover 启动链路

RIG-Hover 的板级类是 HoverBoard,定义在 hover_board.cc,最后通过:

DECLARE_BOARD(HoverBoard);

注册给通用 Board 框架。

构造函数中的初始化顺序大致是:

InitializeSpi()
InitializeLcdDisplay()
InitializeButtons()
InitializeTools()
InitializeCamera()
InitializeUart()
InitializeController()
InitializeBootButton()
imu_init()
创建 xgo_control 任务
创建 xgo_rx 任务
创建 imu_read_once 任务

这里有三个核心 FreeRTOS 任务:

任务所在线程逻辑作用
xgo_task循环调用 xgo_control()计算平衡控制量并发送轮电机/头部舵机指令
xgo_rx_task循环调用 xgo_rx()接收电机反馈,更新轮速、位置、舵机角度
imu_task循环调用 imu_read_once()更新 roll、pitch、yaw、角速度等姿态变量

开发时建议先把启动日志跑通,确认这些日志能看到:

Initialize UART
XGO control tasks created
IMU control tasks created
Initialization complete
Debug server started at http://<device-ip>

7. 运动控制核心

RIG-Hover 的运动控制主要在 main/boards/hover/xgo.cc

7.1 关键全局变量

变量含义
vx目标前进速度
vyaw目标转向速度,目前主控制中主要使用 stable_yawyaw_u
target_head_pos头部舵机目标角度,单位近似为度
wheel1_vel / wheel2_vel左右轮反馈速度
wheel1_x / wheel2_x左右轮累计位置
wheel_x机器人前后位置估计
wheek_vx平均轮速,变量名里有拼写遗留
pitch / roll / yawIMU 姿态反馈,来自 imu.h
dqpitch 角速度反馈
stable_pos站稳时的目标位置
stable_yaw站稳时的目标航向
imu_zero平衡零点角度补偿
robot_state0 表示停止输出力矩,1 表示允许平衡控制
lqr_k[4]LQR 全状态反馈参数

7.2 控制器初始化

InitializeController() 设置默认参数:

pid_pos.fpKp = 60.0;
pid_vel.fpKp = 0.15;
pid_vel.fpKi = 0.01;
pid_pit.fpKp = 21.0;

kd_pit = 2.3;
imu_zero = -5.0;

lqr_k[0] = 1400.0f;
lqr_k[1] = 6.8f;
lqr_k[2] = 32.0f;
lqr_k[3] = 1.6f;

当前主控制逻辑已经从传统串级 PID 迁移到离散 LQR。文件里保留了位置、速度、俯仰 PID 的旧逻辑注释,适合学习对比。

7.3 状态更新

update_state() 做几件事:

  • 根据左右轮累计位置计算 wheel_x
  • 根据左右轮速度计算平均速度 wheek_vx
  • 判断机器人是否已经稳定站立
  • 姿态超过安全范围时关闭力矩输出
  • 轮速过高时判断为被拿起或异常状态

关键保护条件:

pitch >= 60 deg 或 roll >= 40 deg 持续 500 ms -> robot_state = 0
pitch < 7 deg 且 roll < 7 deg 持续 1500 ms -> robot_state = 1
轮速绝对值 > 350 持续多次 -> robot_state = 0

这部分是二次开发必须保留的安全边界。调参时建议先让保护逻辑稳定工作,再提高控制输出。

7.4 LQR 控制流程

xgo_control() 的核心状态量:

pitch_ref = imu_zero * cosf(q_head * PI / 180.0f);
lqr_x  = wheel_x - stable_pos;
lqr_vx = wheek_vx - vx;
lqr_q  = pitch - pitch_ref;
lqr_dq = dq;

temp_u = -(lqr_k[0] * lqr_x
        + lqr_k[1] * lqr_vx
        + lqr_k[2] * lqr_q
        + lqr_k[3] * lqr_dq);

随后做输出限制:

temp_u = Clip(temp_u, -pid_pit.fpUMax, pid_pit.fpUMax);

航向控制:

yaw_u = k_yaw * (yaw - q_head - stable_yaw);
yaw_u = Clip(yaw_u, -80, 80);

左右轮力矩输出:

tor1 = -temp_u + yaw_u;
tor2 =  temp_u + yaw_u;

如果 robot_state == 0,左右轮力矩直接清零。

7.5 舵机和轮电机协议

Hover 使用 UART2,波特率 1 Mbps:

uart_driver_install(UART_NUM_2, 1024, 1024, 0, NULL, 0);
uart_param_config(UART_NUM_2, &uart_cfg);
uart_set_pin(UART_NUM_2, XGO_UART_TX_PIN, XGO_UART_RX_PIN, ...);

头部舵机控制:

WriteByte_P_V(3, short(1500.0 - target_head_pos * 10.0), 1300);

轮电机力矩通过 sendWheelTor() 下发,电机 ID 为 1 和 2:

torque[1] = tor1;
torque[2] = tor2;

8. MCP 远程控制接口

RIG-Hover 在 hover_board.cc 中注册了三个机器人控制工具:

MCP 工具名参数功能
self.robot.head_angleangle: -45~45设置头部舵机角度
self.robot.movedistance: -20~20修改 stable_pos,实现前进/后退
self.robot.rotateangle: -180~180修改 stable_yaw,实现原地转向

示例逻辑:

target_head_pos = angle;
stable_pos = stable_pos + distance;
stable_yaw = stable_yaw + angle;

开发建议:

  • 新增动作时优先注册 MCP 工具,而不是写死在控制循环中。
  • 工具回调中只修改目标量,不要做长时间阻塞。
  • 运动幅度先限制小范围,确认平衡稳定后再放宽。

9. Web 调试服务器

RIG-Hover 有专属调试服务器:hover_debug_server.cc

启动条件:

  • 设备 Wi-Fi 已连接
  • OnInitializationComplete()OnWifiConfigEnd() 中调用 hover_debug_server_start()

浏览器访问:

http://<RIG-Hover-IP>/

页面功能:

  • 实时查看 IMU 的 roll、pitch、yaw
  • 通过 /api/set 在线调整控制参数
  • 每 500 ms 自动刷新姿态数据

接口:

GET /api/data
GET /api/set?i=<index>&v=<value>

调试变量映射:

index页面标签对应变量建议用途
0headtarget_head_pos = value测试头部舵机
1delta_posstable_pos += value小步前进/后退
2POS_kppid_pos.fpKp = value旧 PID 参数,当前主控不直接使用
3POS_kdpid_pos.fpKd = value旧 PID 参数
4VEL_kppid_vel.fpKp = value旧 PID 参数
5VEL_kipid_vel.fpKi = value旧 PID 参数
6PIT_kppid_pit.fpKp = value当前主要用于输出上限结构体字段,保留调试意义
7PIT_kdkd_pit = value旧 PID 微分项
8YAW_kp实际代码映射为 imu_zero = value页面标签与代码含义不一致,调试时要注意
9delta_yawstable_yaw += value小角度转向
10LQR_k0lqr_k[0] = value位置误差反馈
11LQR_k1lqr_k[1] = value速度误差反馈
12LQR_k2lqr_k[2] = value俯仰角误差反馈
13LQR_k3lqr_k[3] = value俯仰角速度反馈

注意:页面显示 YAW_kp,但代码里 index 8 设置的是 imu_zero;真正的 k_yaw 当前没有被 Web 页面修改。这个地方建议后续修正文案或增加单独的 k_yaw 调参入口。

10. 推荐学习路线

第 1 天:跑通工程

目标:固件能编译、烧录、串口有日志。

阅读:

  • README.md
  • README_CN.md
  • main/Kconfig.projbuild
  • main/CMakeLists.txtCONFIG_BOARD_TYPE_HOVER 分支

实验:

idf.py set-target esp32s3
idf.py menuconfig
idf.py build
idf.py -p <PORT> flash monitor

检查点:

  • 串口能看到 HOVER 标签日志
  • LCD 能播放启动表情
  • 按键/触摸能触发配网或聊天开关

第 2 天:理解板级初始化

目标:知道每个外设在哪里初始化。

阅读:

  • hover_board.cc
  • board_config.h
  • boards/common/wifi_board.cc
  • boards/common/button.cc
  • boards/common/imu.*

实验:

  • 修改启动表情:display_->SetEmotion("launch")
  • 修改长按 NVS 清除时间:kLongPressResetMs
  • 打印 IMU 姿态到日志

第 3 天:理解串口电机协议

目标:知道如何给舵机和轮电机发命令,如何读取反馈。

阅读:

  • xgo.h
  • xgo.ccSendMotorCommand()
  • ReadWheelState()
  • ReadMotorState()
  • WriteByte_P_V()
  • xgo_rx()

实验顺序:

  1. 只测试头部舵机 ID 3。
  2. 读取轮电机 ID 1/2 的速度和位置。
  3. 小力矩输出,观察左右轮方向是否一致。

第 4 天:理解平衡控制

目标:知道 Hover 为什么能站住。

阅读:

  • InitializeController()
  • update_state()
  • xgo_control()

理解状态量:

位置误差: wheel_x - stable_pos
速度误差: wheek_vx - vx
角度误差: pitch - pitch_ref
角速度误差: dq

实验:

  • 在 Web 页面观察 pitch、roll、yaw
  • 小幅调整 imu_zero
  • 小幅调整 LQR_k2LQR_k3

建议先不要改 LQR_k0,位置反馈过大容易让车体出现往返冲击。

第 5 天:开发新动作

目标:通过 MCP 或 Web API 增加一个简单动作。

示例:新增“点头/转头”动作。

InitializeTools() 中添加:

mcp_server.AddTool("self.robot.look_left",
    "让 RIG-Hover 向左看",
    PropertyList(),
    [this](const PropertyList& properties) -> ReturnValue {
        target_head_pos = 30;
        return true;
    });

更好的方式是做成带参数工具:

mcp_server.AddTool("self.robot.set_motion_target",
    "设置 RIG-Hover 的移动和转向目标",
    PropertyList({
        Property("distance", kPropertyTypeInteger, -20, 20),
        Property("yaw", kPropertyTypeInteger, -90, 90),
    }),
    [this](const PropertyList& properties) -> ReturnValue {
        stable_pos += properties["distance"].value<int>();
        stable_yaw += properties["yaw"].value<int>();
        return true;
    });

11. 调参建议

调 RIG-Hover 时,要先确认机械、电机方向和 IMU 坐标系,再调参数。

11.1 调试前检查

  1. 轮子悬空时,左右轮反馈速度方向是否符合预期。
  2. 机身前倾时,pitch 的正负方向是否与控制器预期一致。
  3. target_head_pos = 0 时,头部是否居中。
  4. imu_zero 是否让机器人在自然直立姿态附近平衡。
  5. 摔倒保护是否能快速切断力矩。

11.2 参数顺序

推荐顺序:

  1. imu_zero
  2. LQR_k2
  3. LQR_k3
  4. LQR_k1
  5. LQR_k0
  6. k_yaw

含义:

  • imu_zero 决定平衡参考角。
  • LQR_k2 决定对俯仰角偏差的恢复力度。
  • LQR_k3 决定阻尼,太小会摆,太大反应迟钝。
  • LQR_k1 抑制速度偏差。
  • LQR_k0 把机器人拉回目标位置。
  • k_yaw 控制航向保持力度。

11.3 安全范围

建议每次只小幅调整:

imu_zero: 每次 0.5 deg
LQR_k2: 每次 1~3
LQR_k3: 每次 0.1~0.3
LQR_k1: 每次 0.5~1
LQR_k0: 每次 50~100

如果出现高频抖动,优先降低 LQR_k2 或提高一点 LQR_k3。如果出现慢速来回游走,检查 imu_zero、轮速反馈和 LQR_k0

12. 常见开发任务

12.1 修改 Hover 引脚

改:

main/boards/hover/board_config.h

重点检查:

  • UART TX/RX 是否与电机总线一致
  • IMU I2C 是否和摄像头 SCCB 分开
  • Touch GPIO 是否被其他外设复用

12.2 新增表情

放入:

main/boards/hover/emoji/

然后在需要的地方调用:

display_->SetEmotion("your_emotion_name");

如果是新的 EAF 资源,还要检查:

main/boards/hover/240_240/emote.json

12.3 修改唤醒词模型

RIG-Hover 当前模型路径:

main/boards/hover/wakenet/wn9_xiaolutongxue

构建系统中对应:

set(WAKENET_MODEL "wn9_xiaolutongxue")
set(WAKENET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_DIR}/wakenet/${WAKENET_MODEL}")

12.4 Web 调试变量

改:

main/boards/hover/hover_debug_server.cc

需要同步修改:

  • VAR_LABELS
  • set_handler() 中的 switch(index)
  • 必要时增加 /api/data 返回字段

建议把 index 8 的标签从 YAW_kp 改成 imu_zero,或者把代码改成真正控制 k_yaw

12.5 增加新的运动模式

推荐做法:

  1. xgo.cc 中新增目标变量,比如 motion_mode
  2. 在 MCP 工具里只设置目标变量。
  3. xgo_control() 中根据 motion_mode 改变 stable_posstable_yawvx
  4. 保留 robot_state == 0 清零力矩逻辑。

不要在 MCP 回调里长时间 vTaskDelay(),否则会影响应用响应。

13. 代码阅读地图

flowchart TD
    A["app_main / Application"] --> B["Board::GetInstance"]
    B --> C["HoverBoard"]
    C --> D["InitializeSpi + LCD + EmoteDisplay"]
    C --> E["InitializeButtons"]
    C --> F["InitializeTools / MCP"]
    C --> G["InitializeUart"]
    C --> H["InitializeController"]
    C --> I["imu_init"]
    C --> J["xgo_control task"]
    C --> K["xgo_rx task"]
    C --> L["imu_read_once task"]
    J --> M["update_state"]
    M --> N["LQR balance control"]
    N --> O["sendWheelTor"]
    K --> P["ReadWheelState / ReadMotorState feedback"]
    F --> Q["self.robot.move / rotate / head_angle"]
    C --> R["hover_debug_server_start"]

14. 二次开发注意事项

  • RIG-Hover 是自平衡机器人,任何电机方向、IMU 方向、零点角度错误都会导致倒车或剧烈抖动。
  • 先做悬空测试,再做手扶测试,最后才放地独立站立。
  • 不要一开始就大幅提高 LQR 参数。
  • Web 调参服务器默认端口 80,只适合局域网开发调试,不建议直接暴露公网。
  • hover_debug_server.cc 中部分中文注释出现编码异常,不影响编译,但建议统一保存为 UTF-8。
  • 旧 PID 代码被注释保留,学习时很有价值;实际运行主路径以 LQR 为准。
  • xgo_control() 是实时控制路径,避免加入日志刷屏、动态内存分配或耗时网络操作。
  • 运动目标应通过 stable_posstable_yawvx 等目标量间接影响控制,而不是绕过平衡控制直接写轮电机力矩。

15. 建议补充到仓库的文档

可以新增以下文件:

docs/content/RIG-Hover Developer Guide.md
docs/content/RIG-Hover Tuning Guide.md
docs/content/RIG-Hover Hardware Bring-up.md

也可以在 README_CN.md 中增加一节:

## RIG-Hover 快速开发入口

- 板级入口:`main/boards/hover/hover_board.cc`
- 平衡控制:`main/boards/hover/xgo.cc`
- Web 调参:`main/boards/hover/hover_debug_server.cc`
- 引脚配置:`main/boards/hover/board_config.h`
- 构建选择:`idf.py menuconfig -> RIG-Omni -> Board Type -> RIG-Hover`

16. 参考链接

17. 项目信息

现在就开始打造属于你的 AI 机器人吧!\ 只需一台3D打印机,就能拥有一个会听、会动、有“灵魂”的伙伴。

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

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