#  TRON 2  SDK 开发指南

> 来源: https://www.limxdynamics.com/zh/documents/847884267345285120
> 发布时间: 2026-07-21
> 更新时间: 2026-10-29
> 标签: TRON 2 EDU版

| 版本 | 修订日期  | 修改人员 | 修改内容 |
| ---- | --------   | -------- | ---- |
| V1.0 | 20260415   |   damon   |  初稿    |
| V1.1 | 20260726   | damon     |主控制器 V2.1.24 升级更新及此前错误修正 |
| V1.2 | 20260729   | damon     |针对主控 V2.2.11升级的的信息更新   	<br>-增加柔顺控制开关与参数设置接口   <br>-增加升降支架位置控制接口 |

# 1 SDK 概述

> **名词解释：**
> - **底层运动控制接口**：在底层开发模式下，您可以使用 python、C++ 等调用机器人底层接口，实现例如直接控制关节运动等底层操作。
> - **上层应用开发接口**：在上层开发模式下，您可以基于LimX内置运控算法从而控制机器人运动完成任务，例如双臂操作、机器人前后移动、机器人灯光管理等。

## 1.1 通讯架构图

下图呈现了机器人本体通信架构组成及交互关系。运控电脑部分涵盖运控算法节点和软件业务逻辑实现模块，通过 `上层应用开发接口` 和 `底层运动控制开发接口` 的数据通讯方式控制机器人本体的运动。机器人本体由数据交换机、主站及各类硬件组件构成，运控电脑负责协调各组件运行。

![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/developer_expansion_module.png)

## 1.2 查看/设置机器人型号

在编译和运行控制算法及仿真器程序时，选择正确的机器人型号至关重要。
您可以通过查看机器人型号并将其设置到环境变量 `ROBOT_TYPE` 中，确保在不同任务中准确识别并应用相应的机器人构型。以下是查看和配置机器人型号的步骤。

- 请选择并连接您机器人的 Wi-Fi 热点，密码为：`12345678` 在浏览器中输入 `http://10.192.1.2:8080` 可以进入“机器人信息页”，并查看机器人信息。如下图所示，页面中显示的 SN (序列号) 为 WF_TRON2A_001，其中 WF_TRON2A 是机器人型号类型。
![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/wifi_setting.png)
![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/websocket.png)

- 设置机器人型号：打开 Bash 终端，输入以下 Shell 命令来设置机器人型号。这样在编译和运行 RL 训练、控制算法以及仿真器程序时，算法将能获取到正确的机器人型号信息。
```bash
  echo 'export ROBOT_TYPE=WF_TRON2A' >> ~/.bashrc && source ~/.bashrc

```

## 1.3 开发拓展模块电脑

开发拓展模块内置电脑主要用于开发机器人相关算法及应用程序。您可以通过 WiFi或有线网络连接到机器人本体系统登录到开发拓展模块电脑，具体步骤如下：
  - 请选择并连接您机器人的 Wi-Fi 热点，密码为：`12345678`
  - 通过 SSH 登录开发拓展模块电脑
    - 登录地址为：`10.192.1.4`
    - 登录密码为：`123456`
    - 在终端中输入以下命令（首次连接需确认命令）：  `ssh guest@10.192.1.4`
    - 开发拓展模块电脑系统配置：
      - 操作系统：Ubuntu 20.04.6 LTS
      - ROS2：系统默认安装ROS2 foxy  版本机器人系统
      - ROS1：系统默认安装ROS1 Noetic 版本机器人系统

# 2 底层运动控制接口

**跨平台底层运动控制开发接口库**提供统一的C++/Python API，兼容ROS1、ROS2及非ROS系统，实现运动控制算法的快速移植与部署。

## 2.1 C++ 运动控制开发接口

### 2.1.1 概述

底层运动控制开发接口通过硬件抽象层和标准化通信协议，开发者可无缝切换仿真与真实硬件环境，显著降低多平台适配成本。

### 2.1.2 安装运动控制开发库

- Linux x86_64 环境
```bash
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
```

- Linux aarch64 环境
 ```bash
  git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
  pip install limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl

```

- Windows 环境
```bash
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/win/limxsdk-*-py3-none-any.whl
```

### 2.1.3 getInstance 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **getInstance** |
| 函数原型 | `static Tron2* getInstance();` |
| 功能概述 | 获取 Tron2 机器人类单例实例的指针 |
| 参数 | 无 |
| 返回值 | `Tron2*`，指向 Tron2 实例的指针 |
| 备注 | 使用了单例模式，确保 Tron2 类只有一个实例存在于程序中 |

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.4 init 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **init** |
| 函数原型 | `bool init(const std::string& robot_ip_address = "127.0.0.1");` |
| 功能概述 | 初始化lowlevel-sdk的通信运行环境，在主函数中调用其它接口之前调用，完成初始化工作。 |
| 参数 | `robot_ip_address`：机器人的 IP 地址。对于仿真，通常设置为 `"127.0.0.1"`，而对于真实机器人，设置为 `"10.192.1.2"`。 |
| 返回值 | 如果初始化成功，则返回 `true`；否则返回 `false`。 |
| 备注 | 无 |

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.5 getMotorNumber 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **getMotorNumber** |
| 函数原型 | `uint32_t getMotorNumber();` |
| 功能概述 | 获取机器人的电机数量。 |
| 参数 | 无 |
| 返回值 | 返回一个无符号整数，表示机器人中的总电机数量。 |
| 备注 | 例如，双轮足形态下的电机数量为 10 个。 |

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 获取机器人中的电机数量
    uint32_t motor_num = robot->getMotorNumber();

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.6 subscribeImuData 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeImuData** |
| 函数原型 | `void subscribeImuData(std::function<void(const ImuDataConstPtr&)> cb);` |
| 功能概述 | 订阅机器人的 IMU 数据，并在接收到新的 IMU 数据时调用指定的回调函数。 |
| 参数 | `cb`：用于处理新 IMU 数据的回调函数。 |
| 返回值 | 无 |

**备注：**
ImuData 数据结构原型如下:
```cpp

/**
 * @struct ImuData
 *
 * @brief 表示基于传感器反馈的机器人 IMU 数据的结构体。
 *
 * 此结构体封装了 IMU 数据，包括加速度计、陀螺仪和四元数。
 */
struct ImuData {
  uint64_t stamp; // 时间戳，以纳秒为单位，表示记录此数据时的时间。
  float acc[3];   // 用于存储 IMU 加速度计数据，采集沿三个轴（X、Y、Z）的线性加速度。
  float gyro[3];  // 用于存储 IMU 陀螺仪数据，采集沿三个轴（X、Y、Z）的角速度。
  float quat[4];  // 用于存储 IMU 四元数数据，使用四元数表示在三维空间中的方向（w、x、y、z）。
};

// 智能指针类型别名
typedef std::shared_ptr<ImuData> ImuDataPtr;
typedef std::shared_ptr<ImuData const> ImuDataConstPtr;
```
**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 订阅机器人 IMU 数据，并指定回调函数
    robot->subscribeImuData([](const ImuDataConstPtr& msg) {
        // 在这里处理接收到的 IMU 数据
        // 例如：可以进行姿态结算等工作
    });

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```
### 2.1.7 subscribeRobotState 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeRobotState** |
| 函数原型 | `void subscribeRobotState(std::function<void(const RobotStateConstPtr&)> cb);` |
| 功能概述 | 订阅并接收关于机器人状态的更新。 |
| 参数 | `cb`：回调函数，当接收到机器人状态更新时将被调用。回调函数参数指向 `RobotState` 对象的常量指针。 |
| 返回值 | 无 |

**备注**
RobotState 数据结构原型如下：

```cpp
/**
 * @struct RobotState
 *
 * @brief 代表基于传感器反馈的机器人状态的结构体。
 *
 * 此结构封装了各种数据点，可用于监控和控制机器人，包括 IMU 数据（加速度计、陀螺仪、四元数）、输出扭矩、当前角度和速度等。
 */
struct RobotState {
  // 默认构造函数
  RobotState() { }
  // 带参数的构造函数，用于初始化向量大小为 motor_num 的 tau、q、dq 向量，初始值均为 0.0
  RobotState(int motor_num)
  : tau(motor_num, 0.0)
  , q(motor_num, 0.0)
  , dq(motor_num, 0.0) { }
  uint64_t stamp;              // 时间戳，通常表示记录或生成这些数据的时间，以纳秒为单位
  std::vector<float> tau;      // 用于存储当前估计的输出扭矩（以牛顿米为单位）的向量
  std::vector<float> q;        // 用于存储当前角度（以弧度为单位）的向量
  std::vector<float> dq;       // 用于存储当前速度（以弧度每秒为单位）的向量
};

// 智能指针类型别名
typedef std::shared_ptr<RobotState> RobotStatePtr;
typedef std::shared_ptr<RobotState const> RobotStateConstPtr;
```

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 订阅机器人状态更新，并指定回调函数
    robot->subscribeRobotState([](const RobotStateConstPtr& msg) {
        // 在这里处理接收到的 RobotState 数据
        // 例如：读取关节角度、速度、力矩、IMU 等状态信息
    });

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.8 publishRobotCmd 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **publishRobotCmd** |
| 函数原型 | `bool publishRobotCmd(const RobotCmd& cmd);` |
| 功能概述 | 发布一个命令来控制机器人的关节。 |
| 参数 | `cmd`：表示所需机器人命令的 `RobotCmd` 对象。 |
| 返回值 | 无 |
| 备注 |  |

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 获取机器人中的电机数量
    uint32_t motor_num = robot->getMotorNumber();

    // 创建一个包含机器人电机数量的 RobotCmd 对象
    RobotCmd cmd(motor_num);

    // 发布控制指令
    robot->publishRobotCmd(cmd);

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.9 subscribeSensorJoy 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeSensorJoy** |
| 函数原型 | `void subscribeSensorJoy(std::function<void(const SensorJoyConstPtr&)> cb);` |
| 功能概述 | 在真机部署中，该方法用于订阅来自机器人遥控器的数据。当机器人接收到遥控器数据时，将会调用指定的回调函数，并传递包含遥控器数据的 `SensorJoy` 结构体常量指针给回调函数进行处理。 |
| 参数 | `cb`：表示回调函数，用于接收机器人遥控器的数据。回调函数的参数类型为 `SensorJoyConstPtr`，即指向 `SensorJoy` 结构体常量的共享指针。 |
| 返回值 | 无 |
| 备注 |  |

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 订阅机器人遥控器数据
    robot->subscribeSensorJoy([](const SensorJoyConstPtr& joy) {
        // L1 和 R1 按下
        if (joy->buttons[4] == 1 && joy->buttons[7] == 1)
        {
            // 在这里执行相关操作
        }

        // 处理摇杆数据
        double axes_left_horizontal = joy->axes[0];
        double axes_left_vertical = joy->axes[1];
        double axes_right_horizontal = joy->axes[2];
        double axes_right_vertical = joy->axes[3];
    });

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.10 subscribeDiagnosticValue 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeDiagnosticValue** |
| 函数原型 | `void subscribeDiagnosticValue(std::function<void(const DiagnosticValueConstPtr&)> cb);` |
| 功能概述 | 在真机部署中，该方法用于订阅机器人的诊断值和状态信息。当机器人发出诊断值时，系统会调用指定的回调函数，并传递包含诊断值的 `DiagnosticValue` 结构体常量指针给回调函数进行处理。这可以帮助实时监控机器人的健康状态，并及时做出反应以处理可能的问题。 |
| 参数 | `cb`：用于接收机器人诊断值的回调函数，其参数类型为 `DiagnosticValueConstPtr`，即指向 `DiagnosticValue` 结构体常量的共享指针。`DiagnosticValue` 结构体包含机器人诊断值的信息，包括时间戳、级别、名称、代码和消息字段。 |
| 返回值 | 无 |
| 备注 |  |

**代码示例：**

```cpp
#include <thread>
#include <iostream>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 订阅机器人诊断数据
    robot->subscribeDiagnosticValue([](const DiagnosticValueConstPtr& msg) {
        // 在这里处理机器人诊断值
        // 例如，可以根据诊断级别和消息内容进行打印或处理

        std::cout << "Diagnostic Value: " << msg->name << std::endl;
        std::cout << "Level: " << msg->level << std::endl;
        std::cout << "Code: " << msg->code << std::endl;
        std::cout << "Message: " << msg->message << std::endl;
    });

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

### 2.1.11 setRobotLightEffect 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **setRobotLightEffect** |
| 函数原型 | `bool setRobotLightEffect(int effect);` |
| 功能概述 | 在真机部署中，该方法用于设置机器人的灯光效果。 |
| 参数 | `effect`：一个整数，表示所需的机器人灯光效果，具体定义见 `Tron2::LightEffect` 枚举。 |
| 返回值 | `bool`：指示机器人灯光效果是否成功设置。 |

**备注**  - 灯光效果枚举说明：

```cpp
enum LightEffect : int {
    STATIC_RED = 0,        // 静态红光
    STATIC_GREEN,          // 静态绿光
    STATIC_BLUE,           // 静态蓝光
    STATIC_CYAN,           // 静态青光
    STATIC_PURPLE,         // 静态紫光
    STATIC_YELLOW,         // 静态黄光
    STATIC_WHITE,          // 静态白光

    LOW_FLASH_RED,         // 红光闪烁（慢闪）
    LOW_FLASH_GREEN,       // 绿光闪烁（慢闪）
    LOW_FLASH_BLUE,        // 蓝光闪烁（慢闪）
    LOW_FLASH_CYAN,        // 青光闪烁（慢闪）
    LOW_FLASH_PURPLE,      // 紫光闪烁（慢闪）
    LOW_FLASH_YELLOW,      // 黄光闪烁（慢闪）
    LOW_FLASH_WHITE,       // 白光闪烁（慢闪）

    FAST_FLASH_RED,        // 红光闪烁（快闪）
    FAST_FLASH_GREEN,      // 绿光闪烁（快闪）
    FAST_FLASH_BLUE,       // 蓝光闪烁（快闪）
    FAST_FLASH_CYAN,       // 青光闪烁（快闪）
    FAST_FLASH_PURPLE,     // 紫光闪烁（快闪）
    FAST_FLASH_YELLOW,     // 黄光闪烁（快闪）
    FAST_FLASH_WHITE       // 白光闪烁（快闪）
};
```

**代码示例：**

```cpp
#include <thread>

// 包含 limxsdk::Tron2 头文件，用于引入 Tron2 类
#include "limxsdk/tron2.h"

// 使用 limxsdk 命名空间，简化对 Tron2 类的引用
using namespace limxsdk;

int main(int argc, char *argv[]){
    // 获取 Tron2 类的单例实例
    Tron2* robot = Tron2::getInstance();

    // 默认机器人 IP 地址
    std::string robot_ip = "10.192.1.2";

    if (argc > 1)
    {
        // 如果提供了命令行参数，则使用命令行参数作为机器人 IP 地址
        robot_ip = argv[1];
    }

    // 初始化运动控制算法程序的通信运行环境
    if (!robot->init(robot_ip))
    {
        // 如果初始化失败，则退出程序
        exit(1);
    }

    // 设置机器人灯光效果为静态红光
    robot->setRobotLightEffect(limxsdk::Tron2::STATIC_RED);

    // 无限循环以保持程序运行
    while (true)
    {
        // 休眠 1000 毫秒
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }

    return 0;
}
```

## 2.2 Python 运动控制开发接口

### 2.2.1 概述

提供与 C++ 相同功能的 [Python 运动控制算法开发接口](https://github.com/limxdynamics/pointfoot-sdk-lowlevel/tree/master/python3)，**使得不熟悉 C++ 编程语言的开发者能够使用 Python 进行运动控制算法的开发**。

Python 语言易于学习，具有简洁清晰的语法和丰富的第三方库，使开发者能够更快速地上手并迅速实现算法。通过 Python 接口，开发者可以利用 Python 的动态特性进行快速原型设计和实验验证，加速算法的迭代和优化过程。同时，Python 的跨平台性和强大的生态系统支持，使得运动算法能够更广泛地应用于不同平台和环境。

此外，算法模型快速部署到仿真和真机环境中也得益于 Python 的灵活性，开发者可以使用 Python 轻松地将算法模型集成到各种仿真平台和真实硬件中，实现快速迭代和验证算法的性能。

### 2.2.2 安装运动控制开发库

- Linux x86_64 环境
```bash
  git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
  pip install limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl

```

- Linux aarch64 环境
```bash
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
```

- Windows 环境
```bash
  git clone git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
  pip install limxsdk-lowlevel/python3/win/limxsdk-*-py3-none-any.whl

```

### 2.2.3 `__init__` 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **`__init__`** |
| 函数原型 | `def __init__(self, robot_type: robot.RobotType)` |
| 功能概述 | 在初始化时，指定机器人的类型，并创建一个相应类型的本地机器人实例。 |
| 参数 | `robot_type`：表示机器人类型的枚举值，类型为 `RobotType.Tron2`。 |
| 返回值 | 无 |
| 备注 | 无 |

**代码示例：**

```python
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType

if __name__ == '__main__':
    # 创建一个类型为 Tron2 的 Robot 实例
    robot = Robot(RobotType.Tron2)
```

### 2.2.4 `init` 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **init** |
| 函数原型 | `def init(self, robot_ip: str = "127.0.0.1")` |
| 功能概述 | 初始化运动控制算法程序的通信运行环境，通常在主函数中调用其它接口之前调用，完成初始化工作。 |
| 参数 | `robot_ip`：机器人的 IP 地址。对于仿真，通常设置为 `"127.0.0.1"`；对于真实机器人，应当设置为 `"10.192.1.2"`。 |
| 返回值 | 成功返回 `True`，失败返回 `False`。 |
| 备注 | 无 |

**代码示例：**

```python
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了命令行参数作为机器人IP
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用IP地址初始化机器人的通信运行环境
    if not robot.init(robot_ip):
        sys.exit()
```

### 2.2.5 getMotorNumber 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **getMotorNumber** |
| 函数原型 | `def getMotorNumber(self, timeout: float = -1.0)` |
| 功能概述 | 获取机器人中的电机数量。 |
| 参数 | 无 |
| 返回值 | 返回一个无符号整数，表示机器人中的总电机数量。 |
| 备注 | 通常情况下，双臂形态的电机数量应当为 14 个。 |

**代码示例：**

```python
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 获取机器人中的电机数量
    motor_number = robot.getMotorNumber()
```

### 2.2.6 subscribeImuData 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeImuData** |
| 函数原型 | `def subscribeImuData(self, callback: Callable[[datatypes.ImuData], Any])` |
| 功能概述 | 订阅机器人的 IMU 数据，并在接收到新的 IMU 数据时调用指定的回调函数。 |
| 参数 | `callback`：用于处理新 IMU 数据的回调函数。 |
| 返回值 | 成功返回 `True`，并循环打印 IMU 数据；失败返回 `False`。 |

**备注：**
datatypes.ImuData 数据结构原型如下：

```python
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

class RobotReceiver:
    # 订阅机器人的 IMU数据
    def imuDataCallback(self, imu: datatypes.ImuData):
        print("\n------\nrobot_state:" + \
              "\n  stamp: " + str(imu.stamp) + \
              "\n  acc: " + str(imu.acc) + \
              "\n  gyro: " + str(imu.gyro) + \
              "\n  quat: " + str(imu.quat))

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 创建一个 RobotReceiver 实例来处理回调
    receiver = RobotReceiver()

    # 创建回调函数的 partial 函数
    imuDataCallback = partial(receiver.imuDataCallback)

    # 订阅机器人IMU数据
    robot.subscribeImuData(imuDataCallback)

    # 休眠 1 秒，防止程序退出
    import time
    while True:
        time.sleep(1)
```

### 2.2.7 subscribeRobotState 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeRobotState** |
| 函数原型 | `def subscribeRobotState(self, callback: Callable[[datatypes.RobotState], Any])` |
| 功能概述 | 订阅接收关于机器人状态的更新。 |
| 参数 | `callback`：回调函数，当接收到机器人状态更新时将被调用。回调函数参数指向 datatypes.RobotState对象。
 `datatypes.RobotState`数据结构字段：
- stamp：时间戳，通常表示记录或生成这些数据的时间。
  - tau：用于存储当前估计的输出扭矩（以牛顿米为单位）的向量。
   - q：用于存储当前角度（以弧度为单位）的向量。
  - dq：用于存储当前速度（以弧度每秒为单位）的向量 |
| 返回值 | 成功返回 `True`，失败返回 `False` |
| 备注 | 无 |

**代码示例：**

```python
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

class RobotReceiver:
    # 用于接收机器人状态的回调函数
    def robotStateCallback(self, robot_state: datatypes.RobotState):
        print("\n------\nrobot_state:" + \
              "\n  stamp: " + str(robot_state.stamp) + \
              "\n  tau: " + str(robot_state.tau) + \
              "\n  q: " + str(robot_state.q) + \
              "\n  dq: " + str(robot_state.dq))

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 创建一个 RobotReceiver 实例来处理回调
    receiver = RobotReceiver()

    # 创建回调函数的 partial 函数
    robotStateCallback = partial(receiver.robotStateCallback)

    # 订阅机器人状态
    robot.subscribeRobotState(robotStateCallback)

    # 休眠 1 秒，防止程序退出
    import time
    while True:
        time.sleep(1)
```

### 2.2.8 publishRobotCmd 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **publishRobotCmd** |
| 函数原型 | `def publishRobotCmd(self, cmd: datatypes.RobotCmd)` |
| 功能概述 | 发布一个命令来控制机器人的动作。 |
| 参数 | `cmd`：表示所需机器人命令的 `datatypes.RobotCmd` 对象。 |
| 返回值 | 成功返回 `True`，失败返回 `False`。 |
| 备注 | 无 |

**代码示例：**

```python
import sys
import time
import limxsdk.robot.Rate as Rate
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

if __name__ == '__main__':
    # 创建一个 Robot 实例
    robot = Robot(RobotType.Tron2)

    # 对于仿真，通常设置为 "127.0.0.1"，而对于真实机器人，设置为 "10.192.1.2"
    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 获取电机数量信息
    motor_number = robot.getMotorNumber()

    # 主循环以连续发布机器人命令
    rate = Rate(300) # 300Hz
    cmd_msg = datatypes.RobotCmd()
    while True:
        # 设置时间戳、控制模式、关节位置、速度、力矩、Kp 和 Kd 的默认值
        # motor_names 对应您要控制的关节名称
        # 注意以下仅为格式示例，在实际使用过程中应填充具体参数
        cmd_msg.stamp = time.time_ns()
        cmd_msg.mode = [0.0 for _ in range(16)] # 实际使用无需改变
        cmd_msg.q = [0.0 for _ in range(16)]    # 填充您规划的角度，控制频率300hz
        cmd_msg.dq = [0.0 for _ in range(16)]   # 实际使用无需改变
        cmd_msg.tau = [0.0 for _ in range(16)]  # 实际使用无需改变
        cmd_msg.Kp = [420,420,300,300,200,200,200,420,420,300,300,200,200,200]
        cmd_msg.Kd = [12,12,15,15,10,10,10,12,12,15,15,10,10,10,3,3]
        cmd_msg.motor_names = ["" for _ in range(16)]   # 实际使用无需改变
        robot.publishRobotCmd(cmd_msg)  # 发布    机器人命令
        rate.sleep()  # 控制循环频率
```

### 2.2.9 subscribeSensorJoy 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeSensorJoy** |
| 函数原型 | `def subscribeSensorJoy(self, callback: Callable[[datatypes.SensorJoy], Any])` |
| 功能概述 | 在真机部署中，该方法用于订阅来自机器人遥控器的数据。当机器人接收到遥控器数据时，将会调用指定的回调函数，并传递包含遥控器数据的 `datatypes.SensorJoy` 结构体对象给回调函数进行处理。 |
| 参数 | `callback`：表示回调函数，用于接收机器人遥控器的数据。回调函数的参数类型为 `datatypes.SensorJoy`。 |
| 返回值 | 成功返回 `True`，失败返回 `False`。 |
| 备注 | 无 |

**代码示例：**

```python
import sys
import time
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

class RobotReceiver:
    # 用于接收遥控器数据的回调函数
    def sensorJoyCallback(self, sensor_joy: datatypes.SensorJoy):
        print("\n------\nsensor_joy:" + \
              "\n  stamp: " + str(sensor_joy.stamp) + \
              "\n  axes: " + str(sensor_joy.axes) + \
              "\n  buttons: " + str(sensor_joy.buttons))

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 创建一个 RobotReceiver 实例来处理回调
    receiver = RobotReceiver()

    # 创建回调函数的 partial 函数
    sensorJoyCallback = partial(receiver.sensorJoyCallback)

    # 订阅机器人遥控数据
    robot.subscribeSensorJoy(sensorJoyCallback)

    # 休眠 1 秒，防止程序退出
    import time
    while True:
        time.sleep(1)

```
### 2.2.10 subscribeDiagnosticValue 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **subscribeDiagnosticValue** |
| 函数原型 | `def subscribeDiagnosticValue(self, callback: Callable[[datatypes.DiagnosticValue], Any])` |
| 功能概述 | 在真机部署中，该方法用于订阅机器人的诊断值和状态信息。当机器人发出诊断值时，系统会调用指定的回调函数，并传递包含诊断值的 `datatypes.DiagnosticValue` 结构体对象给回调函数进行处理。这可以帮助实时监控机器人的健康状态，并及时做出反应以处理可能的问题。 |
| 参数 | `callback`：用于接收机器人诊断值的回调函数，其参数类型为 `datatypes.DiagnosticValue`。`datatypes.DiagnosticValue` 结构体包含机器人诊断值的信息，包括时间戳、级别、名称、代码和消息字段。 |
| 返回值 | 成功返回 `True`，失败返回 `False`。 |
| 备注 | 无 |

**代码示例：**

```python
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

class RobotReceiver:
    # 用于接收诊断值的回调函数
    def diagnosticValueCallback(self, diagnostic_value: datatypes.DiagnosticValue):
        print("\n------\ndiagnostic_value:" + \
              "\n  stamp: " + str(diagnostic_value.stamp) + \
              "\n  name: " + diagnostic_value.name + \
              "\n  level: " + str(diagnostic_value.level) + \
              "\n  code: " + str(diagnostic_value.code) + \
              "\n  message: " + diagnostic_value.message)

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 创建一个 RobotReceiver 实例来处理回调
    receiver = RobotReceiver()

    # 创建回调函数的 partial 函数
    diagnosticValueCallback = partial(receiver.diagnosticValueCallback)

    # 订阅机器人诊断信息
    robot.subscribeDiagnosticValue(diagnosticValueCallback)

    # 休眠 1 秒，防止程序退出
    import time
    while True:
        time.sleep(1)

```
 **返回值:**  |

```yaml
/home/damon/PyCharmMiscProject/.venv/bin/python /home/damon/PyCharmMiscProject/testsdk.py
INFO: RobotIPAddress - "10.192.1.2"

------
diagnostic_value:
  stamp: 980020701021
  name: battery
  level: 0
  code: 100
  message: 100

------
diagnostic_value:
  stamp: 980016991896
  name: battery_charge
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 980016965062
  name: battery_cost
  level: 0
  code: 0
  message: 100

------
diagnostic_value:
  stamp: 980016998021
  name: battery_voltage
  level: 0
  code: 0
  message: 56

------
diagnostic_value:
  stamp: 590572955807
  name: controller
  level: 0
  code: 1
  message: manipulation_damping controller is ready

------
diagnostic_value:
  stamp: 980017064521
  name: current_12v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 980017054604
  name: current_15v
  level: 0
  code: 0
  message: 2

------
diagnostic_value:
  stamp: 980017043229
  name: current_24v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 13302781672
  name: ecm_version
  level: 0
  code: 0
  message: 1.0.25

------
diagnostic_value:
  stamp: 24524362677
  name: ethercat
  level: 0
  code: 0
  message: ethercat ok!

------
diagnostic_value:
  stamp: 14114416512
  name: imu
  level: 0
  code: 0
  message: OK

------
diagnostic_value:
  stamp: 14059836923
  name: lan_index
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 24534264177
  name: left end-effector
  level: 0
  code: 3
  message: limx 2F gripper

------
diagnostic_value:
  stamp: 980017021646
  name: motor1_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 980017015812
  name: motor1_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 980017032437
  name: motor2_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 980017027187
  name: motor2_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 980017010854
  name: motor_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 23464742635
  name: motor_version
  level: 0
  code: 0
  message: 1: 1.2.23; 2: 1.2.23; 3: 1.2.23; 4: 1.2.23; 5: 1.2.23; 6: 1.2.23; 7: 1.2.23; 8: 1.2.23; 9: 1.2.23; 10: 1.2.23; 11: 1.2.23; 12: 1.2.23; 13: 1.2.23; 14: 1.2.23; 15: 1.2.18; 16: 1.2.18;

------
diagnostic_value:
  stamp: 980017005604
  name: motor_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 24543136677
  name: right end-effector
  level: 0
  code: 3
  message: limx 2F gripper

------
diagnostic_value:
  stamp: 13993435499
  name: rotate_status
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 13983715707
  name: version
  level: 0
  code: 0
  message: robot-tron2-r-1.2.20.20251225182226

------
diagnostic_value:
  stamp: 980017059562
  name: voltage_12v
  level: 0
  code: 0
  message: 12

------
diagnostic_value:
  stamp: 980017049646
  name: voltage_15v
  level: 0
  code: 0
  message: 16

------
diagnostic_value:
  stamp: 980017037687
  name: voltage_24v
  level: 0
  code: 0
  message: 25

------
diagnostic_value:
  stamp: 14044807630
  name: vr_follow_head
  level: 0
  code: 0
  message: disable vr follow head

------
diagnostic_value:
  stamp: 50767494403
  name: working_mode
  level: 0
  code: 0
  message: developer_mode

------
diagnostic_value:
  stamp: 1010017039577
  name: battery_cost
  level: 0
  code: 0
  message: 100

------
diagnostic_value:
  stamp: 1010017068743
  name: battery_charge
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 1010017076910
  name: battery_voltage
  level: 0
  code: 0
  message: 56

------
diagnostic_value:
  stamp: 1010017083035
  name: motor_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1010017088577
  name: motor_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1010017093243
  name: motor1_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1010017099077
  name: motor1_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1010017104327
  name: motor2_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1010017108993
  name: motor2_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1010017114243
  name: voltage_24v
  level: 0
  code: 0
  message: 25

------
diagnostic_value:
  stamp: 1010017119785
  name: current_24v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1010017125618
  name: voltage_15v
  level: 0
  code: 0
  message: 16

------
diagnostic_value:
  stamp: 1010017130285
  name: current_15v
  level: 0
  code: 0
  message: 2

------
diagnostic_value:
  stamp: 1010017134952
  name: voltage_12v
  level: 0
  code: 0
  message: 12

------
diagnostic_value:
  stamp: 1010017139618
  name: current_12v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1010020746952
  name: battery
  level: 0
  code: 100
  message: 100

------
diagnostic_value:
  stamp: 1040017682549
  name: battery_cost
  level: 0
  code: 0
  message: 100

------
diagnostic_value:
  stamp: 1040017712591
  name: battery_charge
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 1040017719008
  name: battery_voltage
  level: 0
  code: 0
  message: 56

------
diagnostic_value:
  stamp: 1040017725424
  name: motor_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1040017730966
  name: motor_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1040017736216
  name: motor1_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1040017741758
  name: motor1_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1040017747299
  name: motor2_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1040017752549
  name: motor2_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1040017758674
  name: voltage_24v
  level: 0
  code: 0
  message: 25

------
diagnostic_value:
  stamp: 1040017763924
  name: current_24v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1040017769174
  name: voltage_15v
  level: 0
  code: 0
  message: 16

------
diagnostic_value:
  stamp: 1040017774133
  name: current_15v
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 1040017779091
  name: voltage_12v
  level: 0
  code: 0
  message: 12

------
diagnostic_value:
  stamp: 1040017783758
  name: current_12v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1040021298341
  name: battery
  level: 0
  code: 100
  message: 100

------
diagnostic_value:
  stamp: 1070017263855
  name: battery_cost
  level: 0
  code: 0
  message: 100

------
diagnostic_value:
  stamp: 1070017288647
  name: battery_charge
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 1070017295064
  name: battery_voltage
  level: 0
  code: 0
  message: 56

------
diagnostic_value:
  stamp: 1070017301480
  name: motor_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1070017307022
  name: motor_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1070017311980
  name: motor1_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1070017317522
  name: motor1_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1070017323939
  name: motor2_voltage
  level: 0
  code: 0
  message: 55

------
diagnostic_value:
  stamp: 1070017328605
  name: motor2_current
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1070017333855
  name: voltage_24v
  level: 0
  code: 0
  message: 25

------
diagnostic_value:
  stamp: 1070017339105
  name: current_24v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1070017344064
  name: voltage_15v
  level: 0
  code: 0
  message: 16

------
diagnostic_value:
  stamp: 1070017348730
  name: current_15v
  level: 0
  code: 0
  message: 1

------
diagnostic_value:
  stamp: 1070017353980
  name: voltage_12v
  level: 0
  code: 0
  message: 12

------
diagnostic_value:
  stamp: 1070017358939
  name: current_12v
  level: 0
  code: 0
  message: 0

------
diagnostic_value:
  stamp: 1070020944689
  name: battery
  level: 0
  code: 100
  message: 100

Process finished with exit code 130 (interrupted by signal 2:SIGINT)

```

### 2.2.11 setRobotLightEffect 接口介绍

| 项目 | 内容 |
|---|---|
| 函数名 | **setRobotLightEffect** |
| 函数原型 | `def setRobotLightEffect(self, effect: datatypes.LightEffect)` |
| 功能概述 | 在真机部署中，该方法用于设置机器人的灯光效果。 |
| 参数 | `effect`：表示所需机器人灯光效果的枚举值，具体定义见 `datatypes.LightEffect`。 |
| 返回值 | 成功返回 `True`，失败返回 `False`。 |

**备注**
Tron2::LightEffect 枚举定义：

```python
enum LightEffect : int {
    STATIC_RED = 0,     // 静态红光
    STATIC_GREEN,       // 静态绿光
    STATIC_BLUE,        // 静态蓝光
    STATIC_CYAN,        // 静态青光
    STATIC_PURPLE,      // 静态紫光
    STATIC_YELLOW,      // 静态黄光
    STATIC_WHITE,       // 静态白光
    LOW_FLASH_RED,      // 红光闪烁（慢闪）
    LOW_FLASH_GREEN,    // 绿光闪烁（慢闪）
    LOW_FLASH_BLUE,     // 蓝光闪烁（慢闪）
    LOW_FLASH_CYAN,     // 青光闪烁（慢闪）
    LOW_FLASH_PURPLE,   // 紫光闪烁（慢闪）
    LOW_FLASH_YELLOW,   // 黄光闪烁（慢闪）
    LOW_FLASH_WHITE,    // 白光闪烁（慢闪）
    FAST_FLASH_RED,     // 红光闪烁（快速闪）
    FAST_FLASH_GREEN,   // 绿光闪烁（快速闪）
    FAST_FLASH_BLUE,    // 蓝光闪烁（快速闪）
    FAST_FLASH_CYAN,    // 青光闪烁（快速闪）
    FAST_FLASH_PURPLE,  // 紫光闪烁（快速闪）
    FAST_FLASH_YELLOW,  // 黄光闪烁（快速闪）
    FAST_FLASH_WHITE    // 白光闪烁（快速闪）
};
```

**代码示例：**

```python
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes

class RobotReceiver:

if __name__ == '__main__':
    # 创建一个类型为Tron2的Robot实例
    robot = Robot(RobotType.Tron2)

    robot_ip = "10.192.1.2"
    # 检查是否提供了机器人 IP 的命令行参数
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # 使用 robot_ip 初始化机器人
    if not robot.init(robot_ip):
        sys.exit()

    # 设置机器人灯光效果为静态红光
    robot.setRobotLightEffect(datatypes.LightEffect.STATIC_RED)

```

# 3 上层应用开发接口

## 3.1 概述

在`上层开发者模式`下机器人通过WebSocket通信端口`5000`来接收用户端请求指令，例如让机器人站起、蹲下、行走等。WebSocket是一种实时通信协议，在机器人和用户端之间建立长连接，以便快速有效地传输控制信息和数据。如下图所示：
![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/robot_info_page_wf.png)

## 3.2 通信协议格式

当机器人通过 WebSocket 接收客户端指令时，采用 JSON 数据协议进行信息传递。这种方式具有显著优势：WebSocket 是一种全双工通信协议，能够在客户端与服务器之间建立实时、低延迟的连接，特别适合频繁交互的应用场景。JSON 数据协议则以其简洁、可读性强的结构，确保数据传输直观明了，且具有跨平台、跨语言的兼容性。WebSocket 与 JSON 的结合不仅与编程语言无关，适用于各种设备和系统，还能提升开发的灵活性和维护的便利性。

- 请求数据格式包含以下字段：
  - `accid`：机器人序列号，注意修为您机器人的序列号；
  - `title`：指令名称，以“`request_`”为前缀；
  - `timestamp`：指令发出时间戳，单位为毫秒；
  - `guid`：指令的唯一标识符，用于区分不同的请求指令。如果是同步接口，则需要在“response_xxx”响应消息中通过guid字段将值带回给客户端。客户端接收到响应消息后，可以通过比较guid字段的值是否与请求指令中的值相同来判断指令是否执行完成；
  - `data`：存放请求指令的数据内容。可以根据具体需求包含多个子字段，以存放请求指令所需的数据内容，例如执行动作的参数、发送消息的文本内容等等；
  - 示例如下：
```

{
  "accid": "WF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号
  "title": "request_xxx",   // 指令名称，以“request_”为前缀
  "timestamp": 1672373633989, // 指令发出时间戳，单位为毫秒
  "guid": "746d937cd8094f6a98c9577aaf213d98", // 指令的唯一标识符，用于区分不同的请求指令
  "data": {}  // 存放请求指令的数据内容
}

```

- 响应数据格式包含以下字段：
  - `accid`：机器人序列号，注意修为您机器人的序列号；
  - `title`：指令名称，以“`response_`”为前缀；
  - `timestamp`：指令发出时间戳，单位为毫秒；
  - `guid`：与对应请求指令的`guid`值相同；
  - `data`：至少应该包含一个“result”子字段，用于存放请求指令的执行结果数据。如果有需要，还可以包含其他子字段，例如错误码、错误信息等用于描述操作结果的信息；
  - 示例如下：
```json
{
  "accid": "WF_TRON2A_001",   // 机器人序列号，注意修为您机器人的序列号
  "title": "response_xxx",  // 指令名称，以“response_”为前缀
  "timestamp": 1672373633989, // 指令发出时间戳，单位为毫秒
  "guid": "746d937cd8094f6a98c9577aaf213d98", // 与对应请求指令的guid值相同
  "data": { # 存放响应指令的具体数据内容
    "result": "success"  // “result” 用于存放请求指令处理是否成功，它的值为：“success 或 fail_xxx”
  }
}
```

- 消息推送：它是机器人主动向客户端发送信息的过程。这些信息可以包括机器人的序列号、当前运行状态、执行的操作等数据。通过及时地向客户端发送这些信息，机器人可以帮助客户端更好地理解它的工作状态，从而更好地使用它提供的服务。它的数据格式包含以下字段：
  - `accid`：机器人序列号，注意修为您机器人的序列号；
  - `title`：指令名称，以“`notify_`”为前缀；
  - `timestamp`：消息发出时间戳，单位为毫秒；
  - `guid`：消息的 guid 值，唯一标识这条消息；
  - `data`：存放消息数据内容。可以根据具体需求包含多个子字段，以存放请求指令所需的数据内容；
  - 示例如下：

```json
{
  "accid": "WF_TRON2A_001",   // 机器人序列号，注意修为您机器人的序列号
  "title": "notify_xxx",  // 消息名称，以“notify_”为前缀
  "timestamp": 1672373633989, // 消息发出时间戳，单位为毫秒
  "guid": "746d937cd8094f6a98c9577aaf213d98", // 消息的guid值，唯一标识这条消息
  "data": { } // 存放消息数据内容
}
```

## 3.3 查看软件序列号(ACCID)

- 连接机器人无线网络
  - 机器人开机完成后，使用个人电脑连接机器人 Wi-Fi，名称格式通常为「WF_TRON2A_xxx」

  - 输入 Wi-Fi 密码：`12345678`
- 在浏览器中输入 `http://10.192.1.2:8080` 可以进入“机器人信息页”，并查看机器人信息。如下图所示，页面中显示的 SN (序列号) 为 SF_TRON2A_127，其中 SF_TRON2A_127 便是此机器人的软件序列号。
![](https://oss.limxdynamics.com/uploads/image/2026/07/27/1785121100498_20260727-105625.jpg)

## 3.4 通信测试方法

Postman 是一个流行的 API 测试软件，可以用于测试 WebSocket 接口。使用 Postman 测试 WebSocket 接口，请按照以下步骤操作：
  - 安装postman，下载地址：https://www.postman.com/downloads/?utm_source=postman-home；
  - 打开Postman，并创建一个WebSocket的请求；
  - 连接机器人无线网络
    - 机器人开机完成后，使用个人电脑连接机器人Wi-Fi，名称格式通常为「TRON2A_xxx」
    - 输入Wi-Fi密码：12345678
  - 在请求的URL中输入WebSocket接口的地址，例如，“ws://10.192.1.2:5000”;
  - 在“Message”中，输入要发送的指令请求；
  - 单击“Send”按钮，发送请求指令；
  - 发送指令后，可以从服务器接收响应消息。使用Postman的响应窗口查看服务器返回的数据，并检查是否符合预期结果。
![](https://oss.limxdynamics.com/uploads/image/2026/07/27/1785121332033_20260727-110046.jpg)

## 3.5 通用协议接口定义

该机器人接口设计遵循与遥控器操控一致的流程和状态流转，确保调用顺序、响应时序及状态过渡与遥控器控制逻辑严格对齐。用户通过接口调用可获得如同使用遥控器的直观体验，同时支持遥控器与接口间的无缝切换，实现统一、稳定的机器人操控效果。

### 3.5.1 全局消息

#### 3.5.1.1 机器人基本信息

  机器人基本信息每秒上报一次，包含以下内容：

  - accid：机器人序列号
  - title：notify_robot_info
  - timestamp：消息发出时间戳，单位为毫秒
  - guid：消息的guid值，唯一标识这条消息
  - data：存放消息内容，示例如下：

**示例：**

```json
{
  "accid": "WF_TRON2A_001",
  "title": "notify_robot_info",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "accid": "WF_TRON2A_001",
    "sw_version": "robot-tron2-2.0.10.20241111103012",
    "imu": "OK",    // 机器人IMU诊断信息
    "camera": "OK", // 机器人相机诊断信息
    "motor": "OK",  // 机器人电机诊断信息
    "battery": 95,  // 机器人电量
    "status": "WALK" // 机器人运行模式
  }
}
```

| 字段 | 说明 |
|---|---|
| `accid` | 机器人序列号 |
| `sw_version` | 机器人本体软件版本信息 |
| `imu` | 机器人 IMU 诊断信息 |
| `camera` | 机器人相机诊断信息 |
| `motor` | 机器人电机诊断信息 |
| `battery` | 机器人电池电量 |
| `status` | 机器人运行模式，例如 `STAND`、`WALK`、`SIT`、`DAMPING`、`ROTATE`、`STAIR`、`ERROR_FALLOVER（摔倒）`、`RECOVER(摔倒恢复中)`、`ERROR_RECOVER（摔倒恢复失败）` |

#### 3.5.1.2 非法指令消息

当机器人收到非法格式的请求指令时，发送此消息，包含以下内容：
  - accid：机器人序列号
  - title：notify_invalid_request
  - timestamp：消息发出时间戳，单位为毫秒
  - guid：消息的guid值，唯一标识这条消息
  - data：存放消息内容
  - 示例如下：
```json
{
  "accid": "WF_TRON2A_001",
  "title": "notify_invalid_request",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": "返回原请求指令内容，便于客户端排查问题"
}
```
### 3.5.2 连接Wifi热点

#### 3.5.2.1 请求：request_connect_wifi
本协议用于向机器人路由器发起请求，指令路由器连接到指定 SSID 的 WiFi 热点并返回连接结果。
```json
{
  "accid": "WF_TRON2A_001",
  "title": "request_connect_wifi",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "wifi_band": 0,  // WiFi频段：0=5GHz，1=2.4GHz
      "wifi_ssid": "Limx-Guests",  // 目标WiFi的SSID（WiFi名称），区分大小写，需与实际热点一致
      "wifi_password": "LimX2024",  // 目标WiFi的密码，WPA2-PSK加密方式的密码
      "router_admin_password": "12345678"  // 机器人路由器的管理员密码
  }
}
```

#### 3.5.2.2 响应：response_connect_wifi

```json
{
  "accid": "WF_TRON2A_001",
  "title": "response_connect_wifi",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  // success: 成功
                           // fail_no_wifi_band: 没有指定频段
                           // fail_no_wifi_ssid: 没有指定ssid
                           // fail_no_wifi_password: 没有指定密码
                           // fail_no_router_admin_password: 没有指定密码
  }
}
```

#### 3.5.2.3 消息推送：无

### 3.5.3 查询 Wifi 连接状态

#### 3.5.3.1 请求：request_wifi_connection_status

本协议用于客户端向机器人路由器发起 WiFi 连接状态查询请求，路由器接收请求后反馈当前已连接 WiFi 的核心状态信息，包括关联 SSID、信号强度及连接结果，支撑客户端实时感知设备网络连接状态。客户端发起 WiFi 连接状态查询，需携带机器人路由器管理员密码完成身份校验。

```json
{
  "accid": "WF_TRON2A_001",
  "title": "request_wifi_connection_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "router_admin_password": "12345678"  // 机器人路由器的管理员密码
  }
}
```

#### 3.5.3.2 响应：response_wifi_connection_status

机器人路由器接收查询请求后，返回当前 WiFi 实际连接状态，供客户端解析展示或后续业务处理。

```json
{
  "accid": "WF_TRON2A_001",
  "title": "response_wifi_connection_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "ssid": "Limx-Guests",
      "signal": -56,        // 单位dBm
      "result": "success"   // success: 成功
                            // fail_disconnected
  }
}
```

#### 3.5.3.3 消息推送：无

### 3.5.4 设置灯效

#### 3.5.4.1 请求：request_light_effect

```json
{
  "accid": "WF_TRON2A_001",
  "title": "request_light_effect",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "effect": 1
  }
}
```

请求参数说明：

| 参数名 | 类型 | 描述 |
| --- | --- | --- |
| `data.effect` | 数字 | 灯光效果的编号，对应不同的灯光显示模式，具体映射关系如下：<br> 1: `STATIC_RED`（静态红色）
 2: `STATIC_GREEN`（静态绿色）
 3: `STATIC_BLUE`（静态蓝色）
 4: `STATIC_CYAN`（静态青色）
 5: `STATIC_PURPLE`（静态紫色）
 6: `STATIC_YELLOW`（静态黄色）
 7: `STATIC_WHITE`（静态白色）
 8: `LOW_FLASH_RED`（低频闪烁红色）
 9: `LOW_FLASH_GREEN`（低频闪烁绿色）
 10: `LOW_FLASH_BLUE`（低频闪烁蓝色）
 11: `LOW_FLASH_CYAN`（低频闪烁青色）
 12: `LOW_FLASH_PURPLE`（低频闪烁紫色）
 13: `LOW_FLASH_YELLOW`（低频闪烁黄色）
 14: `LOW_FLASH_WHITE`（低频闪烁白色）
 15: `FAST_FLASH_RED`（高频闪烁红色）
 16: `FAST_FLASH_GREEN`（高频闪烁绿色）
 17: `FAST_FLASH_BLUE`（高频闪烁蓝色）
 18: `FAST_FLASH_CYAN`（高频闪烁青色）
 19: `FAST_FLASH_PURPLE`（高频闪烁紫色）
 20: `FAST_FLASH_YELLOW`（高频闪烁黄色）
 21: `FAST_FLASH_WHITE`（高频闪烁白色） |

#### 3.5.4.2 响应：response_light_effect

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_light_effect",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_light_effect: 失败
  }
}
```

#### 3.5.4.3 消息推送：无

### 3.5.5 紧急停止

> **提示：**
> 需要在空闲模式下调用，运动过程中无法响应

#### 3.5.5.1 请求：request_emgy_stop

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.5.5.2 响应：response_emgy_stop

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

#### 3.5.5.3 消息推送：无

## 3.6 双臂形态协议接口定义

### 3.6.1 MoveJ 接口介绍

#### 3.6.1.1 请求：request_movej

注意：moveJ 调用时会判断每个关节的关节角是否到达限位，如果超过限位，则会报错超限，本次调用不执行。
moveJ 关节限位（按照左臂关节上到下 -> 右臂关节上到下的顺序，单位 rad）：
- 上限：`[2.60054, 2.96706, 1.48353, 0.523599, 1.39626, 0.7505, 0.7505, 2.60054, 0.261799, 3.66519, 0.523599, 1.74533, 0.7505, 0.7505]`
- 下限：`[-3.14159, -0.261799, -3.66519, -2.61799, -1.74533, -0.7505, -0.7505, -3.14159, -2.96706, -1.48353, -2.61799, -1.39626, -0.7505, -0.7505]`

> **⚠️ 安全提示：**
> - 上述限位为**各关节独立的最大机械极限范围**，请勿将整组上限或下限数组直接填入 `joint` 中一次性下发测试，否则双臂各关节同时大幅度运动极易与机身、桌面发生碰撞或自干涉。
> - 调试时建议基于当前安全姿态（可通过 `request_get_joint_state` 获取），每次仅小幅调整单个关节，并将运动时间 `time` 设为较大值（建议 `≥ 5` 秒），同时确保人员位于工作范围外。

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_movej",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "time": 2, // 2秒运动到指定位置
    "joint": []  //14个joint值, 单位rad
  }
}
```

#### 3.6.1.2 响应：response_movej

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_movej",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_invalid_cmd:错误的命令(缺少字段)
  }
}
```

#### 3.6.1.3 示例

```json
{
    "accid": "DACH_TRON2A_026",
    "title": "request_movej",
    "timestamp": 1672373633989,
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "data": {
        "time": 5,
        "joint": [0,0,0,-1.6,0,0,0,0,0,0,-1.6,0,0,0]
    }
}
```

### 3.6.2 MoveH 接口介绍

此接口为控制头部二自由度

上限：[1.04 ,1.57]

下限：[-0.78, -1.57]

#### 3.6.2.1  请求：request_moveh

```json
{
    "accid": "DACH_TRON2A_026",
    "title": "request_moveh",
    "timestamp": 1672373633989,
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "data": {
        "time": 5,
        "joint": [0.5,0.5] //pitch,yaw
    }
}
```

#### 3.6.2.2 响应：response_moveh

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_moveh",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_invalid_cmd:错误的命令(缺少字段)
  }
}
```

### 3.6.3 MoveP 接口介绍

#### 3.6.3.1 请求：request_movep

参考坐标系如下，坐标系原点位于基座下平面中心，手臂末端基准位于手臂最后一个关节中心
![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/request_movep.png)
> 注意：moveP 调用时会判断左右手的末端位置是否在可达范围内，如果超过限位，则会报错超限，本次调用不执行

moveP 关节限位（按照 x_min, x_max, y_min, y_max, z_min, z_max，单位 m）
左臂：[[0.250, 0.732], [-0.213, 0.900], [-0.673, 0.5]]
右臂：[[0.250, 0.732], [-0.900, 0.213], [-0.673, 0.5]]

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_movep",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "time": 1, //1秒运动到该位置
    "pos": []  //[LEFT_POS(3)、wxyzPOSE(4) + RIGHT_POS(3)、wxyzPOSE(4)]
  }
}
```

#### 3.6.3.2 响应：response_movep

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_movep",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_invalid_cmd:错误的命令(缺少字段)
  }
}
```

#### 3.6.3.3 示例

```json
{
  "accid": "DACH_TRON2A_129",
  "title": "request_movep",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "time": 5,
    "pos": [  0.5,  0.2, -0.3, 0.7, 0,  -0.7,  0,  0.5,-0.2, -0.3,  0.7,   0,  -0.7,  0  ]
  }
}

```

### 3.6.4 ServoJ 控制指令

#### 3.6.4.1 请求：request_servoj

- 推荐在实时系统中按控制频率 300Hz 要求来控制机械臂运动，以保证控制效果和稳定性。

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_servoj",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "filter_ratio": 1.0,//范围是0到1，1表示完全信任真值（无滤波）
    "q": [ ],  // 各个关节的位置，填写16维数据；
  }
}

// 请注意将以上参数更换为实际参数
```

#### 3.6.4.2 响应：无

#### 3.6.4.3 消息推送：notify_servoJ

当 ServoJ 控制操作执行失败时，服务器会主动推送此消息，告知客户端失败原因。

```json
{
  "accid": "WF_TRON2A_001",
  "title": "notify_servoJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "fail_invalid_cmd"  # fail_invalid_cmd: 非法指令， fail_motor: 电机错误
  }
}
```

### 3.6.5 ServoP 控制指令

#### 3.6.5.1 请求：request_servop

持续发送左右手的pos数据.推荐在实时系统中按控制频率 300Hz 要求来控制机械臂运动，以保证控制效果和稳定性。

```json
{
 "accid": "DACH_TRON2A_026", // 机器人序列号，注意修为您机器人的序列号；
 "title": "request_servop",
 "timestamp": 1672373633989,
 "guid": "746d937cd8094f6a98c9577aaf213d98",
 "data": {           }     //[LEFT_POS(3)、wxyzPOSE(4) + RIGHT_POS(3)、wxyzPOSE(4)]
}

```

#### 3.6.5.2 响应：无

#### 3.6.5.3 消息推送：notify_servop

### 3.6.6 获取双臂末端位姿

#### 3.6.6.1 请求：request_get_move_pose

通过此接口获取机器人双臂的末端位姿信息。

```json
{
  "accid": "DACH_TRON2A_036", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_get_move_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.6.6.2 响应：response_get_move_pose

接收到请求后，返回双臂当前位姿的相关信息。

```
{
  "accid": "WF_TRON2A_001",
  "title": "response_get_move_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, // 表示数据时戳，单位为毫秒
      "left_position": [0.0, 0.0, 0.0], // 表示左臂末端的位置，单位为米，顺序为 x, y, z
      "left_quat": [1.0， 0.0, 0.0, 0.0], // 表示左臂末端的姿态，以四元数表示，顺序为 w, x, y, z
      "right_position": [0.0, 0.0, 0.0], // 表示右臂末端的位置，单位为米，顺序为 x, y, z
      "right_quat": [1.0, 0.0, 0.0, 0.0], // 表示右臂末端的姿态，以四元数表示，顺序为 w, x, y, z
      "result": "success"  // fail_not_data
  }
}
```

### 3.6.7 获取机器人关节状态

#### 3.6.7.1 请求：request_get_joint_state

此请求用于获取机器人的各个关节状态。

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_get_joint_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.6.7.2 响应：response_get_joint_state

接收到请求后，返回机器人各个关节当前状态。

```
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_joint_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "names": [], // 各个关节的名称
      "q": [],     // 各个关节的位置
      "dq": [],    // 各个关节的速度
      "tau": [],   // 各个关节的扭矩
      "result": "success"  // fail_not_data
  }
}
```

#### 3.6.7.3 消息推送：无

### 3.6.8 柔顺控制
#### 3.6.8.1 开启/关闭柔顺控制
请求：request_set_compliant_enable
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_set_compliant_enable",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "enable": true
  }
}

```
响应：response_set_compliant_enable

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_compliant_enable",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "enable": true
  }
}

```
#### 3.6.8.2 查询柔顺控制开关
请求：request_get_compliant_enable

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_get_compliant_enable",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

```
响应：response_get_compliant_enable

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_compliant_enable",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "enable": false
  }
}

```

#### 3.6.8.3 设置柔顺控制参数
请求：request_set_compliant_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_set_compliant_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "title": "joint_impedance_kp",            //joint_impedance_kp/cart_impedance_kp
    "data": [ /* joint 14 项 / cart 12 项 */ ]
  }
}

```
响应：response_set_compliant_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_compliant_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "title": "joint_impedance_kp"
  }
}

```

#### 3.6.8.4 查询柔顺控制参数
请求：request_get_compliant_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_get_compliant_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "title": "joint_toque"    // 可选；缺省时同时返回 joint_toque(14) 与 cart_force(12)。
  }
}
```

响应：response_get_compliant_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_compliant_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "title": "joint_toque",
    "data": [    ]
  }
}

```
缺省 title 时响应：
```json
{
  "data": {
    "result": "success",
    "joint_toque": [ /* 14 */ ],
    "cart_force": [ /* 12 */ ]
  }
}

```

### 3.6.9 设置机械臂末端参数

#### 3.6.9.1 手动设置机械臂负载参数
请求：request_set_payload_param

```json

{
  "accid": "DACH_TRON2A_001",
  "title": "request_set_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "arm": 0,     //0=左臂，1=右臂；data：真实物理量 [m(kg), mcx, mcy, mcz(kg·m)]。
    "data": [0.5, 0.0, 0.0, 0.01]
  }
}

```

响应：response_set_payload_param
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "arm": 0
  }
}

```

#### 3.6.9.2 自动辨识单臂负载

让机械臂自动辨识（拟合）单臂负载。
`data.arm`：0=左臂，1=右臂（或字符串 "left"/"right"）
底层由 snowball 下发 `/dyn_identify/cmd [4,3,arm]`

请求：request_fit_payload_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_fit_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "arm": 0
  }
}
```

响应：response_fit_payload_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_fit_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "arm": 0,
    "message": "payload fit left ok"
  }
}
```

#### 3.6.9.3 查询双臂负载参数

请求：request_get_payload_param
```json

{
  "accid": "DACH_TRON2A_001",
  "title": "request_get_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

```

响应：response_get_payload_param

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_payload_param",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "data": [ /* m_L,mcx_L,mcy_L,mcz_L, m_R,mcx_R,mcy_R,mcz_R */ ]
  }
}
```

### 3.6.10 拖动示教
#### 3.6.10.1 进入拖动示教
请求：request_enter_drag_teach

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_enter_drag_teach",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

```
响应：response_enter_drag_teach
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_enter_drag_teach",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "success": true,
    "message": ""
  }
}

```

#### 3.6.10.1 拖动示教轨迹管理
请求：request_drag_teach_manage
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_drag_teach_manage",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "cmd": "start_record",   //status/list/start_record/stop_record/play/delete/rename/gripper/exit
    "arg": ""                 //play/delete=文件名；rename="old|newlabel"；gripper=open/close/toggle
  }
}

```

响应：response_drag_teach_manage

```json

{
  "accid": "DACH_TRON2A_001",
  "title": "response_drag_teach_manage",
  "timestamp": 1762771300000,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success",
    "success": true,
    "message": "",
    "cmd": "start_record"
  }
}

```

仅当cmd == "list" 时，额外返回 files 数组：

```json
{
  "data": {
    "result": "success",
    "success": true,
    "cmd": "list",
    "files": ["traj_1", "traj_2"]
  }
}

```

### 3.6.11 获取升降台状态（仅适用于移动版双臂）

#### 3.6.11.1 请求：request_lifter_state

```json
  {
    "accid": "DACH_TRON2A_001",
    "title": "request_lifter_state",
    "guid": "6f5d2f3c27ac4ef89a4f2c31e8b7f402",
    "timestamp": 1762771201000,
    "data": {}
  }

```

#### 3.6.11.2 响应：response_lifter_state

```json
  {
    "accid": "DACH_TRON2A_001",
    "title": "response_lifter_state",
    "guid": "6f5d2f3c27ac4ef89a4f2c31e8b7f402",
    "timestamp": 1762771201000,
    "data": {
      "result": "success",
      "q": [0.12],
      "v": [0.00]
    }
  }

```

### 3.6.12 升降台绝对位置控制（仅适用于移动版双臂）
#### 3.6.12.1 请求：request_set_lifter_position

```json
  {
  "accid": "DACH_TRON2A_001",
  "title": "request_set_lifter_position",
  "guid": "8b9d7caa48d44b9b9e0d6b2f2b30a101",
  "timestamp": 1762771300000,
    "data": {
    "position": 200.5,     //目标位置，单位 mm
    "duration": 2000      //到达时间，单位 ms，整数；绝对位置控制允许 0，表示最快速度到达
     }
  }
```
#### 3.6.12.2 响应:response_set_lifter_position

```json
{
"accid": "DACH_TRON2A_001",
 "title": "response_set_lifter_position",
 "guid": "8b9d7caa48d44b9b9e0d6b2f2b30a101",
  "timestamp": 1762771300000,
   "data": {
    "result": "success"
    }
}

```

### 3.6.13 升降台速度控制（仅适用于移动版双臂）
#### 3.6.13.1 请求：request_set_lifter_velocity

```json
{
"accid": "DACH_TRON2A_001",
"title": "request_set_lifter_velocity",
"guid": "8b9d7caa48d44b9b9e0d6b2f2b30a101",
"timestamp": 1762771300000,
  "data": {
  "velocity": 50,    //升降台速度，单位 mm/s，有符号，正数/负数分别表示不同方向
  "duration": 2000    //速度控制持续时间，单位 ms，整数，必须 > 0
   }
}

```
#### 3.6.13.2 响应:response_set_lifter_velocity
```json
{ "accid": "DACH_TRON2A_001",
 "title": "response_set_lifter_velocity",
  "guid": "8b9d7caa48d44b9b9e0d6b2f2b30a101",
   "timestamp": 1762771300000,
    "data": {
    "result": "success"
     }
}
```

### 3.6.14 查询升降台当前位置（仅适用于移动版双臂）

#### 3.6.14.1 请求：request_get_lifter_position

```json
{
"accid": "DACH_TRON2A_001",
 "title": "request_get_lifter_position",
 "guid": "8b9d7caa48d44b9b9e0d6b2f2b30a101",
 "timestamp": 1762771300000,
 "data": {
 }
}
```
#### 3.6.14.2 响应:response_get_lifter_position
```json
{ "accid": "DACH_TRON2A_001",
"title": "response_get_lifter_position",
"guid": "...",
 "timestamp": 1762771300000,
  "data": {
  "result": "success",
  "position": 200.3,
  "q": -254.4,
  "q_per_mm": 0.6224,
  "timestamp": 1762771300000
  }
}
```

### 3.6.15 获取底盘状态（仅适用于移动版双臂）

#### 3.6.15.1 请求：request_chassis_state

```json
  {
    "accid": "DACH_TRON2A_001",
    "title": "request_chassis_state",
    "guid": "b6d6f4d2-6f0f-4f0b-8d8e-8f2f3a5a1c01",
    "timestamp": 1762771200000,
    "data": {}
  }
```

#### 3.6.15.2 响应:response_chassis_state

```json
   {
    "accid": "DACH_TRON2A_001",
    "title": "response_chassis_state",
    "guid": "b6d6f4d2-6f0f-4f0b-8d8e-8f2f3a5a1c01",
    "timestamp": 1762771200000,
    "data": {
      "result": "success",
      "data": [0.25, -0.10, 0.03]    //inear_velocity,angular_velocity,steering_angle;
    }
  }

```

### 3.6.16 设置底盘运动模式（仅适用于移动版双臂）

#### 3.6.16.1 请求：request_set_chassis_mode

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_set_chassis_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "move_mode": "ackerman"  //目前只有阿克曼（ackerman）模式
  }
}
```

#### 3.6.16.2 响应：response_set_chassis_mode

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_chassis_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功
  }
}
```

### 3.6.17 控制底盘运动（仅适用于移动版双臂）

#### 3.6.17.1 请求：request_chassis_move

```json
{
  "accid": "DACH_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_chassis_move",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.3,  //速度值，范围-1到1
    "y": 0.0,  //速度值，范围-1到1
    "yaw": 0.0   //转弯角速度，范围-1到1
  }
}
```

#### 3.6.17.2 响应：response_chassis_move

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_chassis_move",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

## 3.7 双臂形态协议接口调用示例

### 3.7.1 C++ 示例实现

**环境准备**

以 Ubuntu 22.04 系统为例，安装 `websocketpp`、`nlohmann/json` 和 `boost` 依赖：

```bash
sudo apt-get install libboost-all-dev libwebsocketpp-dev nlohmann-json3-dev
```

**编译代码**

```bash
g++ -std=c++11 -o websocket_client websocket_client.cpp -lssl -lcrypto -lboost_system -lpthread
```

**运行程序**

```bash
./websocket_client
```

** websocket_client.cpp 实现**

```cpp
#include <iostream>
#include <atomic>
#include <string>
#include <thread>
#include <chrono>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio.hpp>
#include <nlohmann/json.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

using json = nlohmann::json;
using websocketpp::client;
using websocketpp::connection_hdl;

// Replace this value with the actual serial number (SN) of the robot.
static std::string ACCID = "";

// Replace it with the real IP address of the robot.
// Usually, for simulation, it is: 127.0.0.1
// for a real machine, it is: 10.192.1.2
const std::string ROBOT_IP = "10.192.1.2";

// WebSocket client instance
static client<websocketpp::config::asio> ws_client;

// Atomic flag for graceful exit
static std::atomic<bool> should_exit(false);

// Connection handle for sending messages
static connection_hdl current_hdl;

// Generate dynamic GUID
static std::string generate_guid()
{
    boost::uuids::random_generator gen;
    boost::uuids::uuid u = gen();
    return boost::uuids::to_string(u);
}

// Send WebSocket request with title and data
static void send_request(const std::string &title, const json &data = json::object())
{
    json message;

    message["accid"] = ACCID;
    message["title"] = title;
    message["timestamp"] = std::chrono::duration_cast<std::chrono::milliseconds>(
                               std::chrono::system_clock::now().time_since_epoch())
                               .count();
    message["guid"] = generate_guid();
    message["data"] = data;

    std::string message_str = message.dump();

    ws_client.send(current_hdl, message_str, websocketpp::frame::opcode::text);
}

// Handle user commands
void handle_commands()
{
    std::cout << "Enter command ('movej', 'movep', 'light', 'stop') or 'exit' to quit:\n";

    while (!should_exit)
    {
        std::string command;
        std::cin >> command;

        if (command == "exit")
        {
            should_exit = true;
            return;
        }
        else if (command == "movej")
        {
            json data = {
                {"joint", {-0.5, 0.3, -0.2, 0.2, 0.2, 0.2, 0.2,
                           -0.5, -0.3, -0.2, 0.2, 0.2, 0.2, 0.2}},
                {"time", 2}};
            send_request("request_movej", data);
        }
        else if (command == "movep")
        {
            json data = {
                {"pos", {0.3, 0.2, -0.3, 1, 0, 0, 0, 1, 0, 0, 0, 1,
                         0.3, -0.2, -0.3, 1, 0, 0, 0, 1, 0, 0, 0, 1}},
                {"time", 2}};
            send_request("request_movep", data);
        }
        else if (command == "light")
        {
            send_request("request_light_effect", {{"effect", 1}});
        }
        else if (command == "stop")
        {
            send_request("request_emgy_stop");
        }

        std::this_thread::sleep_for(std::chrono::seconds(1));

        std::cout << "Enter command ('movej', 'movep', 'light', 'stop') or 'exit' to quit:\n";
    }
}

// WebSocket open callback
static void on_open(connection_hdl hdl)
{
    std::cout << "Connected!" << std::endl;

    current_hdl = hdl;

    std::thread(handle_commands).detach();
}

// WebSocket TCP initialization handler
static void on_tcp_init(connection_hdl hdl)
{
    auto con = ws_client.get_con_from_hdl(hdl);
    auto &socket = con->get_socket().lowest_layer();

    try
    {
        boost::system::error_code ec;

        const size_t sendBufferSize = 2 * 1024 * 1024;
        socket.set_option(websocketpp::lib::asio::socket_base::send_buffer_size(sendBufferSize), ec);

        if (ec)
        {
            printf("Failed to set send buffer size: %s", ec.message().c_str());
        }

        const size_t recvBufferSize = 2 * 1024 * 1024;
        socket.set_option(websocketpp::lib::asio::socket_base::receive_buffer_size(recvBufferSize), ec);

        if (ec)
        {
            printf("Failed to set receive buffer size: %s", ec.message().c_str());
        }

        socket.set_option(websocketpp::lib::asio::ip::tcp::no_delay(true), ec);

        if (ec)
        {
            printf("Failed to disable Nagle's algorithm: %s", ec.message().c_str());
        }
    }
    catch (const std::exception &e)
    {
        printf("Socket configuration exception: %s", e.what());
    }
}

// WebSocket message callback
static void on_message(connection_hdl hdl, client<websocketpp::config::asio>::message_ptr msg)
{
    json data = json::parse(msg->get_payload());

    if (data.contains("accid") && data["accid"].is_string() && ACCID.empty())
    {
        ACCID = data["accid"].get<std::string>();
    }

    if (msg->get_payload().find("notify_robot_info") == std::string::npos)
    {
        std::cout << "Received message: " << msg->get_payload() << std::endl;
    }
}

// WebSocket close callback
static void on_close(connection_hdl hdl)
{
    std::cout << "Connection closed." << std::endl;
}

// Close WebSocket connection
static void close_connection(connection_hdl hdl)
{
    ws_client.close(hdl, websocketpp::close::status::normal, "Normal closure");
}

int main()
{
    ws_client.init_asio();

    ws_client.set_access_channels(websocketpp::log::alevel::none);

    ws_client.set_open_handler(&on_open);
    ws_client.set_message_handler(&on_message);
    ws_client.set_close_handler(&on_close);
    ws_client.set_tcp_init_handler(&on_tcp_init);

    std::string server_uri = "ws://" + ROBOT_IP + ":5000";

    websocketpp::lib::error_code ec;
    client<websocketpp::config::asio>::connection_ptr con = ws_client.get_connection(server_uri, ec);

    if (ec)
    {
        std::cout << "Error: " << ec.message() << std::endl;
        return 1;
    }

    ws_client.connect(con);

    std::cout << "Press Ctrl+C to exit." << std::endl;

    ws_client.run();

    return 0;
}
```

---

### 3.7.2 Python 示例实现

**环境准备**

以 Ubuntu 22.04 系统、Python 3.10.4 版本为例，安装下面依赖：

```bash
sudo apt install python3-dev python3-pip
pip3 install websocket-client
```

**运行脚本**

```bash
python3 websocket_client.py
```

**websocket_client.py 实现**

```python

import json
import uuid
import threading
import time
import websocket

# Replace this ACCID value with your robot's actual serial number (SN)
ACCID = None

# Replace it with the real IP address of the robot.
# for a real machine, it is: 10.192.1.2
ROBOT_IP = "10.192.1.2"

# Atomic flag for graceful exit
should_exit = False

# WebSocket client instance
ws_client = None

# Generate dynamic GUID
def generate_guid():
    return str(uuid.uuid4())

# Send WebSocket request with title and data
def send_request(title, data=None):
    global ACCID

    if data is None:
        data = {}

    message = {
        "accid": ACCID,
        "title": title,
        "timestamp": int(time.time() * 1000),
        "guid": generate_guid(),
        "data": data
    }

    message_str = json.dumps(message)

    if ws_client:
        ws_client.send(message_str)

# Handle user commands
def handle_commands():
    global should_exit

    while not should_exit:
        command = input("Enter command ('movej', 'movep', 'light', 'stop') or 'exit' to quit:\n")

        if command == "exit":
            should_exit = True
            break

        elif command == "movej":
            send_request("request_movej", {
                "joint": [
                    0,0,0,-1.6,0,0,0,0,0,0,-1.6,0,0,0
                ],
                "time": 5
            })
        elif command == "movep":
            send_request("request_movep", {
                "pos": [
                    0.5,  0.2, -0.3, 0.7, 0,  -0.7,  0,  0.5,-0.2, -0.3,  0.7,   0,  -0.7,  0
                ],
                "time": 5
            })

        elif command == "light":
            send_request("request_light_effect", {
                "effect": 1
            })

        elif command == "stop":
            send_request("request_emgy_stop", {})

# WebSocket on_open callback
def on_open(ws):
    print("Connected!")
    threading.Thread(target=handle_commands, daemon=True).start()

# WebSocket on_message callback
def on_message(ws, message):
    global ACCID

    root = json.loads(message)
    title = root.get("title", "")

    if ACCID is None:
        ACCID = root.get("accid", None)

    if title != "notify_robot_info":
        print(f"Received message: {message}")

# WebSocket on_close callback
def on_close(ws, close_status_code, close_msg):
    print("Connection closed.")

# Close WebSocket connection
def close_connection(ws):
    ws.close()

def main():
    global ws_client

    ws_client = websocket.WebSocketApp(
        f"ws://{ROBOT_IP}:5000",
        on_open=on_open,
        on_message=on_message,
        on_close=on_close
    )

    print("Press Ctrl+C to exit.")
    ws_client.run_forever()

if __name__ == "__main__":
    main()

```

## 3.8 双足/双轮足协议接口定义

该机器人接口设计遵循与遥控器操控一致的流程和状态流转，确保调用顺序、响应时序及状态过渡与遥控器控制逻辑严格对齐。用户通过接口调用可获得如同使用遥控器的直观体验，同时支持遥控器与接口间的无缝切换，实现统一、稳定的机器人操控效果。
![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/biped_wheeled_foot_if_protocol_def.png)

### 3.8.1 起立状态

#### 3.8.1.1 请求：request_stand_mode
```json
{
  "accid": "SF_TRON2A_001",  // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
```

#### 3.8.1.2 响应：response_stand_mode

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
}
```

#### 3.8.1.3 消息推送：notify_stand_mode

机器人站起过程失败或完成后，主动推送此消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

### 3.8.2 行走状态

#### 3.8.2.1 请求：request_walk_mode

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
```

#### 3.8.2.2 响应：response_walk_mode

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
}
```

#### 3.8.2.3 消息推送：notify_walk_mode

机器人站起过程失败或完成后，主动推送此消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

### 3.8.3 控制行走

#### 3.8.3.1 请求：request_twist

请按 30Hz 及以上发送指令。

- 掌足模式下按如下规则：

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_twist",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.0,   //  前进后退速度比值，取值范围[-1, 1]
    "y": 0.0,   //  横向行走速度比值，取值范围[-1, 1]
    "z": 0.0    //  旋转角速度比值，取值范围[-1, 1]
  }
}
```

- 轮足模式下按如下规则：

```json
{
  "accid": "WF_TRON1A_075", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_twist",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.0,   //  前进后退速度（m/s），取值范围[-3.0, 3.0]；同时注意，楼梯模式下发速度需要大于1.5，否则上不了楼梯
    "y": 0.0,   //  横向行走速度为0.0（m/s），轮足模式下不支持横向移动
    "z": 0.0    //  旋转角速度（m/s），取值范围[-1.5, 1.5]
  }
}
```

#### 3.8.3.2 响应：无

#### 3.8.3.3 消息推送：notify_twist

机器人行走失败时，主动推送此消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_twist",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "fail_motor"  // fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

### 3.8.4 调整机器身高

#### 3.8.4.1 请求：request_base_height

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_base_height",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "direction": -1  // 1：表示升高，-1：表示降低
                     // 每次调用此请求会使机器人身高相应升高或降低 5cm
  }
}
```

#### 3.8.4.2 响应：response_base_height

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_base_height",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_status: 则表示机器人当前状态不允许调整身高
  }
}
```

#### 3.8.4.3 消息推送：无

### 3.8.5 蹲下

#### 3.8.5.1 请求：request_sitdown

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_sitdown",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.8.5.2 响应：response_sitdown

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_sitdown",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

#### 3.8.5.3 消息推送：notify_sitdown

机器人蹲下过程失败或完成后，主动推送此消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_sitdown",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

### 3.8.6 开启楼梯模式（仅适用于双轮足形态）

#### 3.8.6.1 请求：request_stair_mode

```json
{
  "accid": "WF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_stair_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: 开启楼梯模式, false: 关闭楼梯模式
  }
}
```

#### 3.8.6.2 响应：response_stair_mode

```json
{
  "accid": "WF_TRON2A_001",
  "title": "response_stair_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

#### 3.8.6.3 消息推送：无

### 3.8.7 紧急停止

#### 3.8.7.1 请求：request_emgy_stop

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.8.7.2 响应：response_emgy_stop

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
```

#### 3.8.7.3 消息推送：无

### 3.8.8 开启 IMU 数据

#### 3.8.8.1 请求：request_enable_imu

该功能用于开启 IMU 数据推送，开启后系统将主动推送 IMU 数据。

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_enable_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: 开启IMU, false: 禁用IMU
  }
}
```

#### 3.8.8.2 响应：response_enable_imu

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_enable_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_imu: IMU
  }
}
```

#### 3.8.8.3 消息推送：notify_imu

开启 IMU 数据后，系统将主动推送包含 IMU 状态的消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "euler": [0.0, 0.0, 0.0],     // 欧拉角 [roll, pitch, yaw] in degrees
      "acc": [0.0, 0.0, 0.0],       // 加速度 [x, y, z] in m/s²
      "gyro": [0.0, 0.0, 0.0],      // 陀螺仪角速度 [x, y, z] in rad/s
      "quat": [0.0, 0.0, 0.0, 0.0]  // 四元数 [w, x, y, z]
  }
}
```

### 3.8.9 摔倒恢复

#### 3.8.9.1 请求：request_recover

当机器人摔倒后，可以调用此接口让机器人自动爬起来并恢复到行走模式。

```json
{
  "accid": "SF_TRON2A_001", // 机器人序列号，注意修为您机器人的序列号；
  "title": "request_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
```

#### 3.8.9.2 响应：response_recover

```json
{
  "accid": "SF_TRON2A_001",
  "title": "response_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功收到指令，开始恢复爬起, fail_no_fallover: 当前没摔倒
  }
}
```

#### 3.8.9.3 消息推送：notify_recover

完成恢复爬起后，推送此消息。

```json
{
  "accid": "SF_TRON2A_001",
  "title": "notify_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  // success: 摔倒恢复成功, fail_recover: 摔倒恢复失败
  }
}
```

## 3.9 双足/双轮足协议接口调用示例

### 3.9.1 Linux C++ 示例实现

**环境准备**

以 Ubuntu 20.04 系统为例，安装 `websocketpp`、`nlohmann/json` 和 `boost` 依赖：

```bash
sudo apt-get install libboost-all-dev libwebsocketpp-dev nlohmann-json3-dev
```

**编译代码**

```bash
g++ -std=c++11 -o websocket_client websocket_client.cpp -lssl -lcrypto -lboost_system -lpthread
```

**运行程序**

```bash
./websocket_client
```

**websocket_client.cpp 实现**

```cpp
#include <iostream>
#include <atomic>
#include <string>
#include <thread>
#include <chrono>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio.hpp>
#include <nlohmann/json.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

using json = nlohmann::json;
using websocketpp::client;
using websocketpp::connection_hdl;

// Replace this ACCID value with your robot's actual serial number (SN)
static std::string ACCID = "";

// WebSocket client instance
static client<websocketpp::config::asio> ws_client;

// Atomic flag for graceful exit
static std::atomic<bool> should_exit(false);

// Connection handle for sending messages
static connection_hdl current_hdl;

// Generate dynamic GUID
static std::string generate_guid()
{
    boost::uuids::random_generator gen;
    boost::uuids::uuid u = gen();
    return boost::uuids::to_string(u);
}

// Send WebSocket request with title and data
static void send_request(const std::string& title, const json& data = json::object())
{
    json message;

    message["accid"] = ACCID;
    message["title"] = title;
    message["timestamp"] = std::chrono::duration_cast<std::chrono::milliseconds>(
                               std::chrono::system_clock::now().time_since_epoch())
                               .count();
    message["guid"] = generate_guid();
    message["data"] = data;

    std::string message_str = message.dump();

    ws_client.send(current_hdl, message_str, websocketpp::frame::opcode::text);
}

// Handle user commands
static void handle_commands()
{
    while (!should_exit)
    {
        std::string command;
        std::cout << "Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:" << std::endl;
        std::getline(std::cin, command);

        if (command == "exit")
        {
            should_exit = true;
            break;
        }
        else if (command == "stand")
        {
            send_request("request_stand_mode");
        }
        else if (command == "walk")
        {
            send_request("request_walk_mode");
        }
        else if (command == "twist")
        {
            float x, y, z;
            std::cout << "Enter x, y, z values:" << std::endl;
            std::cin >> x >> y >> z;
            std::cin.ignore();

            send_request("request_twist", {
                {"x", x},
                {"y", y},
                {"z", z}
            });
        }
        else if (command == "sit")
        {
            send_request("request_sitdown");
        }
        else if (command == "stair")
        {
            std::string enable;
            std::cout << "Enable stair mode (true/false):" << std::endl;
            std::cin >> enable;
            std::cin.ignore();

            send_request("request_stair_mode", {
                {"enable", enable == "true"}
            });
        }
        else if (command == "stop")
        {
            send_request("request_emgy_stop");
        }
        else if (command == "imu")
        {
            std::string enable;
            std::cout << "Enable IMU (true/false):" << std::endl;
            std::cin >> enable;
            std::cin.ignore();

            send_request("request_enable_imu", {
                {"enable", enable == "true"}
            });
        }
    }
}

// WebSocket open callback
static void on_open(connection_hdl hdl)
{
    std::cout << "Connected!" << std::endl;

    current_hdl = hdl;

    std::thread(handle_commands).detach();
}

// WebSocket message callback
static void on_message(connection_hdl hdl, client<websocketpp::config::asio>::message_ptr msg)
{
    json data = json::parse(msg->get_payload());

    if (data.contains("accid") && data["accid"].is_string() && ACCID.empty())
    {
        ACCID = data["accid"].get<std::string>();
    }

    std::cout << "Received: " << msg->get_payload() << std::endl;
}

// WebSocket close callback
static void on_close(connection_hdl hdl)
{
    std::cout << "Connection closed." << std::endl;
}

// Close WebSocket connection
static void close_connection(connection_hdl hdl)
{
    ws_client.close(hdl, websocketpp::close::status::normal, "Normal closure");
}

int main()
{
    ws_client.init_asio();

    ws_client.set_open_handler(&on_open);
    ws_client.set_message_handler(&on_message);
    ws_client.set_close_handler(&on_close);

    std::string server_uri = "ws://10.192.1.2:5000";

    websocketpp::lib::error_code ec;
    client<websocketpp::config::asio>::connection_ptr con = ws_client.get_connection(server_uri, ec);

    if (ec)
    {
        std::cout << "Error: " << ec.message() << std::endl;
        return 1;
    }

    connection_hdl hdl = con->get_handle();
    ws_client.connect(con);

    std::cout << "Press Ctrl+C to exit." << std::endl;

    ws_client.run();

    return 0;
}
```

### 3.9.2 Python 示例实现

**环境准备**

以 Ubuntu 20.04 系统为例，安装下面依赖：

```bash
sudo apt install python3-dev python3-pip
pip3 install websocket-client
```

**运行脚本**

```bash
python3 websocket_client.py
```

**websocket_client.py 实现**

```python
import json
import uuid
import threading
import time
import websocket

# Replace this ACCID value with your robot's actual serial number (SN)
ACCID = None

# Atomic flag for graceful exit
should_exit = False

# WebSocket client instance
ws_client = None

# Generate dynamic GUID
def generate_guid():
    return str(uuid.uuid4())

# Send WebSocket request with title and data
def send_request(title, data=None):
    global ACCID
    global ws_client

    if data is None:
        data = {}

    # Create message structure with necessary fields
    message = {
        "accid": ACCID,
        "title": title,
        "timestamp": int(time.time() * 1000),
        "guid": generate_guid(),
        "data": data
    }

    message_str = json.dumps(message)

    # Send the message through WebSocket if client is connected
    if ws_client:
        ws_client.send(message_str)

# Handle user commands
def handle_commands():
    global should_exit

    while not should_exit:
        command = input(
            "Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:\n"
        )

        if command == "exit":
            should_exit = True
            break

        elif command == "stand":
            send_request("request_stand_mode")

        elif command == "walk":
            send_request("request_walk_mode")

        elif command == "twist":
            # Get twist values from user
            x = float(input("Enter x value: "))
            y = float(input("Enter y value: "))
            z = float(input("Enter z value: "))

            # Send twist command at 30 Hz for 1 second
            for _ in range(30):
                send_request("request_twist", {
                    "x": x,
                    "y": y,
                    "z": z
                })
                time.sleep(1 / 30)

        elif command == "sit":
            send_request("request_sitdown")

        elif command == "stair":
            # Get stair mode enable flag from user
            enable = input("Enable stair mode (true/false): ").strip().lower() == "true"
            send_request("request_stair_mode", {
                "enable": enable
            })

        elif command == "stop":
            send_request("request_emgy_stop")

        elif command == "imu":
            # Get IMU enable flag from user
            enable = input("Enable IMU (true/false): ").strip().lower() == "true"
            send_request("request_enable_imu", {
                "enable": enable
            })

# WebSocket on_open callback
def on_open(ws):
    print("Connected!")

    # Start handling commands in a separate thread
    threading.Thread(target=handle_commands, daemon=True).start()

# WebSocket on_message callback
def on_message(ws, message):
    global ACCID

    root = json.loads(message)
    ACCID = root.get("accid", None)

    print(f"Received message: {message}")

# WebSocket on_close callback
def on_close(ws, close_status_code, close_msg):
    print("Connection closed.")

# Close WebSocket connection
def close_connection(ws):
    ws.close()

def main():
    global ws_client

    # Create WebSocket client instance
    ws_client = websocket.WebSocketApp(
        "ws://10.192.1.2:5000",
        on_open=on_open,
        on_message=on_message,
        on_close=on_close
    )

    # Run WebSocket client loop
    print("Press Ctrl+C to exit.")
    ws_client.run_forever()

if __name__ == "__main__":
    main()
```

## 3.10 双轮足回充基座接口

### 3.10.1 扫描充电桩

仅 WF 支持。请求只执行一次 `扫描 → 等待既有扫描超时 → 读取列表` 事务，不配对、不点灯、不开充电 MOS、不发送 `EV_GO_DOCKING`，也不修改回充生命周期状态。协议当前仅返回 16 位 little\-endian `dock_id`，没有 RSSI 字段，因此响应不伪造信号强度。

扫描与回充扫描/配对、上桩、充电中和下桩互斥；冲突返回 `fail_dock_busy`。列表按硬件扫描顺序返回并去重，payload 长度必须严格等于 `1 + 2 * 原始数量`。空列表是一次成功扫描，与硬件/协议扫描失败明确区分。

- **请求：request\_scan\_docks**

`data` 必须为空对象：

```JSON
{
"accid": "WF_TRON2A_001",
"title": "request_scan_docks",
"guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000,
 "data": {}
}
```

- **响应：response\_scan\_docks**

```JSON
{
  "accid": "WF_TRON2A_001",
  "title": "response_scan_docks",
  "guid": "d2c6521699e035949c9276cb17b62bdb",
  "timestamp": 1762771300000,
  "data": {
    "success": true,
    "result": "success",
    "message": "charging dock scan completed",
    "docks": [{"dock_id": 0}, {"dock_id": 4660}, {"dock_id": 65535}],
    "count": 3,
    "scanned_at": 1787106660123
  }
}
```

空列表仍为 `success=true/result="success"`，`docks=[]`、`count=0`、`message="no charging docks found"`。`scanned_at` 是完成扫描的 Unix 毫秒；未实际发起硬件扫描的拒绝响应为 `0`。

|`result`|含义|
|---|---|
|`success`|扫描事务成功；`count` 可为 0|
|`fail_dock_scan`|启动扫描或读取扫描结果失败|
|`fail_dock_scan_list`|payload 为空、截断、带多余字节或长度不匹配|
|`fail_dock_busy`|已在扫描、配对、回充/充电或下桩|
|`fail_unsupported_robot`|非 WF 机型|
|`fail_invalid_cmd`|`data` 不是空对象|
|`fail_service_call`|`/tron2/service` 不可达或调用超时|
|`fail_remote`|snowball 服务明确拒绝|
|`fail_invalid_response`|snowball 返回非法 JSON；signaling 不重做列表解析|

### 3.10.2 查询完整回充状态

该接口为只读操作，免开发者模式，不触发扫描、控制器切换或任何硬件副作用。signaling 调用 snowball `/tron2/service` 的 `dock/status`，校验 JSON 后原样透传字段和 `message`。

- **请求：request\_get\_docking\_status**

```JSON
{ "accid": "WF_TRON2A_001",
"title": "request_get_docking_status",
"guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000, "data": {}
 }
```

- **响应：response\_get\_docking\_status**

```JSON
{
  "accid": "WF_TRON2A_001",
  "title": "response_get_docking_status",
  "guid": "d2c6521699e035949c9276cb17b62bdb",
  "timestamp": 1762771300000,
  "data": {
    "success": true,
    "result": "success",
    "message": "waiting for charging confirmation",
    "supported": true,
    "phase": "confirming",
    "robot_state": "ST_GO_DOCKING",
    "in_progress": true,
    "has_dock_id": true,
    "dock_id": 4660,
    "has_requested_dock_id": true,
    "requested_dock_id": 4660,
    "dock_selection": "requested",
    "controller_confirmed": true,
    "charging_confirmed": false,
    "confirm_timeout_sec": 60,
    "confirm_remaining_sec": 42.381,
    "failure_code": 0,
    "failure_source": "",
    "failure_reason": "",
    "previous_failure_code": 7,
    "previous_failure_source": "wheelfoot_gocharge",
    "previous_failure_reason": "docking controller reported an error (code 7)",
    "battery_known": true,
    "battery_percent": 63,
    "minimum_undock_battery_percent": 15,
    "attempt_token": 12,
    "confirm_attempt_token": 2,
    "retry_count": 1,
    "attempt_source": "sdk",
    "updated_at": 1787106660123
  }
}
```

- 字段语义：

`phase` 是稳定业务枚举：`idle`（进程启动后尚未发起）、`preparing`（扫描/选桩/配对）、`docking`（上桩或自动重试）、`confirming`（等待 controller \+ charging 双条件）、`charging`（双条件成功并处于 `ST_DOCKING`）、`undock_preparing`、`undocking`、`canceled`、`failed`、`manual_undock`、`walking`。

`robot_state` 保留 snowball 原始 `ST_*` 状态。业务阶段和原始状态不强制一一对应，例如失败已回 `ST_WALKING` 时仍可保持 `phase=failed`。

`dock_id=null` 且 `has_dock_id=false` 表示尚未选中或进程重启后无法恢复；ID `0` 是合法值，不能表示未知。`requested_dock_id` 仅表示 SDK 请求值，最终配对结果以 `dock_id` 为准。

`attempt_token` 每次 RC/SDK 新发起回充递增并清除旧选桩和失败；`confirm_attempt_token` 是每次 gocharge 双条件窗口编号，自动重试会更新它并递增 `retry_count`。

自动重试开始时，本轮错误移入 `previous_failure_*`；最终失败保留在 `failure_*`。新用户 attempt 清空两组失败；明确成功清空当前失败。

`confirm_remaining_sec` 仅确认窗口活动时为数字，否则为 `null`。它基于单调时钟计算；`updated_at` 是最近业务状态更新时间的 Unix 毫秒。

`battery_percent=null` 且 `battery_known=false` 表示尚未收到有效电量；未知电量不会伪造为 0。

非 WF 返回 `success=true`、`supported=false`、`phase=idle` 和明确 `message`，查询本身仍成功。

- 状态流转：

`idle/walking → preparing → docking → confirming → charging → undock_preparing → undocking → walking`。扫描/配对、控制器、确认超时、低电量或下桩错误进入 `failed`；`offcharge code=-1` 进入 `manual_undock`；用户取消进入 `canceled`。controller 与 charging 两个确认信号顺序无关。

- 查询错误码：

|`result`|含义|
|---|---|
|`fail_service_call`|`/tron2/service` 不可达或调用超时|
|`fail_remote`|snowball 服务明确拒绝查询|
|`fail_invalid_response`|snowball 返回无法解析或缺少基础字段的 JSON；signaling 不崩溃且不改写失败原因|

### 3.10.3 开始回充（仅轮足）

- **请求：request\_start\_docking**

```JSON
{
"accid": "WF_TRON2A_001",
 "title": "request_start_docking",
 "guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000,
 "data": {"dock_id":4660} // 可指定要回充的基座ID, 若不赋值，*"data": {}，*则对接机器人搜索到的第一个回充底座
}
```

- **响应：response\_start\_docking**

```JSON
{
 "accid": "WF_TRON2A_001",
 "title": "response_start_docking",
 "guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000,
 "data": {
 "result": "success",
 "success": true,
  "message": "docking started; docking prepared (dock_id:4660)"
   }
}
```

### 3.10.4 取消回充 / 取消下桩（仅轮足）

- **请求：request\_cancel\_docking**

```JSON
{
"accid": "WF_TRON2A_001",
"title": "request_cancel_docking",
"guid": "d2c6521699e035949c9276cb17b62bdb",
"timestamp": 1762771300000,
"data": {}
}
```

- **响应：response\_cancel\_docking**

```JSON
{
"accid": "WF_TRON2A_001",
"title": "response_cancel_docking",
"guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000,
  "data": {
  "result": "success",
   "success": true,
   "message": "docking canceled" }
}
```

### 3.10.5 下桩（仅轮足）

- **请求：request\_undock**

```JSON
{
"accid": "WF_TRON2A_001",
"title": "request_undock",
"guid": "d2c6521699e035949c9276cb17b62bdb",
"timestamp": 1762771300000,
"data": {}
}
```

- **响应：response\_undock**

```JSON
{
 "accid": "WF_TRON2A_001",
 "title": "response_undock",
 "guid": "d2c6521699e035949c9276cb17b62bdb",
 "timestamp": 1762771300000,
  "data": {
  "result": "success",
  "success": true,
  "message": "undocking started"
  }
}
```

# 4 外接部件开发接口

## 4.1 逐际二指夹爪

### 4.1.1 夹爪控制指令

#### 4.1.1.1 请求：request_set_limx_2fclaw_cmd

此协议控制夹爪的抓取动作。
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_set_limx_2fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      // 如您同时给出以下数据，则会控制左夹爪运动
      "left_opening": 50,  // 开口度，0-100，无量纲（0对应最小闭合，100对应张开到最大)
      "left_speed": 50,    // 夹爪速度，0~100 无量纲（数值越大速度越快）
      "left_force": 50,   //力，夹爪夹持力，0~100 无单位（数值越大力越大）

      // 如您同时给出以下数据，则会控制右夹爪运动
      "right_opening": 50,  // 开口度，0-100，无量纲（0对应最小闭合，100对应张开到最大)
      "right_speed": 50,    // 夹爪速度，0~100 无量纲（数值越大速度越快）
      "right_force": 50   //力，夹爪夹持力，0~100 无单位（数值越大力越大）
  }
}

```

#### 4.1.1.2 响应：response_set_limx_2fclaw_cmd
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_limx_2fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_motor: 电机错误，
  }
}
```

#### 4.1.1.3 消息推送：无

### 4.1.2 获取夹爪状态信息

#### 4.1.2.1 请求：request_get_limx_2fclaw_state

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_get_limx_2fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
```

#### 4.1.2.2 响应：response_get_limx_2fclaw_state

返回夹爪状态信息
```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_limx_2fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, // 表示数据时戳，单位为毫秒

      "left_opening": 50,  // 开口度，0-100，无量纲（0对应最小闭合，100对应张开到最大）
      "left_speed": 50,    // 夹爪速度，0~100 无量纲（数值越大速度越快）
      "left_force": 50,   //力，夹爪夹持力，0~100 无单位（数值越大力越大）

      "right_opening": 50,  // 开口度，0-100，无量纲（0对应最小闭合，100对应张开到最大）
      "right_speed": 50,    // 夹爪速度，0~100 无量纲（数值越大速度越快）
      "right_force": 50,   //力，夹爪夹持力，0~100 无单位（数值越大力越大）

      "result": "success"  // success: 成功, fail_motor: 电机错误
  }
}
```

#### 4.4.2.3 消息推送：无

## 4.2 强脑 Revo 2 灵巧手（基础版）

### 4.2.1 灵巧手控制指令

请求：request_set_brainco2_hand_cmd

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_set_brainco2_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      // 左手：
      // 索引从0-5分别对应：拇指尖、拇指根、食指、中指、无名指、小指
      // left_mode: 控制模式
      //            0：退出控制
      //            1：位置时间模式, 必须指定 left_pos、left_time 的值
      //            2：位置速度模式, 必须指定 left_pos、left_vel 的值
      //           3：力控模式, 必须指定 left_current 的值
      // left_pos: 每个手指的目标位置, 单位为rad
      //           范围分别为0-1.0297、0-1.5707、0-1.4137、0-1.4137、0-1.4137、0-1.4137
      // left_vel: 每个手指的目标速度, 单位为rad/s
      //           范围分别为0-2.5367、0-2.6180、0-2.2689、0-2.2689、0-2.2689、0-2.2689
      // left_current: 每个手指的目标电流, 单位为mA, 范围为±1000mA
      // left_time: 每个手指的控制时间, 单位为ms, 范围为1-2000ms

      "left_mode": 1,
      "left_pos": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "left_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "left_current": [500, 500, 500, 500, 500, 500],
      "left_time": [1000, 1000, 1000, 1000, 1000, 1000],

      // 右手：
      // 索引从0-5分别对应：拇指尖、拇指根、食指、中指、无名指、小指
      // right_mode: 控制模式
      //            0：退出控制
      //            1：位置时间模式, 必须指定 right_pos、right_time 的值
      //            2：位置速度模式, 必须指定 right_pos、right_vel 的值
      //            3：力控模式, 必须指定 right_current 的值
      // right_pos: 每个手指的目标位置, 单位为rad
      //           范围分别为0-1.0297、0-1.5707、0-1.4137、0-1.4137、0-1.4137、0-1.4137
      // right_vel: 每个手指的目标速度, 单位为rad/s
      //           范围分别为0-2.5367、0-2.6180、0-2.2689、0-2.2689、0-2.2689、0-2.2689
      // right_current: 每个手指的目标电流, 单位为mA, 范围为±1000mA
      // right_time: 每个手指的控制时间, 单位为ms, 范围为1-2000ms

      "right_mode": 1,
      "right_pos": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_current": [500, 500, 500, 500, 500, 500],
      "right_time": [1000, 1000, 1000, 1000, 1000, 1000]
  }
}
```

响应：response_set_brainco2_hand_cmd

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_set_brainco2_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}
```

控制示例

```json

{
  "accid": "DACH_TRON2A_069",
  "title": "request_set_brainco2_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
          "left_mode": 1,
      "left_pos": [0.5, 0.5, 0.5, 1, 0.5, 0.5],
      "left_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "left_current": [500, 500, 500, 500, 500, 500],
      "left_time": [1000, 1000, 1000, 1000, 1000, 1000],

      "right_mode": 1,
      "right_pos": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_current": [500, 500, 500, 500, 500, 500],
      "right_time": [1000, 1000, 1000, 1000, 1000, 1000]
  }
}
```

### 4.2.2 获取灵巧手状态

请求：request_get_brainco2_hand_state

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "request_get_brainco2_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
```

响应：response_get_brainco2_hand_state

```json
{
  "accid": "DACH_TRON2A_001",
  "title": "response_get_brainco2_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989,
      "left_mode": 1,
      "left_pos": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "left_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "left_current": [500, 500, 500, 500, 500, 500],
      "left_time": [1000, 1000, 1000, 1000, 1000, 1000],
      "right_mode": 1,
      "right_pos": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_vel": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
      "right_current": [500, 500, 500, 500, 500, 500],
      "right_time": [1000, 1000, 1000, 1000, 1000, 1000]
  }
}
```

# 5 数字资产

## 5.1 机器人 urdf
https://github.com/limx-tron2/robot-description

# 6 常见问题

| **序号** | **问题**                           | 常见原因                   | 解决方式                                                                                                                                                                     |
| -------- | ---------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1        | 访问 `http://10.192.1.2:8080` 没反应 | 电脑不在 10.192.1.2 同网段 | 1. 先 ping10.192.1.2，通了清除下浏览器数据，或者换浏览器重新访问 2. 没 ping 通就修改本机地址，关闭 dhcp，DNS 为 [223.5.5.5](http://223.5.5.5) 或者 [8.8.8.8](http://8.8.8.8) |
| 2        | 如何配置相机 sn 号                 |                            | [相机 SN 配置方法](https://cwjgfm21di.feishu.cn/docx/CqXhdGp8doMZBgxGwercJfGGnXe?from=from_copylink)                                                                         |
| 3        |                                    |                            |                                                                                                                                                                              |
| 4        |                                    |                            |                                                                                                                                                                              |
| 5        |                                    |                            |                                                                                                                                                                              |

---

**深圳逐际动力科技有限公司**
**地址：** 深圳市南山区沙河西路 3157 号南山智谷产业园 E 栋 15 层
**网址：**[https://limxdynamics.com/](https://limxdynamics.com/)

![](https://oss.limxdynamics.com/uploads/material/%E9%BB%98%E8%AE%A4/TRON%202/pic/limx_company_logo.png)
