# RM JSON 机械臂控制子工程

这个目录只放机械臂相关代码，和上层 LinkerHand 灵巧手代码分开维护。后续如果机械臂从睿尔曼换成其他品牌，优先保留 `interfaces/`、`core/`、`config/`，只新增或替换 `drivers/<vendor>/`。

## 目录结构

```text
RM/
├── CMakeLists.txt
├── README.md
├── config/
│   ├── sample_readonly.json
│   └── sample_movej_delta_dry_run.json
├── include/rm_control/
│   ├── common/：Result、Pose、RobotInfo 等通用类型
│   ├── interfaces/：IRobotArm 抽象机械臂接口
│   ├── config/：JSON 解析和命令结构
│   ├── core/：JsonCommandRunner、DragTeachRecorder 等流程编排
│   ├── modules/trajectory/：轨迹目录、CSV、C++ waypoint 转换
│   └── drivers/realman/：睿尔曼 RM_API2 适配层
├── src/
│   ├── config/
│   ├── core/
│   ├── modules/trajectory/
│   ├── drivers/realman/
│   ├── main.cpp
│   ├── drag_teach_record_main.cpp
│   └── trajectory_convert_main.cpp
├── data/trajectories/
│   ├── csv/
│   ├── vendor/
│   ├── converted/
│   └── reports/
└── tests/：不依赖真实硬件的命令解析测试
```

## JSON 指令格式

顶层字段：

```json
{
  "connection": {
    "ip": "192.168.1.18",
    "port": 8080,
    "timeout_ms": 1000
  },
  "safety": {
    "dry_run": true,
    "allow_motion": false,
    "max_speed_percent": 10,
    "max_joint_delta_deg": 2.0,
    "max_cartesian_step_m": 0.02,
    "max_euler_delta_rad": 0.2
  },
  "commands": []
}
```

安全字段含义：

| 字段 | 含义 |
|---|---|
| `dry_run` | 为 `true` 时只校验运动指令，不发送 `rm_movej/rm_movel` |
| `allow_motion` | 为 `false` 时禁止真实运动 |
| `max_speed_percent` | 运动速度上限 |
| `max_joint_delta_deg` | 单次关节运动最大角度差 |
| `max_cartesian_step_m` | 单次直线运动最大 xyz 位移 |
| `max_euler_delta_rad` | 单次直线运动最大欧拉角变化 |

支持的 `commands`：

```json
{"type": "get_robot_info"}
```

```json
{"type": "get_current_arm_state"}
```

```json
{"type": "get_force_data"}
```

```json
{"type": "sleep_ms", "duration_ms": 500}
```

绝对关节目标，单位是度：

```json
{
  "type": "movej",
  "joints_deg": [0, 0, 0, 0, 0, 0, 0],
  "speed_percent": 5,
  "blocking": true
}
```

相对关节增量，单位是度。推荐初期用这个：

```json
{
  "type": "movej_delta",
  "delta_deg": [0, 1, 0, 0, 0, 0, 0],
  "speed_percent": 5,
  "blocking": true
}
```

绝对笛卡尔直线目标，位置单位是米，姿态单位是弧度：

```json
{
  "type": "movel",
  "pose": {
    "x": 0.30,
    "y": 0.00,
    "z": 0.25,
    "rx": 3.14,
    "ry": 0.00,
    "rz": 0.00
  },
  "speed_percent": 5,
  "blocking": true
}
```

## 编译

```bash
cd /home/mashiro/project/linkerhand-cpp-sdk
cmake -S RM -B RM/build -DCMAKE_BUILD_TYPE=Debug
cmake --build RM/build -j
ctest --test-dir RM/build --output-on-failure
```

本机默认会优先查找：

```text
/home/mashiro/RM_API2/C++
RM/../third_party/Robotic_Arm
```

如果 SDK 在其他位置：

```bash
cmake -S RM -B RM/build -DREALMAN_RM_API2_ROOT=/path/to/RM_API2/C++
```

## 运行

只校验 JSON，不连接 SDK：

```bash
./RM/build/rm_json_runner RM/config/sample_readonly.json --validate-only
```

执行只读指令，读取机器人信息、当前状态和六维力：

```bash
./RM/build/rm_json_runner RM/config/sample_readonly.json
```

校验一个相对关节运动，但不发送运动：

```bash
./RM/build/rm_json_runner RM/config/sample_movej_delta_dry_run.json
```

需要真实运动时，把 JSON 改成：

```json
"safety": {
  "dry_run": false,
  "allow_motion": true,
  "max_speed_percent": 10,
  "max_joint_delta_deg": 2.0
}
```

第一次真实运动必须清空工作空间，低速、小角度，并保证急停可触达。

## 拖动示教轨迹

录制 RM75 末端六维力位置+姿态拖动示教，同步保存 CSV：

```bash
./RM/build/rm_drag_teach_record demo_pick_path 15 100
```

默认写入：

```text
RM/data/trajectories/csv/demo_pick_path.csv
RM/data/trajectories/vendor/demo_pick_path_vendor.txt
```

把 CSV 转成 C++ TCP waypoint 表：

```bash
./RM/build/rm_trajectory_convert RM/data/trajectories/csv/demo_pick_path.csv
```

默认输出：

```text
RM/data/trajectories/converted/demo_pick_path_tcp_path.cpp
```

维护边界：

- `drivers/realman` 只封装 RM SDK 调用。
- `core/drag_teach_recorder` 负责编排拖动示教、采样、停止和保存。
- `modules/trajectory` 负责目录、CSV 和 C++ 文件转换，不访问硬件。

## 后续换机械臂怎么改

不要改 `JsonCommandRunner` 和 JSON 格式。新增一个驱动：

```text
include/rm_control/drivers/new_vendor/new_vendor_robot_arm.h
src/drivers/new_vendor/new_vendor_robot_arm.cpp
```

新驱动实现 `IRobotArm`：

```cpp
class NewVendorRobotArm final : public rm_control::interfaces::IRobotArm {
    // connect / getCurrentArmState / moveJ / moveL ...
};
```

然后在 `src/main.cpp` 里把 `RealManRobotArm` 换成新驱动即可。上层 JSON 指令、解析、校验和执行流程不需要重写。
