Limx Oli EDU SDK 开发指南

Oli EDU 版2026/3/20
文档版本 修订日期 修订内容 适用主控软件版本 (若不满足请前往官网下载中心获取最新版本主控软件包进行升级)
V1.0 2026.02.27 初版 V2.1.21 及以上
V1.1 2026.07.17 1. 删除 MCP Server
2. 新增关闭相机驱动自启动功能
V2.2.11 及以上
V1.2 2026.07.30 1. 补充获取相机数据步骤说明 V2.2.11 及以上

1 大模型调用接口

1.1 内置大模型

模型 备注
qwen2.5:3b 由阿里云推出的通义千问 2.5 系列模型,参数量为 30 亿。具备较强的语言理解和生成能力,适用于文本生成、对话交互等场景。
qwen2.5:1.5b 通义千问 2.5 系列中参数量为 15 亿的模型,相对轻量,在一些对计算资源要求不高的场景中也能有较好表现 。
qwen2.5:0.5b 参数量为 5 亿的轻量级模型,便于在终端设备或资源有限的环境下运行。
llama3.2:3b Meta 公司开发的大语言模型 LLaMA 3.2 版本中的 30 亿参数模型,在自然语言处理任务上表现出色,开源特性使得开发者可以基于它进行二次开发。
llama3.2:1b LLaMA 3.2 系列的 10 亿参数模型,模型规模较小,训练和推理速度相对较快。
deepseek-r1:1.5b DeepSeek 推出的模型,参数量 15 亿,在多种语言处理任务中具备一定的竞争力。

1.2 大模型调用

Oli 已通过 ollama 在本地完成上述大模型部署,只需将您的设备连接至与机器人相同的网络,即可按以下方法进行调用。

1.2.1 使用 Curl 调用

curl http://10.192.1.3:11434/api/generate \
  -H "Content-Type: application/json" \
  -d '{
        "model": "qwen2.5:3b",
        "prompt": "请写一首描述春天的四言绝句?",
        "temperature": 0.7,
        "max_tokens": 200,
        "stream": false
      }'

1.2.2 使用 Python 调用

import requests

url = "http://10.192.1.3:11434/api/generate"
data = {
    "model": "qwen2.5:3b",
    "prompt": "请写一首描述春天的四言绝句?",
    "temperature": 0.7,
    "max_tokens": 200,
    "stream": False
}

response = requests.post(url, json=data)
if response.status_code == 200:
    print(str(response.json()['response']))
else:
    print(f"Request failed with status code: {response.status_code}, Error: {response.text}")

1.2.3 使用 C++ 调用

以 Ubuntu 20.04 及以上系统版本为例:

  • 安装依赖
sudo apt-get install libcurl4-openssl-dev nlohmann-json3-dev 
  • 代码实现(llm_demo.cpp)
#include <iostream>
#include <string>
#include <curl/curl.h>      // HTTP client library
#include <nlohmann/json.hpp> // JSON parsing

using json = nlohmann::json;

// Callback to handle HTTP response data
static size_t WriteCallback(void* data, size_t size, size_t nmemb, std::string* buf) {
    buf->append((char*)data, size * nmemb);
    return size * nmemb;
}

int main() {
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cerr << "CURL init failed" << std::endl;
        return 1;
    }

    // 1. Configure API endpoint
    const std::string url = "http://10.192.1.3:11434/api/generate";
    
    // 2. Prepare JSON payload
    json req = {
        {"model", "qwen2.5:3b"},
        {"prompt", "请写一首描述春天的四言绝句?"},
        {"temperature", 0.7},
        {"max_tokens", 200},
        {"stream", false}
    };
    std::string payload = req.dump();

    // 3. Set CURL options
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload.size());

    // 4. Add HTTP headers
    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    // 5. Capture response
    std::string response;
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    // 6. Execute request
    CURLcode res = curl_easy_perform(curl);

    // 7. Process result
    if (res == CURLE_OK) {
        try {
            json resp_json = json::parse(response);
            std::cout << "Result: " << resp_json["response"] << std::endl;
        } catch (const json::exception& e) {
            std::cerr << "JSON error: " << e.what() << std::endl;
        }
    } else {
        std::cerr << "HTTP error: " << curl_easy_strerror(res) << std::endl;
    }

    // 8. Cleanup
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    return 0;
}
  • 编译并运行
# Compile with C++11 support
g++ -std=c++11 -o llm_demo llm_demo.cpp -lcurl

# Execute
./llm_demo

2 通讯架构图

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

中文版 英文版
图片 图片

3 上层应用协议接口

机器人通过 WebSocket 通信端口 5000 来接收用户端请求指令,例如让机器人站起、蹲下、行走等。

WebSocket 是一种实时通信协议,在机器人和用户端之间建立长连接,以便快速有效地传输控制信息和数据。如下图所示:

图片


3.1 坐标说明

  • 如无特别说明,双臂末端的位置、姿态,均基于机器人的 base 坐标系。
  • SN 开头为 HU 的人型机器人 base 坐标系的定义。
  • base 坐标系原点为 URDF 文件中定义的 base_link,坐标系标准为右手坐标系 / REP-103 坐标系。

3.2 通信协议格式

当机器人通过 WebSocket 接收客户端指令时,采用 JSON 数据协议进行信息传递。

3.2.1 请求数据

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

请求数据代码示例:

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

3.2.2 响应数据

响应数据格式包含字段 描述
accid 机器人唯一序列号,标识机器人的唯一身份。
title 指令名称,以“response_”为前缀。
timestamp 指令发出时间戳,单位为毫秒。
guid 与对应请求指令的 guid 值相同。
data 至少应该包含一个“result”子字段,用于存放请求指令的执行结果数据。如果有需要,还可以包含其他子字段,例如错误码、错误信息等用于描述操作结果的信息。

响应数据代码示例:

{
  "accid": "HU_D02_001",   # 机器人唯一序列号,标识机器人的唯一身份
  "title": "response_xxx",  # 指令名称,以“response_”为前缀
  "timestamp": 1672373633989, # 指令发出时间戳,单位为毫秒
  "guid": "746d937cd8094f6a98c9577aaf213d98", # 与对应请求指令的guid值相同
  "data": { # 存放响应指令的具体数据内容
    "result": "success"  # “result” 用于存放请求指令处理是否成功,它的值为:“success 或 fail_xxx”
  }
}

3.2.3 消息推送

机器人主动向客户端发送信息的过程。这些信息可以包括机器人的序列号、当前运行状态、执行的操作等数据。通过及时地向客户端发送这些信息,机器人可以帮助客户端更好地理解它的工作状态,从而更好地使用它提供的服务。

消息推送数据格式包含字段 描述
accid 机器人唯一序列号,标识机器人的唯一身份。
title 指令名称,以“notify_”为前缀。
timestamp 消息发出时间戳,单位为毫秒。
guid 消息的 guid 值,唯一标识这条消息。
data 存放消息数据内容。可以根据具体需求包含多个子字段,以存放请求指令所需的数据内容。

消息推送代码示例:

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

3.3 通信测试方法

Postman 是一个流行的 API 开发环境,可以用于测试 WebSocket 接口。

使用 Postman 测试 WebSocket 接口操作步骤:

  1. 安装 postman,下载地址:https://www.postman.com/downloads/?utm_source=postman-home
  2. 打开 Postman,并创建一个 WebSocket 的请求;
  3. 连接机器人无线网络 1. 机器人开机完成后,使用个人电脑连接机器人 Wi-Fi,名称格式通常为「HU_D02_xxx」
  4. 输入 Wi-Fi 密码:12345678
  5. 在请求的 URL 中输入 WebSocket 接口的地址,例如,“ws://10.192.1.2:5000”;
  6. 在“Message”中,输入要发送的指令请求;
  7. 单击“Send”按钮,发送请求指令;
  8. 发送指令后,可以从服务器接收响应消息。使用 Postman 的响应窗口查看服务器返回的数据,并检查是否符合预期结果。图片

3.4 基础功能协议接口

3.4.1 连接 Wi-Fi 热点

3.4.1.1 请求:request_connect_wifi

本协议用于向机器人路由器发起请求,指令路由器连接到指定 SSID 的 WiFi 热点并返回连接结果,支持机器人版本:2.1.3 及以上。

{
  "accid": "HU_D04_01_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.4.1.2 响应:response_connect_wifi

{
  "accid": "HU_D04_01_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.4.1.3 消息推送:无

3.4.2 查询 Wi-Fi 连接状态

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

3.4.2.1 请求:request_wifi_connection_status

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

3.4.2.2 响应:response_wifi_connection_status

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

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

3.4.2.3 消息推送:无

3.4.3 进入准备状态

机器人缓慢摆出准备姿势。

3.4.3.1 请求:request_prepare

控制机器人进入“站姿”,可接受速度指令控制行走。

{
  "accid": "HU_D04_01_001",
  "title": "request_prepare",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": { }
}

3.4.3.2 响应:response_prepare

{
  "accid": "HU_D04_01_001",
  "title": "response_prepare",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}

3.4.3.3 消息推送:无

3.4.4 控制机器人行走

3.4.4.1 进入行走模式

机器人进入行走模式,可以接收速度指令。

3.4.4.1.1 请求:request_set_walk_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.4.1.2 响应:response_set_walk_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.4.1.3 消息推送:无

3.4.4.2 控制机器人行走

在移动操作模式下,通过此协议控制机器人行走。请注意,在全身操作模式下,此协议接口无效。

3.4.4.2.1 请求:request_set_walk_vel
{
  "accid": "HU_D04_01_001",
  "title": "request_set_walk_vel",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.0,   #  前进后退速度比值,取值范围[-1, 1]
    "y": 0.0,   #  横向行走速度比值,取值范围[-1, 1]
    "yaw": 0.0  #  旋转角速度比值,取值范围[-1, 1]
  }
}
3.4.4.2.2 响应:response_set_walk_vel

指令执行失败时返回此消息,成功执行则无返回。

{
  "accid": "HU_D04_01_001",
  "title": "response_set_walk_vel",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "fail_motor"  # fail_imu: IMU 错误, fail_motor: 电机错误
  }
}
3.4.4.2.3 消息推送:无

3.4.5 进入阻尼模式

机器人所有电机停止主动运动,摆动时有明显阻尼感。

3.4.5.1 请求:request_damping

{
  "accid": "HU_D04_01_001",
  "title": "request_damping",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.5.2 响应:response_damping

{
  "accid": "HU_D04_01_001",
  "title": "response_damping",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
  }
}

3.4.5.3 消息推送:无

3.4.6 进入零力矩模式

机器人所有电机停止主动运动,摆动时没有阻尼感。

3.4.6.1 请求:request_zero_torque

{
  "accid": "HU_D04_01_001",
  "title": "request_zero_torque",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.6.2 响应:response_zero_torque

{
  "accid": "HU_D04_01_001",
  "title": "response_zero_torque",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
  }
}

3.4.6.3 消息推送:无

3.4.7 进入坐下指令

3.4.7.1 请求:request_from_stand_to_sit

{
  "accid": "HU_D04_01_001",
  "title": "request_from_stand_to_sit",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.7.2 响应:response_from_stand_to_sit

{
  "accid": "HU_D04_01_001",
  "title": "response_from_stand_to_sit",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
  }
}

3.4.7.3 消息推送:无

3.4.8 进入站立指令

提示:

接口功能:启动机器人使用,机器人开机后调用该接口进入站立状态。
参数 mode:[lying:机器人躺着 hanging:机器人吊着 sit: 机器人坐着]
返回:已站起后返回

3.4.8.1 请求:request_standup

{
  "accid": "HU_D04_01_001",
  "title": "request_standup",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": "lying" // "lying"/"sitting":机器人当前躺着/坐着  or 
                      // "hanging":机器人当前状态吊着
                      // 若没有"mode" 字段 默认机器人状态是"sitting"坐着
  }
}

3.4.8.2 响应:response_standup

{
  "accid": "HU_D04_01_001",
  "title": "response_standup",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
                          # fail_invalid_cmd:参数错误
                          # fail_invalid_mode:机器人状态错误
                          # fail_timeout:执行超时错误
  }
}

3.4.8.3 消息推送:无

3.4.9 进入躺着指令

3.4.9.1 请求:request_lie_down

Walk 状态下可以调用该接口

{
  "accid": "HU_D04_01_001",
  "title": "request_lie_down",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.9.2 响应:response_lie_down

{
  "accid": "HU_D04_01_001",
  "title": "response_lie_down",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
  }
}

3.4.9.3 消息推送:无

3.4.10 机器人校零指令

3.4.10.1 请求:request_calibrate

{
  "accid": "HU_D04_01_001",
  "title": "request_calibrate",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.10.2 响应:response_calibrate

{
  "accid": "HU_D04_01_001",
  "title": "response_calibrate",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: 电机错误
  }
}

3.4.10.3 消息推送:notify_calibrate

校零完成后,推送此消息。

{
  "accid": "HU_D04_01_001",
  "title": "notify_calibrate",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"
  }
}

3.4.11 机器人舞蹈

3.4.11.1 切换机器人到舞蹈模式

3.4.11.1.1 请求:request_enter_dance_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_enter_dance_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 0:退出舞蹈模式
    # 1:进入舞蹈模式 
    "mode": 0
  }
}
3.4.11.1.2 响应:response_enter_dance_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_enter_dance_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.11.1.3 消息推送:无

3.4.11.2 获取舞蹈列表

3.4.11.2.1 请求:request_get_dance_list
{
  "accid": "HU_D04_01_001",
  "title": "request_get_dance_list",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.11.2.2 响应:response_get_dance_list
{
    "accid": "HU_D04_01_001",
    "title": "response_get_dance_list",
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "timestamp": 1672373633989,
    "data": {
        "result": "success",
        "code": 0,
        "dances": [
            {
                "id": "DAN-14",
                "index": 0,
                "name": "\u70ed\u70c8",
                "english_name": "One and Only Dance",
                "rc_mapping": "one_and_only_dance"
            },
            {
                "id": "DAN-08",
                "index": 1,
                "name": "\u4f4e\u4fd7\u5c0f\u8bf4",
                "english_name": "Pulp Fiction Dance",
                "rc_mapping": "pulp_fiction_dance"
            }
        ]
    }
}

3.4.11.3 机器人跳舞

3.4.11.3.1 请求:request_dance

提示:

  1. 执行前置条件:当前处于动作库模式
  2. 适用于主控 V2.1.21 及以上版本
{
  "accid": "HU_D04_01_001",
  "title": "request_dance",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "name": "one_and_only_dance"  # rc_mapping 字段的舞蹈名称
  }
}
3.4.11.3.2 响应:response_dance
{
  "accid": "HU_D04_01_001",
  "title": "response_dance",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.11.3.3 消息推送:notify_dance

跳完舞蹈或执行过程中失败推送此消息。

{
  "accid": "HU_D04_01_001",
  "title": "notify_dance",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}

3.4.12 机器人原地踏步

3.4.12.1 开启原地踏步

3.4.12.1.1 请求:request_start_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "request_start_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.12.1.2 响应:response_start_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "response_start_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.12.1.3 消息推送:无

3.4.12.2 停止原地踏步

3.4.12.2.1 请求:request_stop_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "request_stop_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.12.2.2 响应:response_stop_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "response_stop_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}

3.4.13 机器人动作库

3.4.13.1 动作打断

3.4.13.1.1 请求:request_interrupt_action_joystick
{
  "accid": "HU_D04_01_001",
  "title": "request_interrupt_action_joystick",
  "timestamp": 1779355330784,
  "guid": "32cef03a-5563-4b21-9bbb-3e65a8c9ae9e",
  "data": {}
}
3.4.13.1.2 响应:response_interrupt_action_joystick
{
  "accid": "HU_D04_01_001",
  "title": "response_interrupt_action_joystick",
  "guid": "32cef03a-5563-4b21-9bbb-3e65a8c9ae9e",
  "timestamp": 1779355330784,
  "data": {
    "result": "success"
  }
}

3.4.13.2 获取动作库状态

接口说明:

  1. 机器人进入动作库之后,处于动作库/原子执行/舞蹈中将显示:"action_library_mode": "action_library"
  2. 当机器人在执行原子动作中或者舞蹈中将显示: "action_library_state": "running"
3.4.13.2.1 请求:request_get_action_library_status
{
  "accid": "HU_D04_01_001",
  "title": "request_get_action_library_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.13.2.2 响应:response_get_action_library_status
{
  "accid": "HU_D04_01_001",
  "title": "get_action_library_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "action_library_mode": "action_library" //"remote_control"
      "action_library_state": "running"       //"idle"
      "result": "success" # fail_motor
  }
}

3.4.13.3 执行动作库

接口说明:

  1. 不在 Menu 会自动进入 Menu,动作执行完会保持 Menu;
  2. 需配合 request_get_action_library_statusaction_library_state 使用。
3.4.13.3.1 请求:request_action_sync_stay_menu
{
  "accid": "HU_D04_01_001",
  "title": "request_action_sync_stay_menu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "name": "one_and_only_dance"
  }
}
3.4.13.3.2 响应:response_action_sync_stay_menu
{
  "accid": "HU_D04_01_001",
  "title": "response_action_sync_stay_menu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success" # fail_motor
  }
}

3.4.13.4 执行动作库(同步接口)

接口说明:

  1. 当机器人处于不可执行动作库时,在 100ms 内返回失败响应;
  2. 当机器人处于可执行动作库时(walk/motion library),在执行动作库之后返回响应。
  3. 同步接口,会自动回到Walk,结束状态通过判断是否再Walk结束。
3.4.13.4.1 请求:request_action_sync
{
  "accid": "HU_D04_01_001",
  "title": "request_action_sync",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "name": "one_and_only_dance,this_way_please"  
                                    # name:多个舞蹈/多个动作/舞蹈与动作混合,用“,”隔开
  }
}
3.4.13.4.2 响应:response_action_sync
{
  "accid": "HU_D04_01_001",
  "title": "response_action_sync",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}

3.4.13.5 切换机器人到动作库模式

3.4.13.5.1 请求:request_set_motion_engine
{
  "accid": "HU_D04_01_001",
  "title": "request_set_motion_engine",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 0:退出动作库模式
    # 1:进入动作库模式 
    "mode": 0
  }
}
3.4.13.5.2 响应:response_set_motion_engine
{
  "accid": "HU_D04_01_001",
  "title": "response_set_motion_engine",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.13.5.3 消息推送:无

3.4.13.6 获取动作库列表

3.4.13.6.1 请求:request_get_atomic_motion_list
{
  "accid": "HU_D04_01_001",
  "title": "request_get_atomic_motion_list",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.13.6.2 响应:response_get_atomic_motion_list
{
    "accid": "HU_D04_01_001",
    "title": "response_get_atomic_motion_list",
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "timestamp": 287883835,
    "data": {
        "result": "success",
        "motion_list": [
            {
                "motion_index": 0,
                "motion_name_cn": "静止站立",
                "motion_name_en": "stand"
            },
            {
                "motion_index": 1,
                "motion_name_cn": "指引请这边走",
                "motion_name_en": "this_way_please"
            }
            ......
        ],
        "count": 2
    }
}

3.4.13.7 执行机器人动作库动作

设置机器人为动作库模式后,可以执行机器人动作库中的动作。

3.4.13.7.1 请求:request_execute_atomic_motion
{
  "accid": "HU_D04_01_001",
  "title": "request_execute_atomic_motion",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 动作名称
    "motion_name": "wave_greet_bye"
  }
}
3.4.13.7.2 响应:response_execute_atomic_motion
{
  "accid": "HU_D04_01_001",
  "title": "response_execute_atomic_motion",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.13.7.3 消息推送:notify_execute_atomic_motion

动作执行完成或执行过程中失败推送此消息。

{
  "accid": "HU_D04_01_001",
  "title": "notify_execute_atomic_motion",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}

3.4.14 机器人移动操作

3.4.14.1 移动操作模式切换

在移动操作模式下您还可以控制机器人行走,但不能控制机器人的身高及腰部运动。

图片

3.4.14.1.1 请求:request_set_ub_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_ub_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": 0  # 0: 准备进入模式 1: 操作模式,开始跟踪末端位置 2: 准备退出模式
  }
}
3.4.14.1.2 响应:response_set_ub_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_ub_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.14.1.3 消息推送:无

3.4.14.2 移动操作控制

需要通过协议接口 request_set_ub_manip_mode,功能才生效。

  • 参考坐标系示意图:
    • 位置 —— base_link的原点(髋部下方正中间)
    • 坐标轴定义:红色为x方向:机器人前进方向;绿色为y方向:正方向向左;蓝色为z方向:竖直朝上

| 图片 | 图片 |

3.4.14.2.1 请求:request_set_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_set_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 参考坐标系定义:
      # 原点:base_link坐标系
      # 方向:x方向对齐机器人正前方,y方向朝向机器人左侧,z方向竖直向上
      #
      # 参数定义:
      # 头相对于参考坐标系的姿态,四元数[x,y,z,w]
      "head_quat": [0.0, 0.0, 0.0, 1.0],
    
      # 左手相对于参考坐标系的位置,单位为米
      "left_hand_pos": [0.0, 0.0, 0.0],
      
      # 左手相对于参考坐标系的姿态,四元数[x,y,z,w]
      "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
      # 右手相对于参考坐标系的位置,单位为米
      "right_hand_pos": [0.0, 0.0, 0.0],
      
      # 右手相对于参考坐标系的姿态,四元数[x,y,z,w]
      "right_hand_quat": [0.0, 0.0, 0.0, 1.0]
  }
}
3.4.14.2.2 响应:response_set_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_set_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}
3.4.14.2.3 消息推送:无

3.4.14.3 获取移动操作位姿信息

3.4.14.3.1 请求:request_get_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_get_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.4.14.3.2 响应:response_get_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_get_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 参考坐标系定义:
    # 原点:base_link坐标系
    # 方向:x方向对齐机器人正前方,y方向朝向机器人左侧,z方向竖直向上
    #
    # 参数定义:
    # 头相对于参考坐标系的位置,单位为米
    "head_pos": [0.0, 0.0, 0.0],
      
    # 头相对于参考坐标系的姿态,四元数[x,y,z,w]
    "head_quat": [0.0, 0.0, 0.0, 1.0],
    
    # 左手相对于参考坐标系的位置,单位为米
    "left_hand_pos": [0.0, 0.0, 0.0],
      
    # 左手相对于参考坐标系的姿态,四元数[x,y,z,w]
    "left_hand_quat": [0.0, 0.0, 0.0,1.0],
      
    # 右手相对于参考坐标系的位置,单位为米
    "right_hand_pos": [0.0, 0.0, 0.0],
      
    # 右手相对于参考坐标系的姿态,四元数[x,y,z,w]
    "right_hand_quat": [0.0, 0.0, 0.0,1.0],
    "result": "success"  # success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}

3.4.15 机器人原地操作

3.4.15.1 进入原地操作模式

原地操作模式下,您可控制机器人的身体运动,但无法操控其行走功能。

3.4.15.1.1 请求:request_set_wb_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_wb_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": 0  # 0: 准备进入模式 1: 操作模式,开始跟踪末端位置 2: 准备退出模式
  }
}
3.4.15.1.2 响应:response_set_wb_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_wb_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.15.1.3 消息推送:无

3.4.15.2 原地操作控制

需要通过协议接口 request_set_wb_manip_mode,mode 为 1 进入原地操作模式下,功能才生效。

  • 参考坐标系示意图:
    • 位置 —— left_ankle_roll_link 与 right_ankle_roll_link 原点连线的中点
    • 姿态 —— yaw方向为 left_ankle_roll_link 与 right_ankle_roll_link 转向的中间值
    • 坐标轴定义:红色为x方向:由机器人双脚朝向决定;绿色为y方向:可根据右手坐标系规则确定;蓝色为z方向:竖直朝上。
图片 图片
3.4.15.2.1 请求:request_set_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_set_wb_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 参考坐标系定义:
      # 原点:为左脚以及右脚的中心位置在地面上的投影(z=0)
      # 方向:x方向对齐机器人正前方,y方向朝向机器人左侧,z方向竖直向上
      #
      # 参数定义:
      # 左手相对于参考坐标系的位置,单位为米
      "left_hand_pos": [0.0, 0.3, 0.8],
      
      # 左手相对于参考坐标系的姿态,四元数[x,y,z,w]
      "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
      # 右手相对于参考坐标系的位置,单位为米
      "right_hand_pos": [0.0, -0.3, 0.8],
      
      # 右手相对于参考坐标系的姿态,四元数[x,y,z,w]
      "right_hand_quat": [0.0, 0.0, 0.0, 1.0]
  }
}
3.4.15.2.2 响应:response_set_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_set_wb_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}
3.4.15.2.3 消息推送:无

3.4.15.3 获取原地操作位姿信息

3.4.15.3.1 请求:request_get_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_get_wb_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.4.15.3.2 响应:response_get_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_get_wb_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 参考坐标系定义:
    # 原点:为左脚以及右脚的中心位置在地面上的投影(z=0)
    # 方向:x方向对齐机器人正前方,y方向朝向机器人左侧,z方向竖直向上
    #
    # 参数定义:
    # 左手相对于参考坐标系的位置,单位为米
    "left_hand_pos": [0.0, 0.0, 0.0],
      
    # 左手相对于参考坐标系的姿态,四元数[x,y,z,w]
    "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
    # 右手相对于参考坐标系的位置,单位为米
    "right_hand_pos": [0.0, 0.0, 0.0],
      
    # 右手相对于参考坐标系的姿态,四元数[x,y,z,w]
    "right_hand_quat": [0.0, 0.0, 0.0, 1.0],
    "result": "success"  # success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}
3.4.15.3.3 消息推送:无

3.4.16 双臂协同 Move 控制

3.4.16.1 切换 Move 控制模式

3.4.16.1.1 请求:request_set_move_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_move_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 0: 退出Move控制
      # 1: 移动Move模式
      # 2: 原地Move模式
      "mode": 0 
  }
}
3.4.16.1.2 响应:response_set_move_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_move_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.16.1.3 消息推送:无

3.4.16.2 MoveJ 控制指令

3.4.16.2.1 请求:request_moveJ
{
  "accid": "HU_D04_01_001",
  "title": "request_moveJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 各关节位置范围根据对应型号机器人URDF获取
      # 模型文件下载地址:https://github.com/limxdynamics/humanoid-description
      
      # 如您同时给出以下数据,则会控制双臂运动(目标位置,单位弧度)
      # 左臂关节顺序:  
      # - "left_shoulder_pitch_joint"
      # - "left_shoulder_roll_joint"
      # - "left_shoulder_yaw_joint"
      # - "left_elbow_joint"
      # - "left_wrist_yaw_joint"
      # - "left_wrist_pitch_joint"
      # - "left_wrist_roll_joint"
      "left": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      
      # 右臂关节顺序:  
      # - "right_shoulder_pitch_joint"
      # - "right_shoulder_roll_joint"
      # - "right_shoulder_yaw_joint"
      # - "right_elbow_joint"
      # - "right_wrist_yaw_joint"
      # - "right_wrist_pitch_joint"
      # - "right_wrist_roll_joint"
      "right": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      
      # 仅在原地MoveJ可控,如您同时给出以下数据,则会控制躯干姿态
      "torso_height": 0,  # 调整身高比例值,取值范围[-1, 1]
      "torso_pitch": 0,   # Pitch方向运动比例值,取值范围[-1, 1]
      "torso_roll": 0,    # Roll方向运动比例值,取值范围[-1, 1]
      "torso_yaw": 0,     # Yaw方向运动比例值,取值范围[-1, 1]
      
      # 如您同时给出以下数据,则会控制头运动
      "head_pitch": 0.0,  # pitch 关节的目标位置,单位为弧度
      "head_yaw": 0.0,    # yaw 关节的目标位置,单位为弧度
      
      "speed": 0.2  # 运动速度,取值范围为 0 到 0.5 弧度 / 秒,控制双臂运动的快慢
  }
}
3.4.16.2.2 响应:response_moveJ
{
  "accid": "HU_D04_01_001",
  "title": "response_moveJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.16.2.3 消息推送:notify_moveJ

执行完成或失败,主动推送此消息。

{
  "accid": "HU_D04_01_001",
  "title": "notify_moveJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 执行完成, fail_motor: 电机错误, fail_invalid_speed: 非法速度值
  }
}

3.4.16.3 MoveP 控制指令

  • 参考坐标系示意图:
    • 位置 —— waist_pitch_link 的原点
    • 坐标轴定义:红色为x方向:机器人前进方向;绿色为y方向:正方向向左;蓝色为z方向:竖直朝上
图片 图片
3.4.16.3.1 请求:request_moveP
{
  "accid": "HU_D04_01_001",
  "title": "request_moveP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 如您同时给出以下数据,则会控制双臂运动
      "left_position": [0.0, 0.0, 0.0], # 表示左臂末端要移动到的目标位置,单位为米,顺序为 x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # 用四元数(x, y, z, w)表示左臂末端的目标姿态
      "right_position": [0.0, 0.0, 0.0], # 表示右臂末端要移动到的目标位置,单位为米,顺序为 x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # 用四元数(x, y, z, w)表示右臂末端的目标姿态
      
      # 仅在原地MoveP可控,如您同时给出以下数据,则会控制躯干姿态
      "torso_height": 0,  # 调整身高比例值,取值范围[-1, 1]
      "torso_pitch": 0,   # Pitch方向运动比例值,取值范围[-1, 1]
      "torso_roll": 0,    # Roll方向运动比例值,取值范围[-1, 1]
      "torso_yaw": 0,     # Yaw方向运动比例值,取值范围[-1, 1]
      
      # 如您同时给出以下数据,则会控制头运动
      "head_pitch": 0.0,  # pitch 关节的目标位置,单位为弧度
      "head_yaw": 0.0,    # yaw 关节的目标位置,单位为弧度
      
      "speed": 0.2  # 运动速度,取值范围为 0 到 0.5 弧度 / 秒,控制双臂运动的快慢
  }
}
3.4.16.3.2 响应:response_moveP
{
  "accid": "HU_D04_01_001",
  "title": "response_moveP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.16.3.3 消息推送:notify_moveP

执行完成或失败,主动推送此消息。

{
  "accid": "HU_D04_01_001",
  "title": "notify_moveP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 执行完成, fail_motor: 电机错误, fail_invalid_speed: 非法速度值
  }
}

3.4.16.4 获取双臂末端位姿

3.4.16.4.1 请求:request_get_move_pose

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

{
  "accid": "HU_D04_01_001",
  "title": "request_get_move_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.16.4.2 响应:response_get_move_pose

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

{
  "accid": "HU_D04_01_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": [0.0, 0.0, 0.0, 1.0], # 表示左臂末端的姿态,以四元数表示,顺序为 x, y, z, w
      "right_position": [0.0, 0.0, 0.0], # 表示右臂末端的位置,单位为米,顺序为 x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # 表示右臂末端的姿态,以四元数表示,顺序为 x, y, z, w
      "result": "success"  # fail_not_data
  }
}

3.4.17 双臂协同 Servo 控制

3.4.17.1 切换 Servo 控制模式

3.4.17.1.1 请求:request_set_servo_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_servo_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 0: 退出Servo控制
      # 1: 移动Servo模式
      # 2: 原地Servo模式
      "mode": 0
  }
}
3.4.17.1.2 响应:response_set_servo_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_servo_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.4.17.1.3 消息推送:无

3.4.17.2 ServoJ 控制指令

3.4.17.2.1 请求:request_servoJ
  • 推荐在实时系统中按控制频率 >= 500Hz 要求来控制机械臂运动,以保证控制效果和稳定性。
{
  "accid": "HU_D04_01_001",
  "title": "request_servoJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 各关节位置范围根据对应型号机器人URDF获取
      # 模型文件下载地址:https://github.com/limxdynamics/humanoid-description
      
      # 如您同时给出以下数据,则会控制双臂运动(目标位置,单位弧度)
      # 左臂关节顺序:  
      # - "left_shoulder_pitch_joint"
      # - "left_shoulder_roll_joint"
      # - "left_shoulder_yaw_joint"
      # - "left_elbow_joint"
      # - "left_wrist_yaw_joint"
      # - "left_wrist_pitch_joint"
      # - "left_wrist_roll_joint"
      "left": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      
      # 右臂关节顺序:  
      # - "right_shoulder_pitch_joint"
      # - "right_shoulder_roll_joint"
      # - "right_shoulder_yaw_joint"
      # - "right_elbow_joint"
      # - "right_wrist_yaw_joint"
      # - "right_wrist_pitch_joint"
      # - "right_wrist_roll_joint"
      "right": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      
      # 仅在原地ServoJ可控,如您同时给出以下数据,则会控制躯干姿态
      "torso_height": 0,  # 调整身高比例值,取值范围[-1, 1]
      "torso_pitch": 0,   # Pitch方向运动比例值,取值范围[-1, 1]
      "torso_roll": 0,    # Roll方向运动比例值,取值范围[-1, 1]
      "torso_yaw": 0,     # Yaw方向运动比例值,取值范围[-1, 1]
      
      # 如您同时给出以下数据,则会控制头运动
      "head_yaw": 0.0,    # yaw 关节的目标位置,单位为弧度
      "head_pitch": 0.0  # picth 关节的目标位置,单位为弧度
  }
}
3.4.17.2.2 响应:无
3.4.17.2.3 消息推送:notify_servoJ

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

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

3.4.17.3 ServoP 控制指令

  • 参考坐标系示意图:
    • 位置 —— waist_pitch_link 的原点
    • 坐标轴定义:红色为x方向:机器人前进方向;绿色为y方向:正方向向左;蓝色为z方向:竖直朝上。
图片 图片
3.4.17.3.1 请求:request_servoP
  • 推荐在实时系统中按控制频率 >= 500Hz 要求来控制机械臂运动,以保证控制效果和稳定性。
{
  "accid": "HU_D04_01_001",
  "title": "request_servoP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 如您同时给出以下数据,则会控制双臂运动
      "left_position": [0.0, 0.0, 0.0], # 表示左臂末端的目标位置,单位为米,顺序为 x,y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # 以四元数(x, y, z, w)形式表示左臂末端的目标姿态
      "right_position": [0.0, 0.0, 0.0], # 表示右臂末端的目标位置,单位为米,顺序为 x,y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0] # 以四元数(x, y, z, w)形式表示右臂末端的目标姿态
      
      # 仅在原地ServoP可控,如您同时给出以下数据,则会控制躯干姿态
      "torso_height": 0,  # 调整身高比例值,取值范围[-1, 1]
      "torso_pitch": 0,   # Pitch方向运动比例值,取值范围[-1, 1]
      "torso_roll": 0,    # Roll方向运动比例值,取值范围[-1, 1]
      "torso_yaw": 0,     # Yaw方向运动比例值,取值范围[-1, 1]
      
      # 如您同时给出以下数据,则会控制头运动
      "head_yaw": 0.0,    # yaw 关节的目标位置,单位为弧度
      "head_pitch": 0.0  # picth 关节的目标位置,单位为弧度
  }
}
3.4.17.3.2 响应:无
3.4.17.3.3 消息推送:notify_servoP

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

{
  "accid": "HU_D04_01_001",
  "title": "notify_servoP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "fail_invalid_cmd"  # fail_invalid_cmd: 非法指令, fail_motor: 电机错误
  }
}

3.4.17.4 获取双臂末端位姿

3.4.17.4.1 请求:request_get_servo_pose

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

{
  "accid": "HU_D04_01_001",
  "title": "request_get_servo_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.17.4.2 响应:response_get_servo_pose

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

{
  "accid": "HU_D04_01_001",
  "title": "response_get_servo_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # 表示数据时戳,单位为毫秒
      "left_position": [0.0, 0.0, 0.0], # 表示左臂末端的位置,单位为米,顺序为 x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # 表示左臂末端的姿态,以四元数表示,顺序为 x, y, z, w
      "right_position": [0.0, 0.0, 0.0], # 表示右臂末端的位置,单位为米,顺序为 x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # 表示右臂末端的姿态,以四元数表示,顺序为 x, y, z, w
      "result": "success"  # fail_not_data
  }
}
3.4.17.4.3 消息推送:无

3.4.18 机器人关节状态

3.4.18.1 请求:request_get_joint_state

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

{
  "accid": "HU_D04_01_001",
  "title": "request_get_joint_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.18.2 响应:response_get_joint_state

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

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

3.4.18.3 消息推送:无

3.4.19 获取 IMU 数据

3.4.19.1 请求:request_get_imu_data

{
  "accid": "HU_D04_01_001",
  "title": "request_get_imu_data",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}

3.4.19.2 响应:response_get_imu_data

{
  "accid": "HU_D04_01_001",
  "title": "response_get_imu_data",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success", # fail_no_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.4.19.3 消息推送:无

3.5 灵巧手及夹爪协议接口

3.5.1 逐际二指夹爪

3.5.1.1 夹爪控制指令

3.5.1.1.1 请求:request_set_limx_2fclaw_cmd

此协议控制夹爪的抓取动作。

{
  "accid": "HU_D04_01_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 无单位(数值越大力越大)
  }
}
3.5.1.1.2 响应:response_set_limx_2fclaw_cmd
{
  "accid": "HU_D04_01_001",
  "title": "response_set_limx_2fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.5.1.1.3 消息推送:无

3.5.1.2 获取夹爪状态信息

3.5.1.2.1 请求:request_get_limx_2fclaw_state
{
  "accid": "HU_D04_01_001",
  "title": "request_get_limx_2fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.5.1.2.2 响应:response_get_limx_2fclaw_state

返回夹爪状态信息

3.5.1.2.3 消息推送:无

3.5.2 逐际三指夹爪

3.5.2.1 夹爪控制指令

3.5.2.1.1 请求:request_set_limx_3fclaw_cmd

此协议控制夹爪的抓取动作。

3.5.2.1.2 响应:response_set_limx_3fclaw_cmd
{
  "accid": "HU_D04_01_001",
  "title": "response_set_limx_3fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.5.2.1.3 消息推送:无

3.5.2.2 获取夹爪状态信息

3.5.2.2.1 请求:request_get_limx_3fclaw_state
{
  "accid": "HU_D04_01_001",
  "title": "request_get_limx_3fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.5.2.2.2 响应:response_get_limx_3fclaw_state

返回夹爪状态信息

3.5.2.2.3 消息推送:无

3.5.3 因时二指夹爪

3.5.3.1 夹爪控制指令

3.5.3.1.1 请求:request_set_claw_cmd

此协议控制夹爪的抓取动作。

3.5.3.1.2 响应:response_set_claw_cmd
{
  "accid": "HU_D04_01_001",
  "title": "response_set_claw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.5.3.1.3 消息推送:无

3.5.3.2 获取夹爪状态信息

3.5.3.2.1 请求:request_get_claw_state
{
  "accid": "HU_D04_01_001",
  "title": "request_get_claw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.5.3.2.2 响应:response_get_claw_state

返回夹爪状态信息

3.5.3.2.3 消息推送:无

3.5.4 强脑 1 代灵巧手

3.5.4.1 灵巧手控制指令

3.5.4.1.1 请求:request_set_brainco_hand_cmd
{
  "accid": "HU_D04_01_001",
  "title": "request_set_brainco_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 如您给定以下数据,则会控制左手运动
      "left_thumb": 50,       # 左手大拇指弯曲角度,0-100,无量纲
      "left_thumb_aux": 50,   # 左手大拇指内收角度,0-100,无量纲
      "left_index": 50,       # 左手食指弯曲角度,0-100,无量纲
      "left_middle": 50,      # 左手中指弯曲角度,0-100,无量纲
      "left_ring": 50,        # 左手无名指弯曲角度,0-100,无量纲
      "left_pinky": 50,       # 左手小指弯曲角度,0-100,无量纲
      "left_mode": 3,         # 力量等级 1:小 2:中 3:大。默认值为:2
      
      # 如您给定以下数据,则会控制右手运动
      "right_thumb": 50,       # 右手大拇指弯曲角度,0-100,无量纲
      "right_thumb_aux": 50,   # 右手大拇指内收角度,0-100,无量纲
      "right_index": 50,       # 右手食指弯曲角度,0-100,无量纲
      "right_middle": 50,      # 右手中指弯曲角度,0-100,无量纲
      "right_ring": 50,        # 右手无名指弯曲角度,0-100,无量纲
      "right_pinky": 50,       # 右手小指弯曲角度,0-100,无量纲
      "right_mode": 3          # 力量等级 1:小 2:中 3:大。默认值为:2
  }
}
3.5.4.1.2 响应:response_set_brainco_hand_cmd
{
  "accid": "HU_D04_01_001",
  "title": "response_set_brainco_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.5.4.1.3 消息推送:无

3.5.4.2 获取灵巧手状态

3.5.4.2.1 请求:request_get_brainco_hand_state
{
  "accid": "HU_D04_01_001",
  "title": "request_get_brainco_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.5.4.2.2 响应:response_get_brainco_hand_state
{
  "accid": "HU_D04_01_001",
  "title": "response_get_brainco_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # 表示数据时戳,单位为毫秒
      "left_thumb": 50,       # 左手大拇指弯曲角度,0-100,无量纲
      "left_thumb_aux": 50,   # 左手大拇指内收角度,0-100,无量纲
      "left_index": 50,       # 左手食指弯曲角度,0-100,无量纲
      "left_middle": 50,      # 左手中指弯曲角度,0-100,无量纲
      "left_ring": 50,        # 左手无名指弯曲角度,0-100,无量纲
      "left_pinky": 50,       # 左手小指弯曲角度,0-100,无量纲
      
      "right_thumb": 50,       # 右手大拇指弯曲角度,0-100,无量纲
      "right_thumb_aux": 50,   # 右手大拇指内收角度,0-100,无量纲
      "right_index": 50,       # 右手食指弯曲角度,0-100,无量纲
      "right_middle": 50,      # 右手中指弯曲角度,0-100,无量纲
      "right_ring": 50,        # 右手无名指弯曲角度,0-100,无量纲
      "right_pinky": 50        # 右手小指弯曲角度,0-100,无量纲
      "result": "success"  # success: 成功, fail_motor: 电机错误
  }
}
3.5.4.2.3 消息推送:无

3.5.5 强脑 2 代灵巧手

3.5.5.1 灵巧手控制指令

3.5.5.1.1 请求:request_set_brainco2_hand_cmd
{
  "accid": "HU_D04_01_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.02970-1.57070-1.41370-1.41370-1.41370-1.4137
      # left_vel: 每个手指的目标速度, 单位为rad/s
      #           范围分别为0-2.53670-2.61800-2.26890-2.26890-2.26890-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.02970-1.57070-1.41370-1.41370-1.41370-1.4137
      # right_vel: 每个手指的目标速度, 单位为rad/s
      #           范围分别为0-2.53670-2.61800-2.26890-2.26890-2.26890-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]
  }
}
3.5.5.1.2 响应:response_set_brainco2_hand_cmd
{
  "accid": "HU_D04_01_001",
  "title": "response_set_brainco2_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success: 成功, fail_motor: 电机错误, fail_invalid_cmd: 非法指令
  }
}
3.5.5.1.3 消息推送:无

3.5.5.2 获取灵巧手状态

3.5.5.2.1 请求:request_get_brainco2_hand_state
{
  "accid": "HU_D04_01_001",
  "title": "request_get_brainco2_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
3.5.5.2.2 响应:response_get_brainco2_hand_state
{
  "accid": "HU_D04_01_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]
  }
}
3.5.5.2.3 消息推送:无

3.6 全局消息协议接口

3.6.1 机器人状态信息

此协议将定时上报机器人状态信息。

上报状态信息 描述
accid 机器人序列号
title notify_robot_info
timestamp 消息发出时间戳,单位为毫秒
guid 消息的 guid 值,唯一标识这条消息
data 存放消息内容

示例:

{
  "accid": "HU_D04_01_001", 
  "title": "notify_robot_info", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": []
  }
}

3.6.1.1 电池数据

{
  "accid": "HU_D04_01_001", 
  "title": "notify_robot_info", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": [
        ......
        {
                "level": 0,
                "name": "peripheral",
                "message": "OK",
                "hardware_id": "peripheral",
                "values": [
                    {
                        "key": "bmsconn",
                        "value": "ON"
                    },
                    {
                        "key": "bat_chg",
                        "value": "OFF"
                    },
                    {
                        "key": "bat_off",
                        "value": "OFF"
                    },
                    {
                        "key": "bat_prt",
                        "value": "0"
                    },
                    {
                        "key": "bat_vol",
                        "value": "48830"
                    },
                    {
                        "key": "bat_cur",
                        "value": "2870"
                    },
                    {
                        "key": "battery",
                        "value": "29"
                    },
                    {
                        "key": "bat_temp0",
                        "value": "430"
                    },
                    {
                        "key": "bat_temp2",
                        "value": "430"
                    },
                    {
                        "key": "bat_temp4",
                        "value": "400"
                    },
                    {
                        "key": "battery_capacity",
                        "value": "9000mAh"
                    }
                ]
            },
    ]
  }
}
字段 含义
bmsconn 电池连接状态 :【OFF:未连接,ON:已连接】
bat_chg 电池充电器状态 :【OFF:未连接,ON:已连接】
bat_off 电池预关机状态 :【OFF:1s 后断电,ON:正常】
bat_prt 电池故障码:【 0:正常, 非 0:异常】
bat_vol 电池实时电压 单位:mV
bat_cur 电池实时电流 单位:mA
battery 电池电量百分比 0~100
bat_temp0 电池温度 0~100 单位:x10℃
bat_temp2 电池温度 0~100 单位:x10℃
bat_temp4 电池温度 0~100 单位:x10℃

3.6.1.2 系统信息

{
  "accid": "HU_D04_01_001", 
  "title": "notify_robot_info", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": [
          {
              "level": 0,
              "name": "system_info",
              "message": "system info",
              "hardware_id": "system_info",
              "values": [
                {
                  "key": "ability_running",
                  "value": "ZeroTorque"
                },
                {
                  "key": "ecm_version",
                  "value": "1.1.2"
                },
                {
                  "key": "mode",
                  "value": "Remote"
                },
                {
                  "key": "motor_version",
                  "value": "1: 0.0.9; 2: 0.0.9; 3: 0.0.9; 4: 0.0.9; 5: 0.0.9; 6: 0.0.9; 7: 0.0.9; 8: 0.0.9; 9: 0.0.9; 10: 0.0.9; 11: 0.0.9; 12: 0.0.9; 13: 0.0.9; 14: 0.0.9; 15: 0.0.9; 16: 0.0.9; "
                },
                {
                  "key": "pms_version",
                  "value": "2.1.8"
                },
                {
                  "key": "robot_status",
                  "value": "ZeroTorque"
                },
                {
                  "key": "version",
                  "value": "robot-hu-d-2.1.0.20251225062343"
                },
                {
                  "key": "sn",
                  "value": "HU_D04_01_131"
                }
              ]
        }
    ]
  }
}
字段 含义
version 主控版本
ecm_version 主站版本
pms_version 分电板版本
motor_version 电机版本
sn 机器人序列号
robot_status 机器人当前状态
ability_running 机器人当前运行的控制器

3.6.1.3 电机状态信息

{
  "accid": "HU_D04_01_001", 
  "title": "notify_robot_info", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": [
          {
                "level": 1,
                "name": "ethercatCommunicationExp",
                "message": "WARN",
                "hardware_id": "ethercat",
                "values": [
                    {
                        "key": "ethercatCommunicationExp",
                        "value": "motor 17 MOTOR_LOST triggered HALF_STAND"
                    },
                    {
                        "key": "ethercatResetNormal",
                        "value": "ok!"
                    }
                ]
          }
    ]
  }
}
字段 含义
level 异常等级[0:ok 1:warn 2:error]
name 异常类型
message 等级字符串
hardware_id 硬件 id
values 该硬件的所有异常集合

3.6.2 遥控器数据

此协议将上报机器人遥控器数据。

上报数据信息 描述
accid 机器人序列号
title notify_joy_data
timestamp 消息发出时间戳,单位为毫秒
guid 消息的 guid 值,唯一标识这条消息
data 存放消息内容

示例:

{
  "accid": "HU_D04_01_001", 
  "title": "notify_joy_data", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "axes": [],     # 遥感数据
    "buttons": []   # 按键数据
  }
}

3.7 协议接口调用示例

3.7.1 Python 示例

  • 环境准备: 以 Ubuntu 20.04 系统为例,安装下面依赖
sudo apt install python3-dev python3-pip
sudo pip install websocket-client==1.8.0
  • 运行脚本
python humanoid.py
  • humanoid.py 实现
    • ACCID:替换为真实的软件 SN
    • ROBOT_IP: 一般情况,仿真为 127.0.0.1,真机为 10.192.1.2
import json
import uuid
import threading
import time
import websocket
from datetime import datetime

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

# 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
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 = {}
    
    # Create message structure with necessary fields
    message = {
        "accid": ACCID,
        "title": title,
        "timestamp": int(time.time() * 1000),  # Current timestamp in milliseconds
        "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 ('prepare', 'servo', 'movej', 'movel', 'movep', 'head', 'waist', 'state', 'claw_cmd', 'claw_state', 'damping', 'zero') or 'exit' to quit:\n")
        
        if command == "exit":
            should_exit = True  # Set exit flag to stop the loop
            break
        elif command == "prepare":
            send_request("request_prepare")  # request_prepare
        elif command == "servo":
            # Servo control mode flag from user
            mode_input = input("Enable mode (0/1/2):").strip()
            mode_value = int(mode_input) if mode_input in ('0','1','2') else 0
            send_request("request_set_move_mode", {"mode": mode_value})
        elif command == "movej":
            send_request("request_moveJ", { # request_moveJ
              "left": [-1.44532, 0.0987686, 0.179059, -1.64716, -0.0537614, 0.200834, -0.236136],
              "right": [0.10103,-0.0987769,-0.179462,-1.64705,0.0527488,0.198867,0.235933],
              "speed": 0.2
            }) 
        elif command == "movep":
            send_request("request_moveP", { # request_moveP
              "left_position": [0.089644,0.428712,0.0519788],
              "left_quat": [0.269296,-0.119683,-0.489868,0.820478],
              "right_position": [0.0835307,-0.531453,0.13568],
              "right_quat": [-0.436152,-0.285065,0.265969,0.81103],
              "speed": 0.1
            })
        elif command == "head":
            send_request("request_moveJ", { # request_moveJ
              "head_pitch": 0.5854,
              "head_yaw": 0.5854,
              "speed": 0.1
            })
        elif command == "waist":
            send_request("request_moveJ", { # request_set_waist_and_height
              "torso_height": 0.0,
              "torso_pitch": 0.0,
              "torso_roll": 0.0,
              "torso_yaw": 0.0
            })
        elif command == "claw_cmd":
            send_request("request_set_claw_cmd", { # request_set_claw_cmd
              "left_opening": 100,
              "left_speed": 500, 
              "left_force": 500,
              "left_mode": 1,
              "right_opening": 100,
              "right_speed": 500,
              "right_force": 500,
              "right_mode": 1
            })
        elif command == "claw_state":
            send_request("request_get_claw_state")
        elif command == "state":
            send_request("request_get_move_pose")  # request_get_move_pose
        elif command == "damping":
            send_request("request_damping")  # request_damping
        elif command == "zero":
            send_request("request_zero_torque")  # request_zero_torque

# 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)
    title = root.get("title", "")
    ACCID = root.get("accid", None)

    if title != "notify_robot_info":
        print(f"Received message: {message}")  # Print the received 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(
        f"ws://{ROBOT_IP}:5000",  # WebSocket server URI
        on_open=on_open,
        on_message=on_message,
        on_close=on_close
    )
    
    # Configure socket send and receive buffer sizes
    # Increase send buffer size to 2MB (default is typically much smaller)
    # This helps prevent data loss when sending large messages or high-frequency data
    ws_client.sock_opt = [("socket", "SO_SNDBUF", 2 * 1024 * 1024)]
    
    # Increase receive buffer size to 2MB
    # This allows handling larger incoming messages without truncation
    ws_client.sock_opt.append(("socket", "SO_RCVBUF", 2 * 1024 * 1024))
    
    # Run WebSocket client loop
    print("Press Ctrl+C to exit.")
    ws_client.run_forever()

if __name__ == "__main__":
    main()

3.7.2 Linux C++ 示例

  • 环境准备: 以 Ubuntu 20.04 系统为例,安装 websocketpp、nlohmann/json 和 boost 依赖:
sudo apt-get install libboost-all-dev libwebsocketpp-dev nlohmann-json3-dev
  • 编译代码
g++ -std=c++11 humanoid humanoid.cpp -o humanoid humanoid -lssl -lcrypto -lboost_system -lpthread
  • 运行程序
./humanoid
  • humanoid.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;
  
  // Adding necessary fields to the 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();
  
  // Send the message through WebSocket
  ws_client.send(current_hdl, message_str, websocketpp::frame::opcode::text);
}

// Handle user commands
void handle_commands() {
  std::cout << "Enter command ('prepare', 'servo', 'movej', 'movel', 'movep', 'head', 'state', 'waist', 'claw_cmd', 'claw_state', 'damping', 'zero') or 'exit' to quit:\n";
  while (!should_exit) {
      std::string command;
      std::cin >> command;

      if (command == "exit") {
          should_exit = true;
          return;
      } else if (command == "prepare") {
          send_request("request_prepare");
      } else if (command == "servo") {
          int mode_value;
          std::cout << "Enable mode (0/1/2): ";
          if (!(std::cin >> mode_value)) {
              std::cerr << "Error: Invalid input. Please enter 0, 1, or 2." << std::endl;
              return;
          }
          if (mode_value < 0 || mode_value > 2) {
              std::cerr << "Error: Invalid input. Please enter 0, 1, or 2." << std::endl;
              return;
          }
          
          nlohmann::json data = {{"mode", mode_value}};
          send_request("request_set_move_mode", data);
      } else if (command == "movej") {
          nlohmann::json data = {
              {"left", {-1.44532, 0.0987686, 0.179059, -1.64716, -0.0537614, 0.200834, -0.236136}},
              {"right", {0.10103,-0.0987769,-0.179462,-1.64705,0.0527488,0.198867,0.235933}},
              {"speed", 0.2}
          };
          send_request("request_moveJ", data);
      } else if (command == "movep") {
          nlohmann::json data = {
              {"left_position", {0.089644,0.428712,0.0519788}},
              {"left_quat", {0.269296,-0.119683,-0.489868,0.820478}},
              {"right_position", {0.0835307,-0.531453,0.13568}},
              {"right_quat", {-0.436152,-0.285065,0.265969,0.81103}},
              {"speed", 0.1}
          };
          send_request("request_moveP", data);
      } else if (command == "head") {
          nlohmann::json data = {
              {"head_yaw", 0.5854},
              {"head_pitch", 0.5854},
              {"speed", 0.1}
          };
          send_request("request_moveJ", data);
      } else if (command == "waist") {
          nlohmann::json data = {
              {"torso_height", 0.0},
              {"torso_pitch", 0.0},
              {"torso_roll", 0.0},
              {"torso_yaw", 0.0}
          };
          send_request("request_moveJ", data);
      } else if (command == "claw_cmd") {
          nlohmann::json data = {
              {"left_opening", 100},
              {"left_speed", 500},
              {"left_force", 500},
              {"left_mode", 1},
              {"right_opening", 100},
              {"right_speed", 500},
              {"right_force", 500},
              {"right_mode", 1}
          };
          send_request("request_set_claw_cmd", data);
      } else if (command == "claw_state") {
          send_request("request_get_claw_state");
      } else if (command == "state") {
          send_request("request_get_move_pose");
      } else if (command == "damping") {
          send_request("request_damping");
      } else if (command == "zero") {
          send_request("request_zero_torque");
      }

      sleep(1);

      std::cout << "\nEnter command ('prepare', 'servo', 'movej', 'movel', 'movep', 'servop', 'head', 'waist', 'state', 'damping', 'zero') or 'exit' to quit:\n";
  }
}

// WebSocket open callback
static void on_open(connection_hdl hdl) {
  std::cout << "Connected!" << std::endl;
  
  // Save connection handle for sending messages later
  current_hdl = hdl;

  // Start handling commands in a separate thread
  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);

  // Obtain the underlying TCP socket
  auto& socket = con->get_socket().lowest_layer();

  // Configure socket options
  try {
    boost::system::error_code ec;
    
    // Set send buffer size (e.g., 2MB)
    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());
    }

    // Set receive buffer size (e.g., 2MB)
    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());
    }

    // Disable Nagle's algorithm to reduce latency
    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) {
  // Parse JSON data from message payload
  json data = json::parse(msg->get_payload());
        
  // Extract 'accid' field if present
  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");  // Close connection normally
}

int main() {
  ws_client.init_asio();  // Initialize ASIO for WebSocket client

  ws_client.set_access_channels(websocketpp::log::alevel::none);
  
  // Set WebSocket event handlers
  ws_client.set_open_handler(&on_open);  // Set open handler
  ws_client.set_message_handler(&on_message);  // Set message handler
  ws_client.set_close_handler(&on_close);  // Set close handler
  ws_client.set_tcp_init_handler(&on_tcp_init); // Set tcp init handler

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

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

  if (ec) {
      std::cout << "Error: " << ec.message() << std::endl;
      return 1;  // Exit if connection error occurs
  }

  connection_hdl hdl = con->get_handle();  // Get connection handle
  ws_client.connect(con);  // Connect to server
  std::cout << "Press Ctrl+C to exit." << std::endl;
  
  // Run the WebSocket client loop
  ws_client.run();

  return 0;
}

3.7.3 JavaScript 示例

  • 运行 humanoid.html: 将 humanoid.html 文件保存到电脑中,然后在浏览器中打开 humanoid.html 运行。

图片

  • humanoid.html 实现
<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Robot Control</title>
    <style>
        #commandInput {
            width: 700px; 
            padding: 10px;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <h2>Dual ARM Commands</h2>
    <input type="text" id="commandInput" placeholder="Enter command ('prepare', 'servo', 'movej', 'movel', 'movep', 'head', 'waist', 'claw_cmd', 'claw_state', 'state', 'damping', 'zero', 'exit')">
    <p>Type a command and press Enter.</p>

    <script>
        // Replace this ACCID value with your robot's actual serial number (SN)
        let ACCID = "";

        // WebSocket client instance
        let wsClient = null;

        // Generate dynamic GUID
        function generateGuid() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                const r = Math.random() * 16 | 0,
                      v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        }

        // Send WebSocket request with title and data
        function sendRequest(title, data = {}) {
            const message = {
                accid: ACCID,
                title: title,
                timestamp: Date.now(),
                guid: generateGuid(),
                data: data
            };

            if (wsClient && wsClient.readyState === WebSocket.OPEN) {
                wsClient.send(JSON.stringify(message));
            }
        }

        // Handle user commands
        function handleCommands() {
            const commandInput = document.getElementById('commandInput');
            commandInput.addEventListener('keydown', function(event) {
                if (event.key === 'Enter') {
                    const command = commandInput.value.trim();
                    commandInput.value = '';

                    switch (command) {
                        case 'prepare':
                            sendRequest('request_prepare');
                            break;
                        case 'servo':
                            const modeInput = prompt("Enable Servo (0/1/2):").trim();
                            let modeValue = 0;
                            
                            // 尝试把输入解析为整数
                            const n = parseInt(modeInput, 10);
                            if (!Number.isNaN(n) && (n === 0 || n === 1 || n === 2)) {
                                modeValue = n;
                            } else {
                                // 非法输入,给出错误并返回
                                alert("Error: Invalid input. Please enter 0, 1, or 2.");
                                return;
                            }
                            sendRequest('request_set_move_mode', { mode: modeValue });
                            break;
                        case 'movej':
                            sendRequest('request_moveJ', {
                                left: [-1.44532, 0.0987686, 0.179059, -1.64716, -0.0537614, 0.200834, -0.236136],
                                right: [0.10103,-0.0987769,-0.179462,-1.64705,0.0527488,0.198867,0.235933],
                                speed: 0.2
                            });
                            break;
                        case 'movep':
                            sendRequest('request_moveP', {
                                left_position: [0.089644,0.428712,0.0519788],
                                left_quat: [0.269296,-0.119683,-0.489868,0.820478],
                                right_position: [0.0835307,-0.531453,0.13568],
                                right_quat: [-0.436152,-0.285065,0.265969,0.81103],
                                speed: 0.1
                            });
                            break;
                        case 'head':
                            sendRequest('request_moveJ', {
                                head_yaw: 0.5854,
                                head_pitch: 0.5854,
                                speed: 0.1
                            });
                            break;
                        case 'waist':
                            sendRequest('request_moveJ', {
                                torso_height: 0.0,
                                torso_pitch: 0.0,
                                torso_roll: 0.0,
                                torso_yaw: 0.0
                            });
                            break;
                        case 'claw_cmd':
                            sendRequest('request_set_claw_cmd', {
                                left_opening: 100,
                                left_speed: 500,
                                left_force: 500,
                                left_mode: 1,
                                right_opening: 100,
                                right_speed: 500,
                                right_force: 500,
                                right_mode: 1
                            });
                            break;
                        case 'claw_state':
                            sendRequest('request_get_claw_state');
                            break;
                        case 'state':
                            sendRequest('request_get_move_pose');
                            break;
                        case 'damping':
                            sendRequest('request_damping');
                            break;
                        case 'zero':
                            sendRequest('request_zero_torque');
                            break;
                        case 'exit':
                            wsClient.close();
                            break;
                        default:
                            alert("Invalid command. Try again.");
                    }
                }
            });
        }

        // WebSocket onOpen callback
        function onOpen() {
            console.log("Connected!");
            handleCommands();
        }

        // WebSocket onMessage callback
        function onMessage(event) {
            try {
                const message = JSON.parse(event.data);

                // Dynamically set ACCID from message if not already set
                if (!ACCID && message.accid) {
                    ACCID = message.accid;
                    console.log(`ACCID set to: ${ACCID}`);
                }
            } catch (error) {
                console.log("Failed to parse message:", error);
            }
            
            if (event.data.includes('notify_robot_info')) return;
            console.log("Received message:", event.data);
        }

        // WebSocket onClose callback
        function onClose(event) {
            console.log("Connection closed.");
        }

        // Initialize WebSocket client
        function initWebSocket() {
            // 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
            wsClient = new WebSocket('ws://10.192.1.2:5000');
            wsClient.onopen = onOpen;
            wsClient.onmessage = onMessage;
            wsClient.onclose = onClose;
            console.log("Press Ctrl+C to exit.");
        }

        // Start WebSocket connection when the page loads
        window.onload = initWebSocket;
    </script>
</body>
</html>

4 底层运动控制开发接口

跨平台底层运动控制开发接口库提供统一的 C++/Python API,兼容 ROS1、ROS2 及非 ROS 系统,实现运动控制算法的快速移植与部署。通过硬件抽象层和标准化通信协议,开发者可无缝切换仿真与真实硬件环境,显著降低多平台适配成本。

注意:

  1. 使用底层控制开发接口时,需通过按键 R1+START 切换开发者模式,此时高层开发接口会被禁用,机器人只响应上下电和校零遥控器指令。
  2. 底层接口代码调用示例可参考 RL 部署训练。
  3. 切换到开发者模式后,掉电模式会保留,退出开发者模式按键:L2+○

4.1 C++ 运动控制开发接口

4.1.1 getInstance 接口

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

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 无限循环以保持程序运行
  while (true)
  {
    // 休眠 1000 毫秒
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  
  return 0;
}

4.1.2 init 接口

函数名 init
函数原型 bool init(const std::string& robot_ip_address = "127.0.0.1");
功能概述 初始化运动控制算法程序的通信运行环境,通常在主函数中调用其它接口之前调用,完成初始化工作。
参数 robot_ip_address:机器人的 IP 地址。对于仿真,通常设置为 "127.0.0.1",而对于真实机器人,可能设置为 "10.192.1.2"。
返回值 如果初始化成功,则返回 true;否则返回 false。
备注

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  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;
}

4.1.3 getMotorNumber 接口

函数名 getMotorNumber
函数原型 uint32_t getMotorNumber();
功能概述 获取机器人中的电机数量。
参数
返回值 返回一个无符号整数,表示机器人中的总电机数量。
备注

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  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;
}

4.1.4 subscribeImuData 接口

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

备注:ImuData 数据结构原型如下:

/**
 * @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;

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // 如果提供了命令行参数,则使用命令行参数作为机器人 IP 地址
    robot_ip = argv[1];
  }
  
  // 初始化运动控制算法程序的通信运行环境
  if (!robot->init(robot_ip))
  {
    // 如果初始化失败,则退出程序
    exit(1);
  }
  
  // 订阅机器人状态更新,并指定回调函数
  robot->subscribeImuData([&](const ImuDataConstPtr& msg) {
    // 在这里处理接收到的 ImuData 数据
    // 注意:回调函数会在收到ImuData时被调用
  });
  
  // 无限循环以保持程序运行
  while (true)
  {
    // 休眠 1000 毫秒
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  return 0;
}

4.1.5 subscribeRobotState 接口

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

备注:RobotState 数据结构原型如下:

/**
 * @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)
  , motor_names(motor_num, "") { }
  
  uint64_t stamp;              // 时间戳,通常表示记录或生成这些数据的时间,以纳秒为单位
  std::vector<float> tau;      // 用于存储当前估计的输出扭矩(以牛顿米为单位)的向量
  std::vector<float> q;        // 用于存储当前角度(以弧度为单位)的向量
  std::vector<float> dq;       // 用于存储当前速度(以弧度每秒为单位)的向量
  std::vector<std::string> motor_names; // 用于存储机器人各个关节名称的向量
};

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

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // 如果提供了命令行参数,则使用命令行参数作为机器人 IP 地址
    robot_ip = argv[1];
  }
  
  // 初始化运动控制算法程序的通信运行环境
  if (!robot->init(robot_ip))
  {
    // 如果初始化失败,则退出程序
    exit(1);
  }
  
  // 订阅机器人状态更新,并指定回调函数
  robot->subscribeRobotState([&](const RobotStateConstPtr& msg) {
    // 在这里处理接收到的 RobotState 数据
    // 注意:回调函数会在收到机器人状态更新时被调用
  });
  
  // 无限循环以保持程序运行
  while (true)
  {
    // 休眠 1000 毫秒
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  return 0;
}

4.1.6 publishRobotCmd 接口

函数名 publishRobotCmd
函数原型 bool publishRobotCmd(const RobotCmd& cmd);
功能概述 发布一个命令来控制机器人的动作。
参数 cmd:表示所需机器人命令的 RobotCmd 对象。
返回值

备注:RobotCmd 数据结构原型如下:

/**
 * @struct RobotCmd
 *
 * @brief 代表控制机器人的命令的结构体。
 *
 * 这个结构体包含了可以用于控制机器人的各种命令,包括期望的工作模式、期望的角度、期望的速度、期望的输出扭矩、期望的位置刚度和期望的速度刚度。
 */
struct RobotCmd {
  RobotCmd() { }
  RobotCmd(int motor_num)
  : mode(motor_num, 0)
  , q(motor_num, 0.0)
  , dq(motor_num, 0.0)
  , tau(motor_num, 0.0)
  , Kp(motor_num, 0.0)
  , Kd(motor_num, 0.0)
  , motor_names(motor_num, "") { }
  
  uint64_t stamp;             // 时间戳(以纳秒为单位),通常表示记录或生成数据时的时间。
  std::vector<uint8_t> mode;  // 0: 力矩模式控制;1:速度模式控制;2:位置模式控制,默认设置为:0
  std::vector<float> q;       // 存储期望角度的向量(以弧度为单位)。
  std::vector<float> dq;      // 存储期望速度的向量(以弧度每秒为单位)。
  std::vector<float> tau;     // 存储期望输出扭矩的向量(以牛顿米为单位)。
  std::vector<float> Kp;      // 存储期望位置刚度的向量(以牛顿米每弧度为单位)。
  std::vector<float> Kd;      // 存储期望速度刚度的向量(以牛顿米每弧度每秒为单位)。
  std::vector<std::string> motor_names;   // 存储期望控制的机器人关节名称
};

// 智能指针类型别名
typedef std::shared_ptr<RobotCmd> RobotCmdPtr;
typedef std::shared_ptr<RobotCmd const> RobotCmdConstPtr;

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  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;
}

4.1.7 subscribeSensorJoy 接口

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

备注:SensorJoy 数据结构原型如下:

/**
 * @struct SensorJoy
 *
 * @brief 机器人遥控器数据的结构体。
 *
 * 该结构体包含了与遥控器相关的时间戳信息,以及摇杆和按钮值。
 */
struct SensorJoy {
    uint64_t stamp;                     // 与传感器输入相关的时间戳,单位为纳秒。
    std::vector<float> axes;            // 表示操纵摇杆的值。
    std::vector<int32_t> buttons;       // 表示操纵按钮状态的值。
};       

// SensorJoy 智能指针类型的定义
typedef std::shared_ptr<SensorJoy> SensorJoyPtr;
typedef std::shared_ptr<const SensorJoy> SensorJoyConstPtr;

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"  

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();  
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // 如果提供了命令行参数,则使用命令行参数作为机器人 IP 地址
    robot_ip = argv[1];
  }
  
  // 初始化运动控制算法程序的通信运行环境
  if (!robot->init(robot_ip))
  {
    // 如果初始化失败,则退出程序
    exit(1);
  }
  
  // 订阅机器人遥控器数据
  robot->subscribeSensorJoy([&](const limxsdk::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;
}

4.1.8 subscribeDiagnosticValue 接口

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

备注:DiagnosticValue 数据结构原型如下:

/**
 * @struct DiagnosticValue
 *
 * @brief 结构体,表示诊断值。
 *
 * 此结构体包含有关诊断级别、名称、代码和消息的信息。
 */
struct DiagnosticValue {
  enum { OK = 0 };         // 正常状态的诊断级别
  enum { WARN = 1 };       // 警告状态的诊断级别
  enum { ERROR = 2 };      // 错误状态的诊断级别

  uint64_t stamp;          // 时间戳,单位为纳秒。
  int32_t level;           // 与诊断值相关联的级别。
  std::string name;        // 标识诊断值的名称。
  int32_t code;            // 与诊断值对应的代码。
  std::string message;     // 与诊断值相关的详细消息。
};

// DiagnosticValue 智能指针类型的定义
typedef std::shared_ptr<DiagnosticValue> DiagnosticValuePtr;
typedef std::shared_ptr<DiagnosticValue const> DiagnosticValueConstPtr;

代码示例:

#include <thread>

// 包含 limxsdk::Humanoid 头文件,用于引入 Humanoid 类
#include "limxsdk/humanoid.h"  

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

int main(int argc, char *argv[]){
  // 获取 Humanoid 类的单例实例
  Humanoid* robot = Humanoid::getInstance();  
  
  // 默认机器人 IP 地址
  std::string robot_ip = "127.0.0.1";
  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;
}

4.1.9 publishJsonMessage 接口

函数名 publishJsonMessage
函数原型 void publishJsonMessage(const std::string &json_payload);
功能概述 向机器人发送 "上层应用协议接口" 的 JSON 格式消息。
参数 json_payload: 符合 "上层应用协议接口" 协议的 JSON 字符串。 示例:{"accid": "xxx", "title": "request_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
返回值
备注 高阶开发模式下有效

代码示例

#include <thread>

#include "limxsdk/humanoid.h"

using namespace limxsdk;

int main(int argc, char *argv[]) {
  Humanoid* robot = Humanoid::getInstance();
  
  std::string robot_ip = "10.192.1.2";
  if (argc > 1) {
    robot_ip = argv[1];
  }
  
  if (!robot->init(robot_ip)) {
    exit(1);
  }
  
  std::string json_payload = R"({
    "accid": "HU_D03_01",
    "title": "request_get_joint_state",
    "timestamp": 1672373633989,
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "data": {}
  })";
  
  robot->publishJsonMessage(json_payload);
  
  while (true) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  
  return 0;
}

4.1.10 subscribeJsonMessage 接口

函数名 subscribeJsonMessage
函数原型 void subscribeJsonMessage(std::function<void(const std::string &)> cb);
功能概述 注册一个回调函数,用于处理来自机器人的 "上层应用协议接口" 调用的响应和通知。 该回调函数会在以下两种场景中被调用: 1. 当机器人对之前发送的 JSON 命令(publishJsonMessage)返回响应时 2. 当机器人主动发起通知(未经请求的消息)时
参数 cb: 回调函数,原型为 void(const std::string &json_payload) 其中 json_payload 包含: - 命令响应:{"accid": "xxx", "title": "response_xxx", "timestamp": xxx, "guid": "xxx", "data": {}} - 通知:{"accid": "xxx", "title": "notify_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
返回值
备注 高阶开发模式下有效

代码示例

#include <thread>

#include "limxsdk/humanoid.h"

using namespace limxsdk;

int main(int argc, char *argv[]) {
  Humanoid* robot = Humanoid::getInstance();
  
  std::string robot_ip = "10.192.1.2";
  if (argc > 1) {
    robot_ip = argv[1];
  }
  
  if (!robot->init(robot_ip)) {
    exit(1);
  }
  
  robot->subscribeJsonMessage([&](const std::string & json_payload) {
    std::cout << json_payload << std::endl;
  });
  
  while (true) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
  
  return 0;
}

4.1.11 参考例程

Github: https://github.com/limxdynamics/humanoid-rl-deploy-ros

4.2 Python 运动控制开发接口

提供与 C++ 相同功能的 Python 运动控制算法开发接口使得不熟悉 C++ 编程语言的开发者能够使用 Python 进行运动控制算法的开发。

4.2.1 安装运动控制开发库

  • Linux x86_64 环境
pip install python3/amd64/limxsdk-*-py3-none-any.whl
  • Linux aarch64 环境
pip install python3/aarch64/limxsdk-*-py3-none-any.whl
  • Windows 环境
pip install python3/win/limxsdk-*-py3-none-any.whl

4.2.2 init 接口

函数名 init
函数原型 def init(self, robot_type: robot.RobotType)
功能概述 在初始化时,指定机器人的类型,并创建一个相应类型的本地机器人实例。
参数 robot_type:表示机器人类型的枚举值,点足为RobotType.Humanoid。
返回值
备注

代码示例

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

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

4.2.3 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
备注

代码示例

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

if __name__ == '__main__':
  # 创建一个类型为Humanoid的Robot实例
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了命令行参数作为机器人IP
  if len(sys.argv) > 1:
    robot_ip = sys.argv[1]
  
  # 使用IP地址初始化机器人的通信运行环境
  if not robot.init(robot_ip):
    sys.exit()

4.2.4 getMotorNumber 接口

函数名 getMotorNumber
函数原型 def getMotorNumber(self)
功能概述 获取机器人中的电机数量。
参数
返回值 返回一个无符号整数,表示机器人中的总电机数量。
备注 通常情况下,点足机器人的电机数量为6个

代码示例

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

if __name__ == '__main__':
  # 创建一个 Humanoid 类型的 Robot 实例
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 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()

4.2.5 subscribeImuData 接口

函数名 subscribeImuData
函数原型 def subscribeImuData(self, callback: Callable[[datatypes.ImuData], Any])
功能概述 订阅机器人的 IMU数据,并在接收到新的 IMU 数据时调用指定的回调函数。
参数 callback:用于处理新 IMU 数据的回调函数。
返回值 成功:返回True
失败:返回False

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

import sys

class ImuData(object):
  __slots__ = ['stamp','acc','gyro','quat']
  def __init__(self):
    self.stamp = 0 # 时间戳,通常表示记录或生成这些数据的时间,以纳秒为单位
    self.acc = [0. for x in range(0, 3)]  # 用于存储 IMU(惯性测量单元)加速度计数据,用于跟踪三个轴上的线性加速度
    self.gyro = [0. for x in range(0, 3)] # 用于存储 IMU 陀螺仪数据,用于跟踪角速度或旋转速度
    self.quat = [0. for x in range(0, 4)] # 用于存储 IMU 四元数数据,表示在三维空间中的方向

代码示例

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__':
  # 创建一个 Robot 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 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)

4.2.6 subscribeRobotState 接口

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

备注:datatypes.RobotState 数据结构原型如下:

import sys

class RobotState(object):
  __slots__ = ['stamp','tau','q','dq']
  def __init__(self):
    self.stamp = 0 # 时间戳,通常表示记录或生成这些数据的时间,以纳秒为单位
    self.tau = []  # 用于存储当前估计的输出扭矩(以牛顿米为单位)的向量
    self.q = []    # 用于存储当前角度(以弧度为单位)的向量
    self.dq = []   # 用于存储当前速度(以弧度每秒为单位)的向量
    self.motor_names = []   # 用于存储对应的关节名称。

代码示例

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("
------
robot_state:" + \
          "
  stamp: " + str(robot_state.stamp) + \
          "
  tau: " + str(robot_state.tau) + \
          "
  q: " + str(robot_state.q) + \
          "
  dq: " + str(robot_state.dq))

if __name__ == '__main__':
  # 创建一个 Robot 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 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)

4.2.7 publishRobotCmd 接口

函数名 publishRobotCmd
函数原型 def publishRobotCmd (self, cmd: datatypes.RobotCmd)
功能概述 发布一个命令来控制机器人的动作。
参数 cmd:表示所需机器人命令的 datatypes.RobotCmd 对象,包含以下字段:
- stamp:记录或生成数据时的时间戳,以纳秒为单位。
- q:存储所需的关节角度(以弧度为单位)的向量。
- dq:存储所需的关节速度(以弧度每秒为单位)的向量。
- tau:存储所需的输出扭矩(以牛顿米为单位)的向量。
- Kp:存储所需的位置刚度(以牛顿米每弧度为单位)的向量。
- Kd:存储所需的速度刚度(以牛顿米每弧度每秒为单位)的向量。
- motor_names:存储需要控制的关节名称。
参数取值 - q、dq、tau:在urdf中已定义范围,举例如下:
- joint的字段中描述了每个关节的q、dq、tau取值范围:
- lower和upper共同对应"q",表示控制位置范围(单位为弧度);
- effort对应"tau",表示扭矩峰值(单位为牛顿米);
- velocity对应"dq",表示速度峰值(单位为弧度每秒);
- Kp、Kd可用值(用户自行开发控制器时需根据实际效果调整取值):
返回值 成功:返回True
失败:返回False

备注:datatypes.RobotCmd 数据结构原型如下:

import sys

class RobotCmd(object):
  __slots__ = ['stamp','mode','q','dq','tau','Kp','Kd']
  def __init__(self):
    self.stamp = 0 # 时间戳(以纳秒为单位),表示记录或生成数据时的时间。
    self.mode = [] # 机器人的期望工作模式。
    self.q = []    # 存储期望角度的向量(以弧度为单位)
    self.dq = []   # 存储期望速度的向量(以弧度每秒为单位)。
    self.tau = []  # 存储期望输出扭矩的向量(以牛顿米为单位)。
    self.Kp = []   # 存储期望位置刚度的向量(以牛顿米每弧度为单位)。
    self.Kd = []   # 存储期望速度刚度的向量(以牛顿米每弧度每秒为单位)。
    self.motor_names = []   # 存储需要控制的关节名称。

代码示例

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 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 IP 的命令行参数
  if len(sys.argv) > 1:
    robot_ip = sys.argv[1]
  
  # 使用 robot_ip 初始化机器人
  if not robot.init(robot_ip):
    sys.exit()
  
  # 获取关节偏移、关节限制和电机数量信息
  joint_offset = robot.getJointOffset()
  joint_limit = robot.getJointLimit()
  motor_number = robot.getMotorNumber()
  
  # 主循环以连续发布机器人命令
  rate = Rate(500) # 1500 Hz
  cmd_msg = datatypes.RobotCmd()
  while True:
    # 设置时间戳、控制模式、关节位置、速度、力矩、Kp 和 Kd 的默认值
    cmd_msg.stamp = time.time_ns()
    cmd_msg.mode = [1.0 for _ in range(motor_number)]
    cmd_msg.q = [1.0 for _ in range(motor_number)]
    cmd_msg.dq = [1.0 for _ in range(motor_number)]
    cmd_msg.tau = [1.0 for _ in range(motor_number)]
    cmd_msg.Kp = [1.0 for _ in range(motor_number)]
    cmd_msg.Kd = [1.0 for _ in range(motor_number)]
    robot.publishRobotCmd(cmd_msg)  # 发布机器人命令
    rate.sleep()  # 控制循环频率

4.2.8 subscribeSensorJoy 接口

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

备注: datatypes.SensorJoy 数据结构原型如下:

import sys

class SensorJoy(object):
  __slots__ = ['stamp','axes','buttons']
  def __init__(self):
    self.stamp = 0     # 与传感器输入相关的时间戳,单位为纳秒。
    self.axes = []     # 表示操纵摇杆的值。
    self.buttons = []  # 表示操纵按钮状态的值。

代码示例

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("
------
sensor_joy:" + \
          "
  stamp: " + str(sensor_joy.stamp) + \
          "
  axes: " + str(sensor_joy.axes) + \
          "
  buttons: " + str(sensor_joy.buttons))

if __name__ == '__main__':
  # 创建一个 Robot 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 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)

4.2.9 subscribeDiagnosticValue 接口

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

备注: datatypes.DiagnosticValue 数据结构原型如下:

import sys

class DiagnosticValue(object):
  __slots__ = ['stamp','level','name','code','message']
  def __init__(self):
    self.stamp = 0 # 时间戳,单位为纳秒。
    self.level = 0 # 与诊断值相关联的级别 - 0: OK, 1: WARN, 2: ERROR
    self.name = '' # 标识诊断值的名称。
    self.code = 0  # 与诊断值对应的代码。
    self.message = ''  # 与诊断值相关的详细消息。

代码示例

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("
------
diagnostic_value:" + \
          "
  stamp: " + str(diagnostic_value.stamp) + \
          "
  name: " + diagnostic_value.name + \
          "
  level: " + str(diagnostic_value.level) + \
          "
  code: " + str(diagnostic_value.code) + \
          "
  message: " + diagnostic_value.message)

if __name__ == '__main__':
  # 创建一个 Robot 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  robot_ip = "127.0.0.1"
  # 检查是否提供了机器人 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)

4.2.10 publishJsonMessage 接口

函数名 publishJsonMessage
函数原型 def publishJsonMessage(self, json_payload: str)
功能概述 向机器人发送 "上层应用协议接口" 的 JSON 格式消息。
参数 json_payload: 符合 "上层应用协议接口" 协议的 JSON 字符串。
示例:{"accid": "xxx", "title": "request_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
返回值
备注 高阶开发模式下有效

代码示例

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 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  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()
  
  # 设置要发送的协议内容
  json_payload = '''{
    "accid": "HU_D03_01", # 替换为真实机器人SN
    "title": "request_get_joint_state",
    "timestamp": 1672373633989,
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "data": {}
  }'''
  
  # 发送JSON协议
  robot.publishJsonMessage(json_payload)
  
  # 保持程序运行
  try:
    while True:
      time.sleep(1)
  except KeyboardInterrupt:
    print("程序被用户中断")

4.2.11 subscribeJsonMessage 接口

函数名 subscribeJsonMessage
函数原型 def subscribeJsonMessage(self, callback: Callable[[str], Any])
功能概述 注册一个回调函数,用于处理来自机器人的 "上层应用协议接口" 调用的响应和通知。
该回调函数会在以下两种场景中被调用:
1. 当机器人对之前发送的 JSON 命令(publishJsonMessage)返回响应时
2. 当机器人主动发起通知(未经请求的消息)时
参数 callback: 回调函数, 其中 json_payload 包含:
- 命令响应:{"accid": "xxx", "title": "response_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
- 通知:{"accid": "xxx", "title": "notify_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
返回值
备注 高阶开发模式下有效

代码示例

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 jsonMessageCallback(self, json_payload: str):
    print("
------
json_payload:" + json_payload)

if __name__ == '__main__':
  # 创建一个 Robot 实例,类型为 Humanoid
  robot = Robot(RobotType.Humanoid)
  
  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 函数
  jsonMessageCallback = partial(receiver.jsonMessageCallback)
  
  # 订阅机器人遥控数据
  robot.subscribeJsonMessage(jsonMessageCallback)

4.2.12 参考例程

Github: https://github.com/limxdynamics/humanoid-rl-deploy-python

5 查看/设置机器人型号

在编译和运行RL训练、控制算法及仿真器程序时,选择正确的机器人型号至关重要。通过查看机器人型号并将其设置到环境变量 ROBOT_TYPE 中,确保在不同任务中准确识别并应用相应的机器人模型。

查看和配置机器人型号步骤:

  1. 选择并连接您机器人Wi-Fi 热点,密码为:12345678

图片

  1. 在浏览器中输入http://10.192.1.2:8080进入“机器人信息页”,查看机器人信息。如下图所示,页面中显示的SN (序列号) 为HU_D03_03_001,其中HU_D03_03为机器人型号。

图片

  1. 设置机器人型号:打开 Bash 终端,输入以下 Shell 命令来设置机器人型号。这样在二次开发时,您将能获取到正确的机器人型号信息。
echo 'export ROBOT_TYPE=HU_D03_03' >> ~/.bashrc && source ~/.bashrc

6 机器人仿真器

MuJoCo 是一款轻量级且高性能的物理仿真器,专为多关节机器人和机械系统设计。它具备高效的物理引擎,能够精确处理接触和摩擦,且无需依赖 ROS,可独立运行。凭借其高速计算能力,MuJoCo 被广泛应用于机器人仿真和强化学习,尤其适合对仿真效率要求较高的场景。

6.1 运行仿真器步骤

  1. 运行环境:推荐 Pyhon 3.8 及以上版本

  2. 打开一个 Bash 终端。

  3. 下载 MuJoCo 仿真器代码:

git clone --recurse git@github.com:limxdynamics/humanoid-mujoco-sim.git
  1. 安装运动控制开发库:
  • Linux x86_64 环境
pip install humanoid-mujoco-sim/limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
  • Linux aarch64 环境
pip install humanoid-mujoco-sim/limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
  1. 设置机器人型号:请参考"查看/设置机器人型号"章节,查看您的机器人型号。如果尚未设置,请按照以下步骤进行设置。
  • 通过 Shell 命令 tree -L 3 -P "meshes" -I "urdf|world|xml|usd" humanoid-mujoco-sim/humanoid-description 列出可用的机器人类型:
limx@limx:~$ tree -L 3 -P "meshes" -I "urdf|world|xml|usd" humanoid-mujoco-sim/humanoid-description
humanoid-mujoco-sim/humanoid-description
├── HU_D03_description
│   └── meshes
│       └── HU_D03_03
└── HU_D04_description
    └── meshes
        └── HU_D04_01
  • HU_D04_01(请根据实际机器人类型进行替换)为例,设置机器人型号类型:
echo 'export ROBOT_TYPE=HU_D04_01' >> ~/.bashrc && source ~/.bashrc
  1. 运行 MuJoCo 仿真器:
python humanoid-mujoco-sim/simulator.py

6.2 演示效果(请以实际为准)

图片

7 RL 算法部署

7.1 基于标准C++进行部署

Github项目地址:https://github.com/limxdynamics/humanoid-rl-deploy-cpp,它是一个在标准C++中实现的轻量级算法框架。无需 ROS1/ROS2 即可快速部署训练的模型。

7.2 基于Python进行部署

Github项目地址:https://github.com/limxdynamics/humanoid-rl-deploy-python,它是一种基于Python的强化学习部署算法框架,它简化了在Oli机器人上部署训练模型的过程。

7.3 基于ROS2 进行部署

Github项目地址:https://github.com/limxdynamics/humanoid-rl-deploy-ros2,它是基于 ROS2 的强化学习部署框架,可在 Oli 机器人上快速部署经过训练的模型。

7.4 基于ROS1 进行部署

Github项目地址:https://github.com/limxdynamics/humanoid-rl-deploy-ros,它是基于ROS1的强化学习部署框架,可以在Oli机器人上快速部署经过训练的模型。

8 日志和数据包

  • 机器人系统自动录制数据: IMU数据(ImuData)、状态数据(/joint/state)、控制数据(/joint/cmd)、运行日志数据等重要数据,这些数据对于机器人的运动控制分析至关重要。

  • 访问并下载机器人数据: 机器人将会记录运行时的日志数据,以便在需要时进行故障排查和性能优化,电脑与机器人 WiFi 热点连接后浏览器输入 http://10.192.1.2:8090,可自行选择下载所需数据。

8.1 数据包可视化分析方法

  • 数据包下载: 下载 .bag 文件后,可以使用PlotJuggler可视化工具加载这些数据包进行分析。需要特别注意的是,如果下载的是 .bag.active 文件,需要使用如下Shell命令把它重新索引 .bag.active 文件,生成一个新的 .bag 文件,以便PlotJuggler加载。
rosbag reindex your_file.bag.active
mv your_file.bag.active your_file.bag
  • 可视化查看: 通过Shell命令 rosrun plotjuggler plotjuggler -n 启动PlotJuggler可视化工具。如下图所示加载数据包进行可视化分析。

8.2 日志及诊断埋点数据

如下图所示分别为日志和埋点结构化数据。在需要时可以用于故障排查和性能优化。

图片

9 机器人软件升级

软件升级注意事项:

  1. 请确保设备电量不低于 30%。升级过程中如发生断电,可能导致设备无法正常使用
  2. 请提前将机器人切换至零力矩模式或阻尼模式。升级完成后设备将自动重启,若机器人处于站立状态,可能存在跌倒风险。

通过浏览器进入机器人管理页面,选择本地提前下载好的机器人软件版本进行升级。具体步骤如下:

  1. 连接 Wi-Fi:

    • 选择并连接机器人的 Wi-Fi 热点,密码为:12345678

    图片

  2. 访问管理页面:

    • 在浏览器地址栏输入: http://10.192.1.2:8080 进入机器人管理页面。
  3. 选择并升级软件:

    • 依次选择"版本管理 -> 浏览 -> 升级"。
    • 升级完成后,机器人主控电脑将自动重启。

    图片

10 开发者电脑

开发者电脑主要用于开发机器人相关算法及应用程序。可以通过 WiFi 连接机器人本体系统登录到开发者电脑,具体步骤如下:

  1. 连接Wi-Fi:

    • 选择并连接机器人的 Wi-Fi 热点,密码为:12345678

    图片

  2. 通过 SSH 登录开发者电脑系统:

    • 登录地址:10.192.1.3
    • 登录密码:123456
    • 在终端中输入以下命令(首次连接需确认指纹):
ssh guest@10.192.1.3
  • 开发者电脑系统配置:
    • 操作系统:Ubuntu 22.04 (Jetpack 6.2.1)
    • ROS2:系统默认安装ROS2 Humble 版本机器人系统
    • ROS1:系统默认安装ROS1 Noetic 版本机器人系统Docker,进入方法如下:
sudo docker exec -it ros_noetic /bin/bash

11 Realsense 相机数据获取

注意事项:

  1. 机器人开机后默认会自动启动相机驱动。因此,在进行以下操作前,请先关闭相机驱动的自动启动功能。关闭方法请参考下文说明。该功能仅支持主控版本 V2.0.33 及以上。
  2. 关闭相机驱动自动启动功能后,我司配套的数据采集套件(数采套件)将无法正常使用。请根据实际需求决定是否执行该操作。

11.1 关闭相机驱动自启动

  1. 在浏览器地址栏输入 http://10.192.1.2:8080,进入以下界面。

    进入机器人管理页面

  2. 设置关闭并保存。

    关闭相机驱动自动启动并保存

11.2 获取相机数据

  1. 登录开发者电脑:

    • 请按照“开发者电脑”章节中的步骤登录该电脑。
  2. 启动 Realsense 的 ROS 节点以获取相机数据:

    • 启动方式参考链接:realsense-ros
    • 电脑已预装 Realsense 相机的 SDK:
      • 版本为 v2.56.3,官方下载链接:librealsense v2.56.3,可基于此官方 SDK 自主开发应用程序来获取数据。
  3. 示例代码说明:

    • 相机命名规则:
      • 默认命名:脚本会将多个相机的 topic 前缀命名为 camera 加上序号(如 camera0camera1)。
      • 自定义命名:可通过修改脚本,根据相机的序列号(Serial Number)指定 topic 前缀,而非使用默认的 camera + counter 命名规则。

以下代码示例展示了如何获取多个相机的数据:

  1. 通过 SSH 登录开发者电脑系统
  2. 登录 ROS1 系统
sudo docker exec -it ros_noetic /bin/bash
  1. 将下面的脚本保存为 rs_camera.sh
#!/bin/bash

source /opt/ros/noetic/setup.bash

# Function to detect connected RealSense cameras
detect_cameras() {
    # List all connected RealSense cameras, excluding Asic Serial Number
    serial_numbers=($(rs-enumerate-devices | grep "Serial Number" | grep -v "Asic" | awk '{print $NF}'))
    echo "${serial_numbers[@]}"
}

# Loop to check for the launch flag file and connected cameras
while true; do
    serial_numbers=($(detect_cameras))

    # Check if any cameras were found
    if [ ${#serial_numbers[@]} -gt 0 ]; then
        echo "Detected ${#serial_numbers[@]} cameras."
        break  # Exit the loop if cameras are detected
    else
        echo "No RealSense cameras detected. Retrying in 5 seconds..."
        sleep 5  # Wait for a while before retrying
    fi
done

# Automatically start a ROS node for each detected camera
if [ ${#serial_numbers[@]} -gt 0 ]; then
    for i in "${!serial_numbers[@]}"; do
        serial=${serial_numbers[$i]}

        # Default camera naming using index (camera0, camera1, ...)
        # Customize camera naming by modifying the code below
        camera_topic="camera$i"

        # Example: Custom camera naming based on serial number
        # Uncomment and replace with your actual serial numbers
        # if [[ "$serial" == "0123456789" ]]; then
        #     camera_topic="head"  # Name specific camera as "head"
        # elif [[ "$serial" == "9876543210" ]]; then
        #     camera_topic="chest" # Name another camera as "chest"
        # fi

        echo "Starting ROS node for camera $serial (topic prefix: $camera_topic)..."
        roslaunch realsense2_camera rs_camera.launch \
            serial_no:=$serial \
            camera:=$camera_topic \
            enable_pointcloud:=True \
            enable_accel:=True \
            enable_gyro:=True \
            enable_sync:=True \
            unite_imu_method:=linear_interpolation &
        sleep 10  # Optional: wait a bit before starting the next camera
    done

    # Wait for all background processes to finish
    wait
else
    echo "No cameras to start."
fi
  1. 在终端执行脚本,启动相机节点:
/bin/bash rs_camera.sh
  1. 在另一个终端中,通过 rostopic list 验证结果:
rostopic list