# RM75 + O6 目标软件架构与最小可行重构计划

本文承接工程理解阶段的结论，说明适合睿尔曼 RM75 机械臂与灵心巧手 O6 的目标软件架构、当前工程差距，以及最小可行重构计划。

当前阶段只定义架构和重构边界，不修改业务代码，不引入未确认的厂商 API 签名。

## 1. 目标软件架构

目标不是推翻现有工程，而是把已有 `RM/` 子工程中的机械臂接口化思路，扩展成“机械臂 + O6 手”的统一控制栈。

建议目录形态：

```text
applications/
  move_arm_then_grasp_task.cpp

core/
  coordinated_arm_hand_controller.h
  coordinated_arm_hand_controller.cpp
  motion_safety_guard.h
  motion_safety_guard.cpp

modules/
  arm_motion_service.h
  arm_motion_service.cpp
  o6_grasp_service.h
  o6_grasp_service.cpp
  arm_hand_task_planner.h
  arm_hand_task_planner.cpp

interfaces/
  robot_arm.h
  dexterous_hand.h
  tool_modbus_bus.h

drivers/
  realman/realman_robot_arm.h
  realman/realman_robot_arm.cpp
  realman/realman_tool_modbus_bus.h
  realman/realman_tool_modbus_bus.cpp
  linkerhand/o6_modbus_hand.h
  linkerhand/o6_modbus_hand.cpp
  linkerhand/o6_sdk_hand.h
  linkerhand/o6_sdk_hand.cpp

common/
  result.h
  robot_types.h
  hand_types.h
```

依赖方向固定为：

```text
main/applications -> core -> modules -> interfaces <- drivers
```

### 1.1 应用层

应用层只表达任务顺序，例如：

- `runMoveArmThenGraspTask()`
- `runArmSwingWithO6GraspTask()`

应用层禁止直接包含或调用：

- `rm_interface.h`
- `LinkerHandApi.h`
- 串口 API
- TCP Socket
- Modbus 寄存器读写
- RM_API2 原生函数

### 1.2 Core 层

Core 层负责流程编排、生命周期和安全入口。

建议核心类：

- `CoordinatedArmHandController`
- `MotionSafetyGuard`

职责：

- 组合机械臂动作和 O6 动作；
- 在运动前做速度、幅度、步长限制；
- 遇到失败时简单记录错误并停止当前流程；
- 不直接依赖 RM SDK 或 LinkerHand SDK。

### 1.3 Modules 层

Modules 层负责可测试的业务语义。

建议模块：

- `ArmMotionService`
  - `moveArmToJointTarget()`
  - `moveArmBySmallJointDelta()`
  - `returnArmToRecordedStartPose()`

- `O6GraspService`
  - `openO6HandToSafePose()`
  - `moveO6HandToPreGraspPose()`
  - `closeO6HandForPinchGrasp()`
  - `holdO6HandForObjectTransfer()`

- `ArmHandTaskPlanner`
  - 生成最小抓取任务的步骤列表；
  - 不访问任何真实硬件。

### 1.4 Interfaces 层

接口层定义抽象能力，不写硬件实现。

已有机械臂接口可以继续沿用：

- `IRobotArm`

建议新增：

- `IDexterousHand`
- `IToolModbusBus`

示意职责：

```text
IDexterousHand
  connect()
  disconnect()
  readIdentity()
  moveToJointPose()
  openToSafePose()

IToolModbusBus
  configureToolPowerAndModbus()
  readInputRegisters()
  writeRegisters()
  writeSingleRegister()
```

### 1.5 Drivers 层

Drivers 层承接真实 SDK、通信协议和寄存器细节。

建议实现：

- `RealManRobotArm`
  - 继续包装 `rm_init`、`rm_create_robot_arm`、`rm_movej`、`rm_movel` 等 RM_API2 调用。

- `RealManToolModbusBus`
  - 包装 RM75 工具端电源和工具端 Modbus。
  - 包含 `rm_set_tool_voltage(handle, 3)`。
  - 包含 `rm_set_modbus_mode(handle, 1, 115200, 10)`。
  - 包含 `rm_read_multiple_input_registers`、`rm_write_registers`、`rm_write_single_register`。

- `O6ModbusHand`
  - 通过 `IToolModbusBus` 操作 O6。
  - 管理 O6 右手 ID `0x27`。
  - 管理身份寄存器 `30..35`。
  - 管理 6 维关节姿态寄存器。

- `O6SdkHand`
  - 可选保留，用于 PC USB/CAN 直连基线。
  - 直接包装 `LinkerHandApi`。

## 2. 当前工程与目标架构的差距

### 2.1 已经符合目标架构的部分

`RM/` 子工程已经有比较清晰的机械臂分层：

- `IRobotArm` 已把机械臂能力抽象出来；
- `RealManRobotArm` 已把 RM_API2 细节压在 driver 层；
- `JsonCommandRunner` 已经只依赖 `IRobotArm`，并负责运动安全校验；
- `Result<T>`、`Pose`、`ArmState`、`ForceData` 已经具备公共类型雏形。

这些内容应复用，而不是重写。

### 2.2 主要差距

1. O6 还没有接口层。

   当前根目录 `src/main.cpp` 直接构造 `LinkerHandApi` 并执行抓取流程。它把应用逻辑、O6 姿态、力传感读取和硬件 API 混在入口文件里。

2. RM75 工具端 Modbus 还没有 driver 层。

   当前 `tools/realman_tool_modbus_o6_*` 里直接调用 RM_API2 的工具端电源、Modbus 模式和寄存器读写函数。这些逻辑应该进入 `RealManToolModbusBus`。

3. 机械臂和手部协调逻辑还在临时工具程序里。

   `tools/realman_tool_modbus_o6_grasp_with_arm_swing.cpp` 同时包含：

   - 参数解析；
   - RM SDK 初始化；
   - 工具端 RS485 配置；
   - O6 寄存器写入；
   - J2/J3 摆动目标生成；
   - 机械臂运动等待；
   - 手部抓取动作序列。

   这些职责需要拆到 application、core、modules、drivers。

4. CMake 形态尚未统一。

   根目录 CMake 同时管理 LinkerHand 示例、RealMan 示例、硬件测试；`RM/` 又有较干净的独立子工程。后续应选择一个稳定边界，避免正式控制栈和临时 demo 混在一起。

5. 手部公共类型缺失。

   目前只有机械臂公共类型，没有 `O6JointPose`、`O6HandIdentity`、`O6HandConfig`、`DexterousHandState` 等手部语义类型。

## 3. 最小可行重构计划

### Phase 1: 先建立接口和类型边界

目标：不搬业务代码，只建立未来代码该放的位置。

新增：

- `IDexterousHand`
- `IToolModbusBus`
- `O6JointPose`
- `O6HandConfig`
- `O6HandIdentity`

约束：

- 不引入未确认的新厂商 API。
- 不自动运行真实硬件。
- 不改现有 demo 行为。

验收标准：

- 新增接口可被 fake 实现用于单元测试。
- 应用层未来不需要包含 RM SDK 或 LinkerHand SDK 头文件。

### Phase 2: 抽出 RM75 工具端 Modbus driver

目标：把 RM75 工具端 RS485 访问从工具程序中移出。

新增：

- `RealManToolModbusBus`

迁移内容：

- `rm_set_tool_voltage(handle, 3)`
- `rm_set_modbus_mode(handle, 1, 115200, 10)`
- `rm_get_tool_RS485_mode`
- `rm_read_multiple_input_registers`
- `rm_write_registers`
- `rm_write_single_register`

验收标准：

- 工具端 Modbus 配置只存在于 driver 层。
- O6 driver 只依赖 `IToolModbusBus`。

### Phase 3: 抽出 O6 Modbus 手部 driver

目标：把 O6 寄存器语义从工具程序移到 driver。

新增：

- `O6ModbusHand`

最小支持：

- `readIdentity()`
- `moveToJointPose()`
- `openToSafePose()`
- `moveToPreGraspPose()`
- `closeForPinchGrasp()`

约束：

- 先保留右手 ID `0x27` 作为默认配置。
- 保留寄存器地址 `30..35` 和 6 维关节姿态语义。
- 写失败时简单返回错误，不做复杂重试。

### Phase 4: 抽出协调控制和任务流程

目标：让应用层只表达“任务步骤”，不碰硬件细节。

新增：

- `ArmMotionService`
- `O6GraspService`
- `CoordinatedArmHandController`
- `MoveArmThenGraspTask`

从工具程序迁移：

- J2/J3 小幅摆动目标生成；
- pre-grasp / grasp / hold / safe-open 姿态；
- 机械臂动作启动后写手部姿态；
- 到位等待；
- 失败时停止机械臂并返回错误。

MVP 失败策略：

- 打印错误；
- 停止当前动作；
- 返回失败；
- 不做复杂恢复、不做自动重试。

### Phase 5: 增加无硬件单元测试

优先测试：

- O6 姿态长度校验；
- 抓取姿态生成；
- 安全限幅；
- fake arm + fake hand 的调用顺序；
- 失败时是否停止流程。

不自动测试：

- 真实机械臂运动；
- 真实 O6 抓取；
- 长时间力控；
- 需要人工监护的动作。

### Phase 6: 收敛 CMake

短期策略：

- 保留现有根目录目标和 `RM/` 子工程；
- 新增一个小的控制库，例如 `rm_o6_control_core`；
- 不一次性重排整个仓库。

后续策略：

- 若 MVP 跑通，再决定是否把 `RM/` 子工程并入统一 `control/` 目录；
- 或继续保持 `RM/` 独立，只把 O6 协调控制作为上层应用库。

## 4. 推荐第一轮落地范围

确认后建议只做以下最小修改：

1. 新增 `IDexterousHand`、`IToolModbusBus` 和 O6 公共类型；
2. 新增 `RealManToolModbusBus` 骨架；
3. 不迁移工具程序；
4. 不改现有 demo；
5. 增加 fake 测试，验证接口可用。

这样第一轮风险最低，并能立即建立正确边界。

## 5. 当前硬件事实约束

当前 RM75 + O6 调试结论会影响架构实现顺序：

- PC USB-RS485 直连 O6 已验证可用；
- O6 右手 Modbus ID 为 `0x27`；
- 直连路径可读取身份寄存器 `30..35`；
- RM75 末端工具端 24V 和 Modbus 模式配置可成功；
- RM75 末端 RS485 到 O6 目前仍无回包，常见返回为 `-2` 或 `write_state:false`；
- 该现象更像物理链路、A/B、共地或接线位置问题，不应优先继续堆寄存器编码逻辑。

因此软件重构应先把边界建好，但真实末端 RS485 动作验证仍要等硬件链路确认后再执行。
