# 示例代码说明

本文档介绍 LinkerHand-CPP-SDK 提供的示例代码及其使用方法。

## 目录

- [示例列表](#示例列表)
- [如何运行示例](#如何运行示例)
- [示例详细说明](#示例详细说明)
- [学习路径](#学习路径)
- [常见问题](#常见问题)

---

## 示例列表

| 示例文件 | 功能描述 | 适用型号 | 难度 |
|---------|---------|---------|------|
| `toolset_example.cpp` | 交互式工具集，演示所有 API 功能 | 所有型号 | 中级 |
| `o6_async_reader.cpp` | O6 后台采集示例，主线程只消费快照 | O6/L6 | 中级 |
| `action_group_show_l10.cpp` | L10 型号的动作组演示 | L10 | 初级 |
| `realman_minimal_demo.cpp` | 睿尔曼机械臂最小连接与版本/信息读取 Demo，不发送运动指令 | RealMan 机械臂 | 初级 |
| `realman_slow_motion_demo.cpp` | 睿尔曼机械臂低速小幅关节往返运动 Demo | RealMan 机械臂 | 初级 |
| `realman_joint_pose_config_demo.cpp` | 睿尔曼机械臂源码参数调姿态 Demo | RealMan 机械臂 | 初级 |
| `realman_metrics_test.cpp` | 睿尔曼机械臂六维力/力矩、关节电流和低速运动指标测试 | RealMan 机械臂 | 中级 |
| `realman_trajectory_record.cpp` | 睿尔曼机械臂关节角/TCP 位姿 CSV 录制，只读不运动 | RealMan 机械臂 | 初级 |
| `realman_trajectory_replay.cpp` | 睿尔曼机械臂 CSV 轨迹低速逐点回放，默认 dry-run | RealMan 机械臂 | 中级 |

---

## 如何运行示例

### 方法一：使用安装脚本（推荐）

```bash
./script.sh
# 选择选项 [6]: Run examples
```

### 方法二：手动编译和运行

```bash
# 1. 构建项目
mkdir build && cd build
cmake ..
make

# 2. 运行示例
./toolset_example
./o6_async_reader
./action_group_show_l10
./realman_minimal_demo 192.168.1.18 8080
./realman_slow_motion_demo 192.168.1.18 8080 1000 2 5 10
./realman_joint_pose_config_demo
./realman_metrics_test
./realman_trajectory_record
./realman_trajectory_replay reports/realman_trajectories/demo.csv dry-run
```

### 方法三：直接编译示例

```bash
# 编译 toolset_example
g++ -std=c++11 examples/toolset_example.cpp -I./include \
    -L./lib/x86_64 -llinkerhand_cpp_sdk -lpthread -o toolset_example

# 编译 o6_async_reader
g++ -std=c++11 examples/o6_async_reader.cpp -I./include \
    -L./lib/x86_64 -llinkerhand_cpp_sdk -lpthread -o o6_async_reader

# 运行
./toolset_example
./o6_async_reader
```

## 示例详细说明

### realman_minimal_demo.cpp

**功能描述**
睿尔曼 RealMan RM_API2 最小 C++ 示例，用于验证项目内置的机械臂 SDK、动态库、网络连接和控制器通信是否可用。

**主要功能**:
1. 初始化 RM_API2
2. 连接机械臂控制器
3. 打印 API 版本
4. 读取机械臂软件版本信息
5. 读取机械臂基础信息
6. 断开连接并销毁 SDK 线程

**安全边界**:
- 不发送运动指令
- 默认只读信息
- 支持通过命令行覆盖 IP、端口和超时时间

**使用示例**:
```bash
./realman_minimal_demo
./realman_minimal_demo 192.168.1.18 8080
./realman_minimal_demo 192.168.1.18 8080 1500
```

详细说明见 [睿尔曼机械臂最小 Demo](../docs/REALMAN_MINIMAL_DEMO.md)。

### realman_slow_motion_demo.cpp

**功能描述**
睿尔曼 RealMan RM_API2 低速小幅运动示例，用于确认机械臂可以稳定、缓慢地执行关节空间运动。

**默认动作**:
1. 读取当前关节角
2. 第 2 关节低速 `+5°`
3. 回到起始关节角
4. 第 2 关节低速 `-5°`
5. 回到起始关节角

**安全边界**:
- 默认速度比例 `10%`
- 默认角度幅度 `5°`
- 参数校验限制速度不超过 `20%`，幅度不超过 `5°`
- 每一步阻塞等待完成，失败时发送停止指令

**使用示例**:
```bash
./realman_slow_motion_demo
./realman_slow_motion_demo 192.168.1.18 8080 1000 2 5 10
./realman_slow_motion_demo 192.168.1.18 8080 1000 2 3 5
```

详细说明见 [睿尔曼机械臂低速运动 Demo 操作手册](../docs/REALMAN_SLOW_MOTION_MANUAL.md)。

### realman_joint_pose_config_demo.cpp

**功能描述**
睿尔曼 RealMan RM_API2 源码参数调姿态示例。适合在 CLion 中直接修改 C++ 文件顶部参数，然后重新构建运行。

**主要修改区**:
```cpp
constexpr TargetMode kTargetMode = TargetMode::kRelativeJointOffset;
constexpr std::array<float, ARM_DOF> kRelativeJointOffsetsDeg = {
    0.0F, 5.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F,
};
constexpr int kSpeedPercent = 10;
```

**安全边界**:
- 默认相对当前姿态执行 `J2 +5°`
- 默认速度比例 `10%`
- 单关节单次变化超过 `10°` 会拒绝执行
- 运行前有 5 秒安全倒计时
- 可设置 `kDryRunOnly = true` 只预览目标，不发送运动指令

**使用示例**:
```bash
./realman_joint_pose_config_demo
```

详细说明见 [睿尔曼机械臂源码参数调姿态 Demo](../docs/REALMAN_JOINT_POSE_CONFIG_DEMO.md)。

### realman_metrics_test.cpp

**功能描述**
睿尔曼 RealMan RM_API2 指标测试示例。程序会采集六维力/力矩、关节电流、电压、温度、控制器状态，并执行一次低速小幅 J2 往返运动，用于评估基础运动到位误差和负载代理量变化。

**默认测试流程**:
1. 静态采样
2. 5 秒安全倒计时
3. J2 `+3°`
4. 返回起始姿态
5. J2 `-3°`
6. 返回起始姿态
7. 结束静态采样
8. 写入 CSV 和 Markdown 报告

**输出文件**:
```bash
reports/REALMAN_METRICS_TEST_RESULT.md
reports/realman_metrics_latest.csv
```

**安全边界**:
- 默认速度比例 `5%`
- 默认运动幅度 `3°`
- 程序限制速度不超过 `10%`，幅度不超过 `5°`
- 不自动启用接触式力位混合控制

详细说明见 [睿尔曼机械臂力/力矩与关节指标测试 README](../docs/REALMAN_METRICS_TEST_README.md)。

### realman_trajectory_record.cpp / realman_trajectory_replay.cpp

**功能描述**
睿尔曼 RealMan RM_API2 最小轨迹录制/回放方案。录制程序读取 `J1..J7` 关节角和 TCP 位姿写入 CSV；回放程序读取 CSV，默认只做 dry-run 校验，显式传 `execute` 才低速逐点调用 `rm_movej`。

**使用示例**:
```bash
./realman_trajectory_record reports/realman_trajectories/demo.csv 15 100
./realman_trajectory_replay reports/realman_trajectories/demo.csv dry-run
./realman_trajectory_replay reports/realman_trajectories/demo.csv execute 5 5 5
```

详细说明见 [RM75 轨迹录制与回放](../docs/REALMAN_TRAJECTORY_RECORD_REPLAY.md)。

### toolset_example.cpp

**功能描述**
交互式工具集，提供菜单界面，可以测试所有 API 功能。适合学习和调试。

**主要功能**:
1. 获取版本信息
2. 获取温度数据
3. 获取故障码
4. 获取电流数据
5. 获取扭矩数据
6. 获取速度数据
7. 获取压力传感信息
8. 循环获取关节状态
9. 执行预设动作
10. 清除故障码

**使用步骤**:
1. 运行程序
2. 选择机械手型号（L6/O6、L7、L10、L20、L21、L25）
3. 选择手部方向（左手/右手）
4. 选择通信接口（CAN0、CAN1、ModBus、EtherCAT）
5. 进入交互菜单，选择要测试的功能

**代码结构**:
```cpp
// 1. 选择型号
// 2. 选择手部方向
// 3. 选择通信接口
// 4. 创建 API 实例
LinkerHandApi hand(linkerhand, handType, channel);

// 5. 进入交互模式
interactiveMode(hand);
```

**预设动作**:
- **L6**: 单指循环弯曲
- **L7**: 握拳和张开
- **L10**: 
  - 手指循环弯曲
  - 拇指与其他手指循环对指
  - 手指侧摆
  - 握拳和张开
- **L20**: 握拳和张开
- **L21/L25**: 握拳和张开

**使用示例**:
```bash
$ ./toolset_example

=========================
        Example
=========================

Run Choose LinkerHand:
-------------------------
[1]: L6/O6
-------------------------
[2]: L7
-------------------------
[3]: L10
-------------------------
...
```

---

### o6_async_reader.cpp

**功能描述**
O6 后台采集示例。设备读取放到独立线程中执行，主线程只读取最近一次快照。

**主要功能**:
1. 周期性读取 `getState()`
2. 周期性读取 `getTorque()`
3. 周期性读取 `getTemperature()`
4. 周期性读取 `getFaultCode()`
5. 周期性读取 `getForce()`
6. 检测快照是否超时并标记 `STALE`

**适用场景**:
- 不希望采集逻辑阻塞主线程
- 需要持续监控 O6/L6 状态
- 希望把控制线程和读取线程拆开

**使用说明**:
- 默认参数为 `O6 + RIGHT + can0 + 1000000`
- 若项目还有控制线程，建议控制和读取分别使用独立 `LinkerHandApi` 实例
- 若读取长时间没有更新，输出会标记为 `STALE`

**使用示例**:
```bash
$ ./o6_async_reader
O6 async reader started.
Main thread only consumes cached snapshots.
Press Ctrl+C to stop.
```

---

### action_group_show_l10.cpp

**功能描述**
专门为 L10 型号设计的动作组演示程序。展示如何实现连续的动作序列。

**主要功能**:
- 演示 L10 型号的各种动作组合
- 展示动作序列的编程方式
- 适合学习动作控制的基本方法

**适用型号**: L10（适配球形拇指根部关节版本）

**代码特点**:
- 使用关节映射表管理关节状态
- 实现动作序列的循环执行
- 支持左右手（默认右手，修改 `HAND_TYPE::RIGHT` 为 `HAND_TYPE::LEFT` 可切换为左手）

**关节映射**:
```cpp
// L10 关节顺序
vector<string> joint_order = {
    "joint1",  // 拇指根部弯曲
    "joint2",  // 拇指侧摆
    "joint3",  // 食指根部弯曲
    "joint4",  // 中指根部弯曲
    "joint5",  // 无名指根部弯曲
    "joint6",  // 小指根部弯曲
    "joint7",  // 食指侧摆
    "joint8",  // 中指侧摆
    "joint9",  // 无名指侧摆
    "joint10"  // 拇指旋转
};
```

**使用示例**:
```bash
$ ./action_group_show_l10
# 程序会自动执行预设的动作序列
```

**注意事项**:
- 本示例仅支持 L10 型号
- 默认使用右手，如需左手请修改代码中的 `HAND_TYPE::RIGHT` 为 `HAND_TYPE::LEFT`
- 确保设备已正确连接
- 确保通信接口已正确配置（CAN 总线、ModBus 或 EtherCAT）

---

## 学习路径

### 初学者

1. **第一步**: 阅读 [README.md](../README.md) 了解基本概念
2. **第二步**: 运行 `action_group_show_l10.cpp` 查看基本动作
3. **第三步**: 阅读代码，理解关节控制的基本方法
4. **第四步**: 尝试修改代码，实现自己的动作

### 中级用户

1. **第一步**: 运行 `toolset_example.cpp` 熟悉所有 API
2. **第二步**: 阅读 [API 参考文档](../docs/API-Reference.md) 了解详细接口
3. **第三步**: 参考示例代码实现自己的应用
4. **第四步**: 学习传感器数据读取和处理

### 高级用户

1. **第一步**: 深入研究示例代码的实现细节
2. **第二步**: 优化代码性能
3. **第三步**: 实现复杂的动作序列
4. **第四步**: 集成到自己的项目中

---

## 代码学习要点

### 1. 初始化 API

```cpp
// 创建 API 实例
LinkerHandApi hand(LINKER_HAND::L10, HAND_TYPE::RIGHT);
```

### 2. 设置速度和扭矩

```cpp
// 设置速度（5 个元素对应 5 根手指）
std::vector<uint8_t> speed = {200, 200, 200, 200, 200};
hand.setSpeed(speed);

// 设置扭矩
std::vector<uint8_t> torque = {255, 255, 255, 255, 255};
hand.setTorque(torque);
```

### 3. 控制关节运动

```cpp
// L10 需要 10 个关节值
std::vector<uint8_t> pose = {128, 128, 128, 128, 128, 128, 128, 128, 128, 128};
hand.fingerMove(pose);

// 等待动作完成
std::this_thread::sleep_for(std::chrono::seconds(1));
```

### 4. 读取传感器数据

```cpp
// 获取压力数据
auto force = hand.getForce();

// 获取关节状态
auto state = hand.getState();

// 获取温度
auto temperature = hand.getTemperature();
```

### 5. 错误处理

```cpp
// 检查故障码
auto fault_code = hand.getFaultCode();
for (size_t i = 0; i < fault_code.size(); i++) {
    if (fault_code[i] != 0) {
        std::cout << "警告: 电机 " << i << " 故障码: " 
                  << static_cast<int>(fault_code[i]) << std::endl;
    }
}
```

---

## 常见问题

### Q1: 示例程序无法运行？

**A**: 检查以下几点：
1. 确保已正确编译
2. 检查库文件路径是否正确
3. 确保设备已连接
4. 检查通信接口配置（CAN 总线等）

详细排查请参考 [故障排查指南](../docs/TROUBLESHOOTING.md)。

---

### Q2: 如何修改示例代码适配其他型号？

**A**: 
1. 修改型号参数：
   ```cpp
   // 从 L10 改为 L20
   LinkerHandApi hand(LINKER_HAND::L20, HAND_TYPE::RIGHT);
   ```

2. 修改关节数组长度：
   ```cpp
   // L20 需要 20 个关节值
   std::vector<uint8_t> pose(20, 128);
   ```

3. 参考 [关节映射表](../README.md#关节映射表) 了解不同型号的关节配置

---

### Q3: 如何实现自己的动作序列？

**A**: 
1. 定义动作数组：
   ```cpp
   std::vector<std::vector<uint8_t>> actions = {
       {255, 104, 255, 255, 255, 255, 255, 255, 255, 71},  // 动作1
       {101, 60, 0, 0, 0, 0, 255, 255, 255, 51},            // 动作2
       // ...
   };
   ```

2. 循环执行：
   ```cpp
   for (const auto& pose : actions) {
       hand.fingerMove(pose);
       std::this_thread::sleep_for(std::chrono::seconds(1));
   }
   ```

---

### Q4: 如何读取和处理传感器数据？

**A**: 参考 `toolset_example.cpp` 中的示例：

```cpp
// 获取压力数据
auto force_data = hand.getForce();

// 遍历每根手指
for (size_t finger = 0; finger < force_data.size(); finger++) {
    // 处理数据...
}
```

详细说明请参考 [API 参考文档](../docs/API-Reference.md#getforce)。

---

### Q5: 示例代码中的延时时间如何设置？

**A**: 延时时间取决于：
- 动作的复杂度
- 设置的速度
- 设备的响应时间

**建议**:
- 简单动作: 0.5-1 秒
- 复杂动作: 1-2 秒
- 连续动作: 根据实际效果调整

```cpp
// 快速动作
std::this_thread::sleep_for(std::chrono::milliseconds(500));

// 标准动作
std::this_thread::sleep_for(std::chrono::seconds(1));

// 慢速动作
std::this_thread::sleep_for(std::chrono::seconds(2));
```

---

## 下一步

- 阅读 [API 参考文档](../docs/API-Reference.md) 了解完整的 API
- 查看 [故障排查指南](../docs/TROUBLESHOOTING.md) 解决遇到的问题
- 参考 [常见问题解答](../docs/FAQ.md) 获取更多帮助
- 贡献您的示例代码到项目

如有问题，请参考 [故障排查指南](../docs/TROUBLESHOOTING.md) 或联系技术支持。
