diff --git a/README.md b/README.md
index 5084ced..bb6e78d 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,12 @@
```
invicta_ws/src/
├── invicta_msgs/ 消息与共享值域(只用 3 条自定义消息,其余复用 sensor_msgs)
-├── invicta_remote/ 遥控/设备协议节点:UDP + JSON ↔ ROS,厂家错误码、状态上报(待实现)
+├── invicta_remote/ 遥控/设备协议节点:UDP + JSON ↔ ROS,厂家错误码、状态上报
+│ config/invicta_remote.toml 节点参数
├── invicta_deploy/ RL 控制节点:状态机 + ONNX 推理 + 安全检查(不碰网络协议)
+│ config/invicta_deploy.toml 节点参数(含机器人模型/策略)
└── invicta_hardware/ 关节/IMU 数据源 + 关节指令执行端(骨架,硬件 I/O 待实现)
+ config/invicta_hardware.toml 节点参数(含电机/CAN)
```
这样拆的依据:协议面是平台级接口(控制 + 上报 + 充电/休眠/导航),会跟厂家版本演进;控制面要求 200 Hz 稳定运行、最怕重启。两者生命周期和变更节奏不同。
@@ -55,22 +58,44 @@ invicta_hardware ──/joint_states (200 Hz)──→ invicta_deploy ──/joi
上游厂商侧口径:Lite3 的数据面上限 200 Hz 且必须整除 1000(内部 1 kHz)。M20 实机的真实上报频率请在接上硬件后用 `rostopic hz` 实测。
-## 关键约定:硬件约定 vs policy 约定
+## 配置:每个节点一份自己的 TOML
-关节量有两套约定,**必须只施加一次**:
+原来的 `config/*.yaml`(ROS 参数表)换成 TOML,**粒度不变:每个节点一份,节点自己决定读什么**。
+没有共享配置包,节点之间不通过配置文件耦合:
-- 硬件约定:电机方向、编码器零点、单位(CAN 原始值)。
-- policy 约定:与 `policy.onnx` 训练一致(rad / rad/s / Nm)。
+| 文件 | 归属 | 内容 |
+|---|---|---|
+| `invicta_deploy/config/invicta_deploy.toml` | RL 节点 | 关节表与标定、腿的运动学(IK)、时序与各状态增益、站立后自动进 RL、策略与步态、本节点话题/队列/控制周期 |
+| `invicta_remote/config/invicta_remote.toml` | 协议节点 | UDP 端口、心跳超时、未实现项语义、本节点话题、四路状态上报频率 |
+| `invicta_hardware/config/invicta_hardware.toml` | 硬件节点 | 关节表与硬件标定、每条腿挂的总线/CANID、**电机量程与 CAN 参数**、本节点话题与发布频率 |
-默认由 `invicta_hardware` 承担换算,话题上流动的就是 policy 约定;因此 `invicta_deploy` 的 `calibration/joint_dir` 全 1、`joint_offset_deg` 全 0、`auto_calibrate=false`。若反过来让 RL 节点承担,则填 M20 的表并打开 `auto_calibrate`:
+launch 只传一个路径:``;
+不传则默认取本包 `config/` 下的同名文件。TOML 解析用 toml++,以 **header-only 方式**使用(`-DTOML_HEADER_ONLY=1`),CMake 里只有一句 `find_package(tomlplusplus REQUIRED)`——库源码不进仓库,也不引入运行时动态库依赖。
-| | 值 |
-|---|---|
-| `joint_dir` | `[1,1,-1,1, 1,-1,1,-1, -1,1,-1,1, -1,-1,1,-1]` |
-| `joint_offset_deg` | `[-25,-131,160,0, 25,-131,160,0, -25,131,-160,0, 25,131,-160,0]` |
+改动节奏:**改 TOML 不用重编**;改各节点的配置头(`deploy_config.h` / `remote_config.h` / `hardware_config.h`)会自动触发该节点重编。
-两条 M20 遗留语义保持原样:轮关节(下标 `%4==3`)指令 `kp` 强制 0(`wheel_kp_zero`);关节顺序固定为 `fl/fr/hl/hr × hipx/hipy/knee/wheel`,与 `policy.onnx` 绑定。
+两个 TOML 结构坑(已在文件里注释):① 数组表之后的裸键会归到最后一个元素,所以 `[joint_limits]`/`[leg]` 必须放在 `legs` 段之前;② 整数键要加引号(如 `"4097"`),roslaunch 不接受整数键。
+## 关键约定:硬件约定 vs 模型约定(2 自由度腿)
+本构型每条腿只有**摆臂 + 轮**两个关节(4 腿 × 2 = 8 关节),模型约定为:
+
+- 摆臂角 θ=0 时摆臂竖直向下,θ 增大表示摆臂向外倒开;髋轴到轮轴的高度 h = L·cos(θ)。
+- 站立:h = `[leg].stand_height`(默认 0.48 m,L=0.50 m → ±16.3°)。
+- 趴下:h = `[leg].lie_height`(默认 0.10 m → ±78.5°),**前腿向前倒、后腿向后倒**(由 `[[legs]].splay_sign` 决定,与 M20 相反)。
+- 轮关节指令 `kp` 恒为 0(速度/力矩模式,`wheel_kp_zero`)。
+
+硬件约定(电机方向、机械零点)在 `[[legs]]` 里逐关节给:
+
+```toml
+[[legs]]
+name = "fl"
+splay_sign = 1.0 # 模型姿态:趴下时向前倒
+swing = { dir = 1.0, offset_deg = 0.0 } # 硬件:电机正方向 / 机械零点
+wheel = { dir = 1.0, offset_deg = 0.0 }
+```
+
+指令换算:`q_cmd = dir · θ_model + offset`,反馈反向(`invicta_interface` 一处完成)。
+第一次上机先按默认值跑,用 idle 打印的关节角与实测对照,再填 `dir`/`offset_deg`。
## 步态 = RL 模型
厂家协议里的 `GaitParam` 不是简单参数,**每个步态对应一个 RL 模型**:
@@ -95,6 +120,27 @@ source devel/setup.bash
依赖:`roscpp`、`roslib`、`sensor_msgs`、`std_msgs` + 自建 `invicta_msgs`。`eigen`、`onnxruntime 1.22`(x86/aarch64)、`policy.onnx` 已 vendor,无需联网。
+### 一次性依赖:toml++(系统安装,源码不入仓库)
+
+三个节点都用 `find_package(tomlplusplus REQUIRED)` 找它,**以 header-only 方式编译**(实现编进节点里),
+因此目标机不需要额外部署 `libtomlplusplus.so`。
+
+```bash
+# 方式一:Ubuntu 22.04+ 有官方包
+sudo apt install libtomlplusplus-dev
+
+# 方式二:目标机是 Ubuntu 20.04(Noetic 基线)或要交叉编译 aarch64 —— 源里没有此包,源码编译安装到系统
+# 源码自行获取(不放进本仓库),例如 tomlplusplus 3.4.0
+cmake -S tomlplusplus -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local
+cmake --build build -j
+sudo cmake --install build # 装头文件 + CMake 配置(tomlplusplusConfig.cmake)
+
+# 方式三:离线镜像里预装 .deb(只含头文件与 CMake 配置,123 KB)
+sudo dpkg -i libtomlplusplus-dev_3.4.0+ds-0.2build2_amd64.deb
+```
+
+装完 `cmake --find-package ... tomlplusplus` 能找到即可;`catkin_make` 会在 configure 阶段报缺库并停下。
+
## 运行
```bash
@@ -120,29 +166,25 @@ python3 src/invicta_remote/scripts/protocol_udp_test.py --steer 0.3,0,0 --motion
## 已验证到什么程度
-在 `localhost/custom-ros-noetic:v1.0`(Ubuntu 20.04 + ROS Noetic)容器里实测:
+在 `localhost/custom-ros-noetic:v1.0`(Ubuntu 20.04 + ROS Noetic)容器里实测(8 关节 + 每节点自己的 TOML):
| 项目 | 结果 |
|---|---|
-| 四个包 catkin_make | 通过 |
-| `ldd` 解析 `libonnxruntime.so.1` | 通过(RPATH 生效) |
-| `/joint_states` | 200.001 Hz,关节名/顺序正确 |
-| `/joint_commands` | 站立 200 Hz;RL 状态 50.001 Hz,策略输出正常(kp=80,kd=2/0.6) |
-| `/motion_state` | 20.000 Hz,RL 时 `motion_state=17`、`gait=4097` |
-| 协议:心跳 / 轴指令 / MotionParam | 回 0,状态机正确切换(空闲→站立→RL) |
-| 状态上报 `1002/3,4,5,6` | 2 / 10 / 2 / 2 Hz;RL 时 `BasicStatus.MotionState=17`、切步态后 `Gait` 同步变化 |
-| 协议:非 RL 下切步态 | 回 `0xE008 Not allowed operation`(57352) |
-| 协议:楼梯步态 | 协议节点回 0(已接收),RL 节点拒绝并记录"模型尚未注册" |
-| 协议:照明灯 / 导航模式 / 未知命令 | `0xE00A`(57354)/ `0xE00A` / `0xE003`(57347) |
-| 手柄实测(真遥控器) | 心跳、摇杆、MotionParam、照明灯、使用模式报文均能收到并正确应答 |
-
-**未验证**:真实电机与 IMU(`invicta_hardware` 的 `ReadHardware/WriteHardware` 还是 TODO)、实机上报频率、新机器人几何参数、状态上报(未实现)。
+| 四个包 catkin_make | 干净构建通过(`rc=0`) |
+| 三份配置加载 | deploy:`dof=8 legs=4 L=0.500 stand=0.480 pre=0.200 lie=0.000`;remote:UDP 30000、上报 2/10/2/2 Hz;hardware:8 关节、`fl: swing can0#1 / wheel can0#3`、电机 P65MAXS/W190S、CAN 2 路 @1M/5M |
+| IK 目标角 | 站立 ±16.3°、收腿 ±66.4°、**趴下 ±90.0°(摆臂水平)**,前腿 +、后腿 −(与 M20 相反)✓ |
+| 趴下指令实测 | 订阅 `/joint_commands` 采样,四个摆臂峰值均为 **1.5708 rad = 90.0°**,轮关节 0 ✓ |
+| `/joint_states` | 200.000 Hz,8 个关节名与配置一致 |
+| `/motion_state` | 20.000 Hz |
+| 协议 | 心跳回 0;`MotionParam=1` → idle→standup;`MotionParam=4` → standup→liedown |
+| 站立后自动进 RL 自检 | 生效(骨架发零位 → `[RL] 暂不进入 RL:关节 fl_swing 未到站立位姿`,正确地拦住) |
+**未验证**:真实电机与 IMU(`invicta_hardware` 的 `ReadHardware/WriteHardware` 仍是 TODO)、实机上报频率、**新机器人几何与 `dir`/`offset_deg` 的实测标定**(TOML 里的 0.50 m / 0.48 m 等是待实测替换的占位值)。
## 与上游 M20 的差异
保留:状态机五状态、ONNX 策略与关节顺序/动作尺度、安全检查、厂家协议的头/JSON 结构与心跳、`eigen`/`onnxruntime`/`policy.onnx`。
-删除或改写:ROS2→ROS1;`drdds` 消息→标准消息 + 3 条自定义;电池订阅(上游死代码);键盘控制模式;`third_party/gamepad`(未被引用);Mujoco 仿真与 `M20_description`;Lite3 三个包;`backward.hpp` 死代码;M20 几何硬编码→ROS 参数;原厂控制字→平台无关控制字;**UDP/JSON 协议层从 RL 进程拆到 `invicta_remote`**。
+删除或改写:**16 关节三关节腿 → 8 关节(摆臂 + 轮)2 自由度腿,IK 换成单连杆反解、新增 splay_sign 倒向**;**死参数(机身长宽/髋偏置/轮半径/力矩限制/通用策略参数)全部删除**;ROS2→ROS1;`drdds` 消息→标准消息 + 3 条自定义;电池订阅(上游死代码);键盘控制模式;`third_party/gamepad`(未被引用);Mujoco 仿真与 `M20_description`;Lite3 三个包;`backward.hpp` 死代码;M20 几何硬编码→ROS 参数;原厂控制字→平台无关控制字;**UDP/JSON 协议层从 RL 进程拆到 `invicta_remote`**。
## 已知缺口
diff --git a/src/invicta_deploy/CMakeLists.txt b/src/invicta_deploy/CMakeLists.txt
index 2d94e00..a8da0c0 100644
--- a/src/invicta_deploy/CMakeLists.txt
+++ b/src/invicta_deploy/CMakeLists.txt
@@ -31,10 +31,18 @@ catkin_package(
CATKIN_DEPENDS roscpp roslib sensor_msgs invicta_msgs
)
+# TOML 解析库:系统安装(见 README「依赖」),CMake 里体现为 required
+find_package(tomlplusplus REQUIRED)
+get_target_property(TOMLPLUSPLUS_INCLUDE_DIR tomlplusplus::tomlplusplus INTERFACE_INCLUDE_DIRECTORIES)
+include_directories(${TOMLPLUSPLUS_INCLUDE_DIR})
+# 以 header-only 方式使用:实现编进本节点,避免引入运行时动态库依赖(目标机无需部署 .so)
+add_definitions(-DTOML_HEADER_ONLY=1)
+
set(THIRD_PARTY ${CMAKE_CURRENT_SOURCE_DIR}/third_party)
set(ORT_ROOT ${THIRD_PARTY}/onnxruntime/${BUILD_PLATFORM})
include_directories(
+ include
include/types
include/utils
interface
@@ -42,7 +50,6 @@ include_directories(
interface/robot/hardware
interface/user_command
state_machine
- state_machine/parameters
run_policy
${THIRD_PARTY}/eigen
${ORT_ROOT}/include
@@ -51,9 +58,17 @@ include_directories(
file(GLOB_RECURSE STATE_MACHINE_SRC "state_machine/*.cpp")
+# 把本节点自己的配置头列进源列表:改该头会触发重编(改 TOML 不用重编)
+set(NODE_CONFIG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/include/config/deploy_config.h")
+if(NOT EXISTS "${NODE_CONFIG_HEADER}")
+ message(WARNING "invicta_deploy: 找不到本节点配置头")
+ set(NODE_CONFIG_HEADER "")
+endif()
+
add_executable(invicta_deploy
main.cpp
${STATE_MACHINE_SRC}
+ ${NODE_CONFIG_HEADER}
)
add_library(onnxruntime SHARED IMPORTED)
@@ -88,6 +103,6 @@ install(DIRECTORY ${ORT_ROOT}/lib/
install(DIRECTORY policy
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
-install(DIRECTORY config launch
+install(DIRECTORY launch config
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
diff --git a/src/invicta_deploy/README.md b/src/invicta_deploy/README.md
index 65bfe34..5368089 100644
--- a/src/invicta_deploy/README.md
+++ b/src/invicta_deploy/README.md
@@ -7,7 +7,7 @@ RL 控制节点:ROS 回调跑在 main 线程,状态机跑在 200 Hz 控制
## 源码结构
```
-main.cpp ros::init("invicta_deploy") + 状态机启动
+main.cpp ros::init + 加载 invicta.toml + 启动状态机
include/types/ RobotBasicState / RobotAction / UserCommand / 枚举
include/utils/ basic_function(Eigen 工具)、safe_controller(1 kHz 安全检查)、
motion_state_feedback(状态回执)
@@ -21,7 +21,6 @@ interface/user_command/
state_machine/state_machine_base.h 控制线程框架(timerfd + epoll,默认 5 ms)
state_machine/state_base.h 状态基类
state_machine/quadruped_wheel/ 五个状态 + 状态机装配(invicta_state_machine.hpp)
-state_machine/parameters/ 控制参数(全部来自 ROS 参数,默认值 = 上游 M20)
run_policy/ ONNX 推理:policy_runner_base + invicta_policy_runner
policy/policy.onnx 策略模型(与关节顺序/动作尺度绑定)
scripts/smoke_test.sh 无硬件自检
@@ -102,41 +101,40 @@ gait:
> 实测(把同一份 ONNX 复制成另一个文件模拟新模型):预加载成功、来回切换 3 轮共 6 次,`gait` 正确跟随、`/joint_commands` 保持 50 Hz、无报错。当前仓库配置只登记 `0x1001`,其余步态请求会被拒绝。
-## 参数(私有命名空间,`config/invicta_deploy.yaml`)
+## 参数(本节点自己的 `config/invicta_deploy.toml`)
-| 参数 | 默认 | 说明 |
-|---|---|---|
-| `control_period_ms` | 5 | 控制周期,5 ms = 200 Hz |
-| `policy_path` | 空 | 空 = `$(find invicta_deploy)/policy/policy.onnx` |
-| `topics/joint_states` `topics/imu` `topics/joint_commands` | 见 yaml | 数据面话题 |
-| `topics/motion_command` / `topics/motion_state` | `motion_command` / `motion_state` | 与协议节点的接口 |
-| `queues/motion_command` | 1 | 指令只取最新值 |
-| `queues/joint_commands` / `queues/state` | 1 / 10 | 指令队列 1(新鲜度优先),状态队列 10 |
-| `motion_state_report_hz` | 20.0 | `/motion_state` 频率 |
-| `robot_version` | `INVICTA` | 协议 `BasicStatus.Version` |
-| `gait/default` `gait/policy_map` | 4097 / 两项 | 步态与模型 |
-| `joint_names` | 16 个 | 关节顺序,与 policy 绑定 |
-| `calibration/*` | 恒等 | 见根 README「硬件约定 vs policy 约定」 |
-| `wheel_kp_zero` | true | 轮关节 `kp` 强制 0 |
-| `timing/joint_damping_duration` | 3.0 | 阻尼过渡时长(0.1~30 s) |
-| `geometry/*` `gains/*` `limits/*` `timing/stand_duration` `timing/liedown_duration` | M20 值 | 换机器人主要改这里 |
+本节点不再读 ROS 参数表,也不读别的节点的配置;launch 只传 `config_file`(默认取本包同名文件)。
-启动时会打印实际读到的 `joint_dir`、`joint_offset_deg`、话题名与模型路径。
+| TOML 段 | 内容 |
+|---|---|
+| `[robot]` | 关节名与顺序(8 个)、机型标识 |
+| `[joint_limits]` | 逐关节位置/速度限位(顺序同 joint_names) |
+| `[leg]` `[leg.timing]` `[leg.gains]` | 2 自由度腿的几何(摆臂长度、站/收腿/趴下高度)、时序、各状态 PD 增益 |
+| `[[legs]]` ×4 | 每腿的摆臂/轮关节名、`splay_sign`(倒向)、`dir`/`offset_deg`(硬件标定) |
+| `[rl]` | 站立完成后自动进 RL 的开关与自检阈值 |
+| `[policy]` | 模型路径、decimation、观测缩放、关节顺序、动作尺度、默认姿态、kp/kd |
+| `[gait.policies]` | 步态 → 模型文件(空 = 该步态不可用) |
+| `[topics]` `[queues]` `[control]` | 本节点话题、队列、控制周期、状态回报频率 |
+启动时打印配置摘要与各腿目标角,便于第一次上机核对。改 TOML 不需重编;改 `deploy_config.h` 会自动重编。
## 关节顺序(与 `policy.onnx` 绑定,勿改)
+机器人顺序(`robot.joint_names`,即 `/joint_states`、`/joint_commands` 的下标):
+
```
-0 fl_hipx 1 fl_hipy 2 fl_knee 3 fl_wheel
-4 fr_hipx 5 fr_hipy 6 fr_knee 7 fr_wheel
-8 hl_hipx 9 hl_hipy 10 hl_knee 11 hl_wheel
-12 hr_hipx 13 hr_hipy 14 hr_knee 15 hr_wheel
+0 fl_swing 1 fl_wheel
+2 fr_swing 3 fr_wheel
+4 hl_swing 5 hl_wheel
+6 hr_swing 7 hr_wheel
```
-策略内部顺序不同(12 个腿关节在前、4 个轮在后),`InvictaPolicyRunner` 用 `robot2policy_idx` / `policy2robot_idx` 映射。
+策略内部顺序由 `[policy].joint_order` 给出(默认 4 个摆臂在前、4 个轮在后);
+`InvictaPolicyRunner` 用置换矩阵在两种顺序之间映射,轮关节位置默认不进观测(`zero_obs_wheels`)。
+**模型文件放在本包 `policy/` 下**,TOML 里写相对路径(相对本包目录);每个步态可指向不同模型。
## 数据面行为
-- `/joint_states`:优先按 `name` 匹配关节(顺序可不同);`name` 缺失时按数组下标解析并告警一次;长度不等于 16 的帧丢弃;缺某个关节名时保留上一帧值并告警。
+- `/joint_states`:优先按 `name` 匹配关节(顺序可不同);`name` 缺失时按数组下标解析并告警一次;长度不等于关节数的帧丢弃;缺某个关节名时保留上一帧值并告警。
- `/imu/data`:`orientation_covariance[0] == -1` 视为"不提供姿态",丢弃该帧并告警(不伪造姿态);四元数模长近零的帧也丢弃。rpy 由四元数换算(ZYX,与 `RpyToRm` 一致)。
- `/joint_commands`:`control_word` 见 `invicta_msgs/control_word.h`;启动时依次发 `kDisable → kErrorReset → kEnable`,运行时恒为 `kMotorControl`。
@@ -144,5 +142,5 @@ gait:
```bash
source devel/setup.bash
-bash src/invicta_deploy/scripts/smoke_test.sh # 三节点 + 话题/频率/协议/ONNX
+bash src/invicta_deploy/scripts/smoke_test.sh # 三节点 + 话题/频率/协议/ONNX/配置摘要
```
diff --git a/src/invicta_deploy/config/invicta_deploy.toml b/src/invicta_deploy/config/invicta_deploy.toml
new file mode 100644
index 0000000..78cc118
--- /dev/null
+++ b/src/invicta_deploy/config/invicta_deploy.toml
@@ -0,0 +1,145 @@
+# =============================================================================
+# invicta_deploy(RL 节点)自己的配置
+# 只放本节点需要的参数;话题由本节点声明,别的节点不读这份文件
+# 关节电机/CAN 相关参数在 invicta_hardware/config/invicta_hardware.toml
+# =============================================================================
+
+[robot]
+name = "invicta"
+version = "INVICTA" # 协议 BasicStatus.Version
+# 关节顺序:下标即 /joint_states、/joint_commands 里的索引
+# 每腿两关节:摆臂 swing + 轮 wheel;腿序 前左(fl) / 前右(fr) / 后左(hl) / 后右(hr)
+joint_names = [
+ "fl_swing", "fl_wheel",
+ "fr_swing", "fr_wheel",
+ "hl_swing", "hl_wheel",
+ "hr_swing", "hr_wheel",
+]
+
+# ------------------------------------------------------------------ 关节限位
+# 顺序与 joint_names 一致;轮关节的 lower/upper 填 ±100 表示不限(用 vel_limit 约束)
+[joint_limits]
+lower = [-1.40, -100.0, -1.40, -100.0, -1.40, -100.0, -1.40, -100.0] # [rad]
+upper = [ 1.40, 100.0, 1.40, 100.0, 1.40, 100.0, 1.40, 100.0] # [rad]
+vel_limit = [ 22.40, 100.0, 22.40, 100.0, 22.40, 100.0, 22.40, 100.0] # [rad/s]
+
+# ------------------------------------------------------------------ 腿的运动学(2 自由度:摆臂 + 轮)
+# 模型约定:摆臂角 θ = 0 时摆臂竖直向下、轮轴在髋轴正下方;θ 增大表示摆臂向外倒开,
+# 髋轴到轮轴的高度 h = L·cos(θ);反解 θ(h) = splay_sign · acos(h / L)
+# 注意:TOML 里数组表之后的裸键会归到最后一个元素,所以本节必须放在 legs 段之前
+[leg]
+swing_link_len = 0.50 # L:髋轴 → 轮轴 的距离 [m](必须实测)
+wheel_radius = 0.06 # 轮半径 [m],只用于打印离地高度,IK 不用
+
+stand_height = 0.48 # 站立:髋轴 → 轮轴 高度 [m],应 ≤ L
+pre_height = 0.20 # 起立中间的收腿姿态高度 [m]
+lie_height = 0.00 # 趴下:0 = 摆臂**水平**(θ = acos(0) = 90°)
+ # 若摆臂几何有偏置(轮轴不在摆臂轴线上),这里填该偏置
+
+[leg.timing]
+stand_duration_s = 1.5 # 起立每一阶段(收腿 / 撑起)的样条时长
+pre_hold_s = 0.0 # 收腿姿态额外保持(0 = 不停留)
+liedown_duration_s = 2.0 # 趴下样条时长
+damping_hold_s = 3.0 # 关节阻尼保持多久后回 idle(缓慢松下 → 着地 → 自由)
+
+[leg.gains]
+stand_kp = 200.0 # 起立:摆臂位置刚度 [Nm/rad]
+stand_kd = 4.0 # 起立:摆臂阻尼
+lie_kp = 300.0 # 趴下:摆臂位置刚度
+lie_kd = 4.5 # 趴下:摆臂阻尼
+damping_kd_leg = 4.0 # 阻尼态:摆臂 kd
+damping_kd_wheel = 1.0 # 阻尼态:轮 kd(把轮刹住)
+wheel_kp_zero = true # 轮的 kp 强制置 0(速度/力矩模式)
+
+# ------------------------------------------------------------------ 站立后自动进 RL
+# 原厂机制:站立到位并自检通过即自动切 RL,不必等遥控器指令
+[rl]
+auto_enter = true
+enter_delay_s = 0.5 # 到站立位姿后再稳定多久 [s]
+require_pose = true # 是否检查摆臂角已到站立位姿(无硬件台架时可关)
+joint_tolerance = 0.15 # 摆臂角容差 [rad]
+velocity_tolerance = 1.0 # 摆臂角速度容差 [rad/s]
+require_imu = true # 是否检查 IMU 数据有效
+
+# ------------------------------------------------------------------ 每腿标定
+# 摆臂指令角 = dir · θ_model + offset_deg,θ_model = splay_sign · acos(h/L)
+# splay_sign:模型姿态约定。趴下/收腿时摆臂往哪边倒——本题要求「前腿向前、后腿向后」,
+# 即前腿 +1、后腿 −1(与 M20 相反)
+# dir :硬件约定,电机正方向与模型正方向的关系(±1)
+# offset_deg:机械零点偏置 [deg],第一次上机按 idle 打印角与实测对照后填写
+[[legs]]
+name = "fl"
+swing_joint = "fl_swing"
+wheel_joint = "fl_wheel"
+splay_sign = 1.0
+swing = { dir = 1.0, offset_deg = 0.0 }
+wheel = { dir = 1.0, offset_deg = 0.0 }
+
+[[legs]]
+name = "fr"
+swing_joint = "fr_swing"
+wheel_joint = "fr_wheel"
+splay_sign = 1.0
+swing = { dir = -1.0, offset_deg = 0.0 }
+wheel = { dir = -1.0, offset_deg = 0.0 }
+
+[[legs]]
+name = "hl"
+swing_joint = "hl_swing"
+wheel_joint = "hl_wheel"
+splay_sign = -1.0
+swing = { dir = 1.0, offset_deg = 0.0 }
+wheel = { dir = 1.0, offset_deg = 0.0 }
+
+[[legs]]
+name = "hr"
+swing_joint = "hr_swing"
+wheel_joint = "hr_wheel"
+splay_sign = -1.0
+swing = { dir = -1.0, offset_deg = 0.0 }
+wheel = { dir = -1.0, offset_deg = 0.0 }
+
+# ------------------------------------------------------------------ 策略模型
+[policy]
+# 相对路径基于本包目录(模型放在 invicta_deploy/policy/ 下)
+path = "policy/policy.onnx"
+decimation = 4 # 每 N 个控制周期推理一次(200 Hz / 4 = 50 Hz)
+omega_scale = 0.25 # 观测里的角速度缩放
+dof_vel_scale = 0.05 # 观测里的关节速度缩放
+zero_obs_wheels = true # 轮关节位置不进观测(与 M20 同做法)
+
+# 策略内部关节顺序;与 joint_names 不同时在此列出(代码自动做置换)
+joint_order = [
+ "fl_swing", "fr_swing", "hl_swing", "hr_swing",
+ "fl_wheel", "fr_wheel", "hl_wheel", "hr_wheel",
+]
+# 以下四个数组的顺序 = joint_order
+action_scale = [0.25, 0.25, 0.25, 0.25, 5.0, 5.0, 5.0, 5.0]
+default_joint_pos = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 训练时的默认姿态 [rad]
+kp = [80.0, 80.0, 80.0, 80.0, 0.0, 0.0, 0.0, 0.0]
+kd = [ 2.0, 2.0, 2.0, 2.0, 0.6, 0.6, 0.6, 0.6]
+
+# ------------------------------------------------------------------ 步态 → RL 模型
+# 每个步态对应一个模型;空路径 = 该步态不可用(模型尚未训练)
+[gait]
+default = 4097 # 0x1001 基础(平地)
+[gait.policies]
+"4097" = "" # 0x1001 基础(平地) → policy.path
+"12290" = "" # 0x3002 平地(敏捷) → 暂共用
+
+# ------------------------------------------------------------------ 本节点的话题与节奏
+[topics]
+joint_states = "joint_states" # 订阅 sensor_msgs/JointState
+imu = "imu/data" # 订阅 sensor_msgs/Imu
+joint_commands = "joint_commands" # 发布 invicta_msgs/JointCommandArray
+motion_command = "motion_command" # 订阅 invicta_msgs/MotionCommand
+motion_state = "motion_state" # 发布 invicta_msgs/MotionState
+
+[control]
+period_ms = 5 # 控制周期,5 ms = 200 Hz
+state_report_hz = 20.0 # /motion_state 发布频率
+
+[queues]
+motion_command = 1
+joint_commands = 1
+state = 10
diff --git a/src/invicta_deploy/config/invicta_deploy.yaml b/src/invicta_deploy/config/invicta_deploy.yaml
deleted file mode 100644
index ff4cf0c..0000000
--- a/src/invicta_deploy/config/invicta_deploy.yaml
+++ /dev/null
@@ -1,105 +0,0 @@
-# invicta_deploy 参数(私有命名空间 ~,由 launch 加载到 /invicta_deploy/ 下)
-
-# ---- 控制周期 ----
-control_period_ms: 5 # 5 ms = 200 Hz,与策略 decimation=4(50 Hz)配套
-
-# ---- 策略 ----
-policy_path: "" # 留空 = $(find invicta_deploy)/policy/policy.onnx
-
-# ---- 话题 ----
-topics:
- joint_states: joint_states # 订阅 sensor_msgs/JointState
- imu: imu/data # 订阅 sensor_msgs/Imu
- joint_commands: joint_commands # 发布 invicta_msgs/JointCommandArray
- motion_command: motion_command # 订阅 invicta_msgs/MotionCommand(来自 invicta_remote)
- motion_state: motion_state # 发布 invicta_msgs/MotionState(给 invicta_remote)
-
-queues:
- motion_command: 1 # 指令只关心最新值
- joint_commands: 1 # 指令只关心最新值,队列不堆积
- state: 10 # 状态流按 200 Hz 设计,队列给足以吸收抖动
-
-# ---- 站立完成后自动进入 RL(原厂机制)----
-# 原厂不是等用户发 MotionParam=17,而是站立完成后自检通过就自动切 RL;用户指令也可以,
-# 但同样要满足自检。自检不通过时一直保持站立(/motion_state 报 1),每秒打印一次原因。
-rl:
- auto_enter: true
- enter_delay: 0.5 # 到达站立位姿后再稳定多久 [s]
- require_pose: true # 关节角必须到达站立位姿(无硬件台架时设为 false)
- joint_tolerance: 0.15 # 关节角容差 [rad]
- velocity_tolerance: 1.0 # 关节速度容差 [rad/s]
- require_imu: true # IMU 数据必须有效(无硬件台架时设为 false)
-
-# ---- 状态回报 ----
-motion_state_report_hz: 20.0 # /motion_state 发布频率(协议状态上报 2 Hz 时从这里取)
-robot_version: "INVICTA" # 对应协议 BasicStatus.Version
-
-# ---- 步态 → RL 模型 ----
-# 不同步态对应不同的 RL 模型:
-# 0x1001 基础(平地) / 0x3002 平地(敏捷) → 当前共用 policy_path 里的模型
-# 0x1003 楼梯 / 0x3003 楼梯(敏捷) → 模型尚未训练,请求会被拒绝并记录日志
-# 键是十进制,等号右边是模型文件路径(空 = 用 policy_path)
-gait:
- default: 4097 # 0x1001 基础,协议规定起立后回到基础步态
- # 键必须加引号(roslaunch 不接受整数键);值 = 模型文件路径,留空表示"没有模型"。
- # 只有这里提供了模型路径的步态才会在启动时加载、才允许切换;填了路径但加载失败的也不可用。
- policy_map:
- "4097": "" # 0x1001 基础(平地) → 复用 policy_path 的默认模型
- # "12290": "/path/to/flat_agile.onnx" # 0x3002 平地(敏捷),训练好后填这里
- # "4099": "/path/to/stairs.onnx" # 0x1003 楼梯
- # "12291": "/path/to/stairs_agile.onnx" # 0x3003 楼梯(敏捷)
-
-# ---- 关节顺序(索引即指令/状态数组下标,与 policy.onnx 绑定,勿随意改)----
-joint_names:
- [fl_hipx_joint, fl_hipy_joint, fl_knee_joint, fl_wheel_joint,
- fr_hipx_joint, fr_hipy_joint, fr_knee_joint, fr_wheel_joint,
- hl_hipx_joint, hl_hipy_joint, hl_knee_joint, hl_wheel_joint,
- hr_hipx_joint, hr_hipy_joint, hr_knee_joint, hr_wheel_joint]
-
-# ---- 关节标定 ----
-# 默认恒等:约定硬件约定(方向/零点/单位)由 invicta_hardware 负责,
-# 话题上流动的就是 policy 约定下的量。
-# 如果反过来让本节点承担 M20 的原始约定,就把下面两张表填成 M20 的值并打开 auto_calibrate,
-# 参考值(上游 M20,角度制):
-# joint_dir = [1,1,-1,1, 1,-1,1,-1, -1,1,-1,1, -1,-1,1,-1]
-# joint_offset_deg = [-25,-131,160,0, 25,-131,160,0, -25,131,-160,0, 25,131,-160,0]
-# 二者只能选一处施加,不要两边都做。
-calibration:
- joint_dir: [1.0,1.0,1.0,1.0, 1.0,1.0,1.0,1.0, 1.0,1.0,1.0,1.0, 1.0,1.0,1.0,1.0]
- joint_offset_deg: [0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0]
- auto_calibrate: false # 仅当上报的是原始编码器角(需要等 16 关节数据更新并做 ±360 回绕修正)时打开
- wrap_hip_deg: 140.0 # 标定用回绕阈值,对应 hipy(下标 %4==1)
- wrap_knee_deg: 164.0 # 标定用回绕阈值,对应 knee(下标 %4==2)
-
-# ---- 轮关节是否强制 kp=0(速度/力矩模式)----
-wheel_kp_zero: true
-
-# ---- 机器人几何(默认值来自上游 M20,换机器人时改这里)----
-geometry:
- body_len_x: 0.619
- body_len_y: 0.13
- hip_len: 0.104
- thigh_len: 0.25
- shank_len: 0.25
- pre_height: 0.12
- stand_height: 0.48
- wheel_link_len: 0.040575
-
-# ---- PD 增益(站立/摆动相)----
-gains:
- swing_leg_kp: [200.0, 200.0, 200.0]
- swing_leg_kd: [4.0, 4.0, 4.0]
-
-# ---- 限制 ----
-limits:
- fl_joint_lower: [-0.4363, -2.443, -2.758] # rad
- fl_joint_upper: [0.6109, 2.443, 2.758] # rad
- joint_vel_limit: [22.4, 22.4, 22.4] # rad/s
- torque_limit: [76.4, 76.4, 76.4] # Nm
- wheel_vel_limit: 100.0 # rad/s
-
-# ---- 时长 ----
-timing:
- stand_duration: 1.5 # 起立曲线时长(总时长约为 2 倍)
- liedown_duration: 2.0 # 趴下曲线时长(总时长约为 2 倍)
- joint_damping_duration: 3.0 # 阻尼过渡时长:松腿→着地→转空闲(0.1~30 s)
diff --git a/src/invicta_deploy/include/config/deploy_config.h b/src/invicta_deploy/include/config/deploy_config.h
new file mode 100644
index 0000000..a92106d
--- /dev/null
+++ b/src/invicta_deploy/include/config/deploy_config.h
@@ -0,0 +1,341 @@
+/**
+ * @file deploy_config.h
+ * @brief invicta_deploy 自己的配置(TOML):config/invicta_deploy.toml
+ *
+ * 只包含本节点需要的参数(关节表与标定、腿的运动学、状态机时序与增益、策略与步态、
+ * 本节点的话题/队列/控制周期)。本节点不读别的节点的配置文件。
+ */
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include