Oli EDU SDK Development Guide

Oli EDU Ed.2026/3/23
Version Date Description of Changes Compatible Software
(If incompatible, update to the latest version via the Official Download Center)
V1.0 2026.02.27 Initial Release V2.1.21 or later
V1.1 2026.07.17
  1. Removed the MCP server
  2. Added an option to disable camera driver auto-start
V2.2.11 or later
V1.2 2026.07.30
  1. Updated camera data acquisition instructions
V2.2.11 or later

1 Large Model API Interface

1.1 Built-in Models

Model Description
qwen2.5:3b Developed by Alibaba Cloud, the Qwen 2.5 series model features 3 billion parameters, offering robust language understanding and generation capabilities, suitable for text generation and dialogue interaction.
qwen2.5:1.5b A lighter 1.5 billion-parameter model from the Qwen 2.5 series, providing solid performance in resource-efficient scenarios with moderate computational demands.
qwen2.5:0.5b A lightweight 0.5 billion-parameter model optimized for terminal devices or environments with limited computational resources.
llama3.2:3b A 3 billion-parameter model from Meta’s LLaMA 3.2 series. It excels across a wide range of natural language processing tasks. Its open-source nature allows developers to perform secondary development and customization.
llama3.2:1b A 1 billion-parameter model from the LLaMA 3.2 series, offering faster training and inference speed due to its smaller model size.
deepseek-r1:1.5b Developed by DeepSeek, this 1.5 billion-parameter model delivers competitive performance across multiple language processing tasks.

1.2 Model invocation

Oli has pre-deployed the above large models locally via Ollama. To invoke them, connect your device to the same network as the robot and follow the instructions below.

1.2.1 Invoke via Curl

curl http://10.192.1.3:11434/api/generate \
  -H "Content-Type: application/json" \
  -d '{
        "model": "qwen2.5:3b",
        "prompt": "Please write a quatrain describing spring?",
        "temperature": 0.7,
        "max_tokens": 200,
        "stream": false
      }'

1.2.2 Invoke via Python

import requests

url = "http://10.192.1.3:11434/api/generate"
data = {
    "model": "qwen2.5:3b",
    "prompt": "Please write a quatrain describing spring?",
    "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 Invoke via C++

Using Ubuntu 20.04 or later as an example:

  • Install Dependencies
sudo apt-get install libcurl4-openssl-dev nlohmann-json3-dev 
  • Implement Code (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", "Please write a quatrain describing spring?"},
        {"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 and Run
# Compile with C++11 support
g++ -std=c++11 -o llm_demo llm_demo.cpp -lcurl

# Execute
./llm_demo

2 Communication Architecture

The diagram below illustrates the system composition and interaction between the developer's computer and the robot.

The development computer includes the motion control algorithm node and software business logic implementation module, which control the robot's motion via the high-level application protocol interface and limxsdk-lowlevel data communication interface.

The robot consists of a data switch, a main control computer, and various hardware components. The main control computer is responsible for coordinating the operation of all components and ensuring synchronized performance..

图片

3 High-Level Application Protocol Interface

The robot communicates with the user terminal through a WebSocket connection on port 5000 to receive user commands such as stand, sit, walk, and other motion instructions.

WebSocket is a real-time communication protocol that establishes a persistent connection between the robot and the user terminal, enabling fast and efficient transmission of control commands and data.

The communication structure is illustrated in the diagram below:

图片


3.1 Coordinate System Description

  • Unless otherwise specified, the position and orientation of both arm end-effectors are defined with respect to the robot’s base coordinate system.
  • The base coordinate system is defined for humanoid robots with serial numbers starting with “HU”.
  • The origin of the base coordinate system corresponds to the base_link defined in the URDF file. The coordinate system follows the right-hand rule (REP-103 standard).

3.2 Communication Protocol Format

When the robot receives commands from the client via WebSocket, data is transmitted using the JSON communication protocol.

3.2.1 Request Data

Fields in the request data format Description
accid The robot's unique serial number identifies its identity.
title The command name, prefixed with "request_".
timestamp The timestamp when the command is sent, in milliseconds.
guid A unique command identifier. For synchronous interfaces, the "response_xxx" message includes the same guid, allowing the client to confirm command completion by matching values
data Contains the content of the request. Depending on the command, it may include multiple sub-fields, such as parameters for motion execution, text content for messaging, or other required data.

Request Data Example:

{
  "accid": "HU_D02_001", # Robot’s unique serial number
  "title": "request_xxx",   # Command name, prefixed with "request_"
  "timestamp": 1672373633989, # Timestamp (in milliseconds) when the response was generated.
  "guid": "746d937cd8094f6a98c9577aaf213d98", # Unique identifier for the request. Used to match the response for synchronous operations
  "data": {}  # Contains command-specific parameters
}

3.2.2 Response Data

Fields in the response data format Description
accid The robot's unique serial number identifies its identity.
title The command name, prefixed with "response_".
timestamp The timestamp when the command is issued, in milliseconds.
guid Matches the guid value from the corresponding request command.
data The response data must include at least a "result" subfield to store the request's execution result. Additional subfields, such as error code or error message, may be included as needed to provide details about the operation outcome.

Response Data Example:

{
  "accid": "HU_D02_001",   # Robot’s unique serial number
  "title": "response_xxx",  # Command name, prefixed with "response_"
  "timestamp": 1672373633989, # Timestamp (in milliseconds) when the response was generated.
  "guid": "746d937cd8094f6a98c9577aaf213d98", # Must match the guid value from the corresponding request command.
  "data": { # Used to store the specific data content of the response command.
    "result": "success"  # “result” Indicates whether the request was processed successfully, the value can be: "success or fail_xxx"
  }
}

3.2.3 Message Push

The message push process refers to the robot actively sending information to the client. These messages may include the robot’s serial number, current operational status, executed actions, and other relevant data.

By providing real-time updates, the robot enables the client to understand its working status better and make more effective use of its services.

Fields in the message push format Description
accid The robot's unique serial number identifies its identity.
title The command name, prefixed with "notify_".
timestamp The message timestamp, in milliseconds.
guid The unique identifier of the message.
data Contains the message data. May include multiple subfields depending on the specific requirements of the notification.

Message Push Example:

{
  "accid": "HU_D02_001",   # Robot’s unique serial number
  "title": "notify_xxx",  # notification command name, prefixed with "notify_"
  "timestamp": 1672373633989, # The time the message was sent, in milliseconds.
  "guid": "746d937cd8094f6a98c9577aaf213d98", # The unique identifier for this notification message.
  "data": { } # Contains detailed status data
}

3.3 Communication Testing Method

Postman is a popular API development environment that can be used to test WebSocket interfaces.

Steps to Test the WebSocket Interface Using Postman:

  1. Install Postman: Download address: https://www.postman.com/downloads/
  2. Create a WebSocket Request: Launch Postman and create a new WebSocket request.
  3. Connect to the Robot’s Wi-Fi Network: After the robot powers on successfully, connect your computer to the robot’s Wi-Fi network. The network name typically follows the format:「HU_D02_xxx」
  4. Enter the Wi-Fi password: 12345678
  5. Enter the WebSocket URL: In the request URL field, input the robot’s WebSocket address, e.g.:
    "ws://10.192.1.2:5000"
  6. Input the Command Request: In the “Message” field, enter the JSON-formatted command request.
  7. Send the Command: Click “Send” to transmit the request to the robot.
  8. View the Response: After sending the command, the robot will return a response message. Review the response in Postman’s output window to verify whether the result matches the expected outcome.

图片

3.4 Basic Function Protocol Interfaces

3.4.1 Connect to Wi-Fi Hotspot

This protocol is used to send a request to the robot router to connect to a specified Wi-Fi SSID and return the connection result. Supported robot versions: v2.1.3 and later.

3.4.1.1 Request: request_connect_wifi

{
  "accid": "HU_D04_01_001",
  "title": "request_connect_wifi",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": { 
      "wifi_band": "0",  # WiFi band:0=5GHz,1=2.4GHz
      "wifi_ssid": "Limx-Guests",  # Target Wi-Fi SSID (network name) — case-sensitive and must match the actual hotspot name
      "wifi_password": "LimX2024",  # Target Wi-Fi password — password for a WPA2-PSK–encrypted network
      "router_admin_password": "12345678"  # Robot router administrator password
  }
}

3.4.1.2 Response: 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
                           # fail_no_wifi_password
                           # fail_no_router_admin_password
  }
}

3.4.1.3 Message Push: none

3.4.2 Query Wi-Fi Connection Status

This protocol allows the client to query the Wi-Fi connection status of the robot router. The system returns the SSID, signal strength, and connection status for real-time monitoring.

3.4.2.1 Request: request_wifi_connection_status

{
  "accid": "HU_D04_01_001",
  "title": "request_wifi_connection_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "router_admin_password": "12345678"  # Robot router administrator password
  }
}

3.4.2.2 Response: response_wifi_connection_status

The router returns the current Wi-Fi status for client-side parsing and real-time display.

{
  "accid": "HU_D04_01_001",
  "title": "response_wifi_connection_status",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "ssid": "Limx-Guests",
      "signal": -56,        # Signal strength (dBm)
      "result": "success"   # success
                            # fail_disconnected
  }
}

3.4.2.3 Message Push: none

3.4.3 Enter Ready State

The robot slowly moves into a ready position.

3.4.3.1 Request: request_prepare

Controls the robot to enter the "Standing State", enabling it to receive velocity commands for walking control.

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

3.4.3.2 Response: response_prepare

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

3.4.3.3 Message Push: none

3.4.4 Control Robot Walking

3.4.4.1 Enter Walking Mode

Sets the robot to Walking Mode, enabling it to receive velocity commands.

3.4.4.1.1 Request: 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: response_set_walk_mode
{
  "accid": "HU_D04_01_001",
  "title": "response_set_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  # success, fail_motor: Motor Error
  }
}
3.4.4.1.3 Message Push: none

3.4.4.2 Control Robot Walking

In Motion Operation Mode, use this protocol to control the robot’s walking.
Note: This interface is invalid in Full-Body Operation Mode.

3.4.4.2.1 Request: request_set_walk_vel
{
  "accid": "HU_D04_01_001",
  "title": "request_set_walk_vel",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.0,   #  Forward/Backwards Speed Ratio, range[-1, 1]
    "y": 0.0,   #  Lateral Speed Ratio, range[-1, 1]
    "yaw": 0.0  #  Rotational Angular Velocity Ratio, range[-1, 1]
  }
}
3.4.4.2.2 Response: response_set_walk_vel

Returned only if the command execution fails; no response on success.

{
  "accid": "HU_D04_01_001",
  "title": "response_set_walk_vel",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "fail_motor"  # fail_imu: IMU Error, fail_motor: Motor Error
  }
}
3.4.4.2.3 Message Push: none

3.4.5 Enter Damping State

All motors stop active control and exhibit resistance when moved.

3.4.5.1 Request: request_damping

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

3.4.5.2 Response: response_damping

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

3.4.5.3 Message Push: none

3.4.6 Enter Zero-Torque State

All motors stop active control and move freely without resistance.

3.4.6.1 Request: request_zero_torque

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

3.4.6.2 Response: response_zero_torque

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

3.4.6.3 Message Push: none

3.4.7 Enter Sitting Command

3.4.7.1 Request: 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: 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 Message Push: none

3.4.8 Enter Standing Command

Notes:
Interface Function: Starts robot operation. After the robot is powered on, calling this interface transitions the robot into the standing state.
Parameter mode: lying — robot is lying on the ground, hanging — robot is suspended, sit — robot is sitting
Return: Returns after the robot has successfully stood up.

3.4.8.1 Request: request_standup

{
  "accid": "HU_D04_01_001",
  "title": "request_standup",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": "lying" // "lying"/"sitting":The robot is currently lying or sitting.  or 
                      // "hanging":The robot is currently hanging.
                      // If the "mode" field is not provided, the robot state defaults to "sitting".
}

3.4.8.2 Response: response_standup

{
  "accid": "HU_D04_01_001",
  "title": "response_standup",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor: Motor error
                          # fail_invalid_cmd: Invalid parameter
                          # fail_invalid_mode: Invalid robot state
                          # fail_timeout: Execution timeout error
  }
}

3.4.8.3 Message Push: none

3.4.9 Enter Lying Command

3.4.9.1 Request: request_lie_down

This interface is available when the robot is in the Walk state.

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

3.4.9.2 Response: 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 Message Push: none

3.4.10 Zero-Point Calibration Command

3.4.10.1 Request: request_calibrate

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

3.4.10.2 Response: response_calibrate

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

3.4.10.3 Message Push: notify_calibrate

Pushed after calibration is completed.

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

3.4.11 Robot Dance

3.4.11.1 Switch to Dance Mode

3.4.11.1.1 Request: request_enter_dance_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_enter_dance_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 0:exit dance mode
    # 1:enter dance mode
    "mode": 0
  }
}
3.4.11.1.2 Response: 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 Message Push: none

3.4.11.2 Get Dance List

3.4.11.2.1 Request: 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: 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 Robot Dancing

3.4.11.3.1 Request: request_dance

Notes:

  1. Precondition: The robot must be in Action Library Mode.
  2. Supported on: Main controller firmware v2.1.21 and later.
{
  "accid": "HU_D04_01_001",
  "title": "request_dance",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "name": "one_and_only_dance"  # rc_mapping Dance name
  }
}
3.4.11.3.2 Response: response_dance
{
  "accid": "HU_D04_01_001",
  "title": "response_set_motion_engine",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}
3.4.11.3.3 Message Push: notify_dance

Pushed when the dance completes or execution fails.

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

3.4.12 Robot Marching in Place

3.4.12.1 Enable March-in-Place

3.4.12.1.1 Request: request_start_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "request_start_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.12.1.2 Response: 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 Message Push: notify_dance

3.4.12.2 Stop March-in-Place

3.4.12.2.1 Request: request_stop_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "request_stop_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
3.4.12.2.2 Response: response_stop_walktoggle
{
  "accid": "HU_D04_01_001",
  "title": "response_stop_walktoggle",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success" # fail_motor
  }
}

3.4.13 Robot Action Library

3.4.13.1 Action Interruption

3.4.13.1.1 Request: 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: 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 Get Action Library Status

Interface Description:

  1. When the robot has entered the Action Library, and is in Action Library Mode, Atomic Action Execution, or Dance, the following field is returned: "action_library_mode": "action_library"
  2. When the robot is executing an atomic action or a dance routine, the following field is returned: "action_library_state": "running"
3.4.13.2.1 Request: 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: 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 Execute Action Library

Interface Description:

  1. If not already in Menu mode, it will automatically enter Menu mode. After the action finishes, the system will remain in Menu mode.
  2. Must be used together with the action_library_state field from request_get_action_library_status.
3.4.13.3.1 Request: 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: 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 Execute Action Library (Synchronous Interface)

Notes:
Interface Description:

  1. If the robot is in a state where the Action Library cannot be executed, a failure response is returned within 100ms.
  2. If the robot is in a state where the Action Library can be executed (walk/motion library), the response is returned after the action library execution is completed.
  3. Synchronous interface, automatically transitions back to the Walk state. Completion is indicated when the system returns to the Walk state.
3.4.13.4.1 Request: 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:Multiple dances, multiple actions, or a mix of dances and actions, separated by commas (,).
  }
}
3.4.13.4.2 Response: 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 Switching the Robot to Action Library Mode

3.4.13.5.1 Request: request_set_motion_engine
{
  "accid": "HU_D04_01_001",
  "title": "request_set_motion_engine",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # 0:Exit Motion Library Mode
    # 1:Enter Motion Library Mode
    "mode": 0
  }
}
3.4.13.5.2 Response: 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 Message Push: none

3.4.13.6 Get Action Library List

3.4.13.6.1 Request: 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: 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_en": "stand"
            },
            {
                "motion_index": 1,
                "motion_name_en": "this_way_please"
            }
            ......
        ],
        "count": 2
    }
}

3.4.13.7 Executing Actions

Actions can be executed once the robot is in Action Library Mode.

3.4.13.7.1 Request: request_execute_atomic_motion
{
  "accid": "HU_D04_01_001",
  "title": "request_execute_atomic_motion",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # Motion Name
    "motion_name": "wave_greet_bye"
  }
}
3.4.13.7.2 Response: 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 Message Push: notify_execute_atomic_motion

Pushed when an action is completed, or execution fails.

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

3.4.14 Motion Control

3.4.14.1 Switching to Motion Operation Mode

In Motion Operation Mode, walking can be controlled, while height and waist movements remain disabled.

图片

3.4.14.1.1 Request: request_set_ub_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_ub_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": 0  # 0: Preparing to enter Mode 1: Operation Mode, start tracking end-effector position 2: Ready to Exit Mode
  }
}
3.4.14.1.2 Response: 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 Message Push: none

3.4.14.2 Motion Operation Control

Activated only via the request_set_ub_manip_mode interface.

  • Reference Coordinate Frame Diagram:
    • Position: The origin of base_link (located at the center beneath the hip).
    • Axis Definitions:
      • Red (X-axis): Forward direction of the robot
      • Green (Y-axis): Positive direction to the left
      • Blue (Z-axis): Vertical upward direction
Reference coordinate frame diagram 1 Reference coordinate frame diagram 2
3.4.14.2.1 Request: request_set_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_set_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # Coordinate System Definition Reference:
      # Origin:base_link coordinate frame
      # Axes: X-axis aligned with the robot’s forward direction, Y-axis pointing to the robot’s left, Z-axis pointing upward
      #
      # Parameter Definition:
      # Head orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
      "head_quat": [0.0, 0.0, 0.0, 1.0],
    
      # Left-hand position relative to the reference frame, in meters
      "left_hand_pos": [0.0, 0.0, 0.0],
      
      # Left-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
      "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
      # Right-hand position relative to the reference frame, in meters
      "right_hand_pos": [0.0, 0.0, 0.0],
      
      # Right-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
      "right_hand_quat": [0.0, 0.0, 0.0, 1.0]
  }
}
3.4.14.2.2 Response: 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: Invalid Command
  }
}
3.4.14.2.3 Message Push: none

3.4.14.3 Get Motion Operation Pose Information

3.4.14.3.1 Request: 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: response_get_ub_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_get_ub_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # Coordinate System Definition Reference:
    # Origin:base_link coordinate frame
    # Axes: X-axis aligned with the robot’s forward direction, Y-axis pointing to the robot’s left, Z-axis pointing upward
    #
    # Parameter Definition:
    # Head position relative to the reference frame, in meters
    "head_pos": [0.0, 0.0, 0.0],
      
    # Head orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
    "head_quat": [0.0, 0.0, 0.0, 1.0],
    
    # Left hand position relative to the reference frame, in meters.
    "left_hand_pos": [0.0, 0.0, 0.0],
      
    # Left-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
    "left_hand_quat": [0.0, 0.0, 0.0,1.0],
      
    # Right-hand position relative to the reference frame, in meters.
    "right_hand_pos": [0.0, 0.0, 0.0],
      
    # Right-hand orientation relative to the reference frame, represented as a quaternion[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 In-Place Operation

3.4.15.1 Enter In-Place Operation Mode

In this mode, you can control the robot's body movements while walking control is disabled.

3.4.15.1.1 Request: request_set_wb_manip_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_wb_manip_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "mode": 0  # 0: Preparing to enter Mode 1: Operation Mode, start tracking end-effector position 2: Ready to Exit Mode
  }
}
3.4.15.1.2 Response: 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 Message Push: none

3.4.15.2 In-Place Operation Control

The function takes effect only when In-Place Operation Mode is activated via the protocol interface request_set_wb_manip_mode with mode set to 1.

  • Reference Coordinate Frame Diagram:
    • Position: Midpoint of the line connecting the origins of left_ankle_roll_link and right_ankle_roll_link
    • Orientation: The yaw angle is the midpoint of the yaw orientations of left_ankle_roll_link and right_ankle_roll_link
    • Axis Definitions:
      • Red (X-axis): Determined by the forward-facing direction of the robot’s feet
      • Green (Y-axis): Determined according to the right-hand coordinate system rule
      • Blue (Z-axis): Vertical upward direction
In-place operation coordinate frame diagram 1 In-place operation coordinate frame diagram 2
3.4.15.2.1 Request: request_set_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "request_set_wb_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # Reference Frame Definition:
      # Origin: The projection on the ground (z=0) of the midpoint between the left and right feet.
      # Axes: X-axis aligned with the robot’s forward direction, Y-axis pointing to the robot’s left, Z-axis pointing upward
      #
      # Parameter Definition:
      # Left hand position relative to the reference frame, in meters.
      "left_hand_pos": [0.0, 0.3, 0.8],
      
      # Left-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
      "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
      # Right-hand position relative to the reference frame, in meters.
      "right_hand_pos": [0.0, -0.3, 0.8],
      
      # Right-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
      "right_hand_quat": [0.0, 0.0, 0.0, 1.0]
  }
}
3.4.15.2.2 Response: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 Message Push: none

3.4.15.3 Get In-Place Operation Pose Information

3.4.15.3.1 Request: 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: response_get_wb_manip_ee_pose
{
  "accid": "HU_D04_01_001",
  "title": "response_get_wb_manip_ee_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    # Reference Frame Definition:
    # Origin: The projection on the ground (z=0) of the midpoint between the left and right feet.
    # Axes: X-axis aligned with the robot’s forward direction, Y-axis pointing to the robot’s left, Z-axis pointing upward
    #
    # Parameter Definition:
    # Left-hand position relative to the reference frame, in meters.
    "left_hand_pos": [0.0, 0.0, 0.0],
      
    # Left-hand orientation relative to the reference frame, represented as a quaternion[x,y,z,w]
    "left_hand_quat": [0.0, 0.0, 0.0, 1.0],
      
    # Right-hand position relative to the reference frame, in meters.
    "right_hand_pos": [0.0, 0.0, 0.0],
      
    # Right-hand orientation relative to the reference frame, represented as a quaternion[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 Message Push: none

3.4.16 Dual-Arm Coordinated Move Control

3.4.16.1 Switch to Move Control Mode

3.4.16.1.1 Request: request_set_move_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_move_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 0: Exit Move Control
      # 1: Mobile Move Mode
      # 2: In-place Move Mode
      "mode": 0 
  }
}
3.4.16.1.2 Response: 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 Message Push: none

3.4.16.2 MoveJ Control Command

3.4.16.2.1 Request: request_moveJ
{
  "accid": "HU_D04_01_001",
  "title": "request_moveJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # Joint position ranges are defined by the corresponding robot model's URDF file
      # Model File Download Address: https://github.com/limxdynamics/humanoid-description
      
      # If the following data are provided simultaneously, both arms will be controlled (target positions in radians)
      # Left Arm Joint Order:  
      # - "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 Arm Joint Order:  
      # - "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],
      
      # In In-place MoveJ Mode only, providing the following data will control torso posture
      "torso_height": 0,  # Body height ratio: range[-1, 1]
      "torso_pitch": 0,   # Pitch motion ratio: range[-1, 1]
      "torso_roll": 0,    # Roll motion ratio: range[-1, 1]
      "torso_yaw": 0,     # Yaw motion ratio: range[-1, 1]
      
      # If the following data are provided, head motion will be controlled
      "head_pitch": 0.0,  # pitch joint target position (radians)
      "head_yaw": 0.0,    # yaw joint target position (radians)
      
      "speed": 0.2  # Motion speed: range [0, 0.5] rad/s, controlling the motion speed of both arms
  }
}
3.4.16.2.2 Response: 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 Message Push: notify_moveJ

Execution completion or failure will trigger an active message push.

{
  "accid": "HU_D04_01_001",
  "title": "notify_moveJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success, fail_motor, fail_invalid_speed: invalid speed value
  }
}

3.4.16.3 MoveP Control Command

  • Reference Coordinate Frame Diagram:
    • Position: Origin of the waist_pitch_link.
    • Axis Definitions:
      • Red (X-axis): Forward direction of the robot
      • Green (Y-axis): Positive direction to the left
      • Blue (Z-axis): Vertical upward direction
MoveP coordinate frame diagram 1 MoveP coordinate frame diagram 2
3.4.16.3.1 Request: request_moveP
{
  "accid": "HU_D04_01_001",
  "title": "request_moveP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided simultaneously, both arms will be controlled
      "left_position": [0.0, 0.0, 0.0], # Target position of the left arm end-effector in meters, specified in the order x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # Target orientation of the left arm end-effector, represented as a quaternion(x, y, z, w)
      "right_position": [0.0, 0.0, 0.0], # Target position of the right arm end-effector in meters, specified in the order x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # Target orientation of the right arm end-effector, represented as a quaternion(x, y, z, w)
      
      # In In-place MoveP Mode only, providing the following data will control torso posture
      "torso_height": 0,  # Body height ratio: range[-1, 1]
      "torso_pitch": 0,   # Pitch motion ratio: range[-1, 1]
      "torso_roll": 0,    # Roll motion ratio: range[-1, 1]
      "torso_yaw": 0,     # Yaw motion ratio: range[-1, 1]
      
      # If the following data are provided simultaneously, the head will be controlled
      "head_pitch": 0.0,  # pitch joint target position (radians)
      "head_yaw": 0.0,    # yaw joint target position (radians)
      
      "speed": 0.2  # Motion speed: range [0, 0.5] rad/s, controlling the motion speed of both arms
  }
}
3.4.16.3.2 Response: 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 Message Push: notify_moveP

Execution completion or failure will trigger an active message push.

{
  "accid": "HU_D04_01_001",
  "title": "notify_moveP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  # success, fail_motor, fail_invalid_speed: invalid speed value
  }
}

3.4.16.4 Get Dual-Arm End-Effector Pose

3.4.16.4.1 Request: request_get_move_pose

This interface is used to obtain the end-effector pose information of both robot arms.

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

Upon receiving the request, the robot returns the current pose data of both arms.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_move_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      "left_position": [0.0, 0.0, 0.0], # The position of the left arm end-effector, in meters, ordered as x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # The orientation of the left arm end-effector, as a quaternion x, y, z, w
      "right_position": [0.0, 0.0, 0.0], # The position of the right arm end-effector, in meters, ordered as x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # The orientation of the right arm end-effector, as a quaternion x, y, z, w
      "result": "success"  # fail_not_data
  }
}

3.4.17 Dual-Arm Coordinated Servo Control

3.4.17.1 Switch to Servo Control Mode

3.4.17.1.1 Request: request_set_servo_mode
{
  "accid": "HU_D04_01_001",
  "title": "request_set_servo_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # 0: Exit Servo Control
      # 1: Mobile Servo Mode
      # 2: In-place Servo Mode
      "mode": 0
  }
}
3.4.17.1.2 Response: 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 Message Push: none

3.4.17.2 ServoJ Control Command

3.4.17.2.1 Request: request_servoJ

Recommend controlling the robotic arm at the control frequency of ≥500 Hz in real-time systems to ensure control performance and stability.

{
  "accid": "HU_D04_01_001",
  "title": "request_servoJ",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # Joint position ranges are defined by the corresponding robot model's URDF file
      # Model File Download Address: https://github.com/limxdynamics/humanoid-description
      
      # If the following data are provided simultaneously, both arms will be controlled (target positions in radians)
      # Left Arm Joint Order:  
      # - "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 Arm Joint Order:
      # - "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],
      
      # In In-place ServoJ Mode only, providing the following data will control torso posture
      "torso_height": 0,  # Body height ratio: range[-1, 1]
      "torso_pitch": 0,   # Pitch motion ratio: range[-1, 1]
      "torso_roll": 0,    # Roll motion ratio: range[-1, 1]
      "torso_yaw": 0,     # Yaw motion ratio: range[-1, 1]
      
      # If the following data are provided, head motion will be controlled
      "head_yaw": 0.0,    # yaw joint target position (radians)
      "head_pitch": 0.0  # pitch joint target position (radians)
  }
}
3.4.17.2.2 Response: none
3.4.17.2.3 Message Push: notify_servoJ

The server will send this message to indicate the failure reason when a ServoJ control operation fails.

{
  "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 Control Command

  • Reference Coordinate Frame Diagram:
    • Position: Origin of the waist_pitch_link.
    • Axis Definitions:
      • Red (X-axis): Forward direction of the robot
      • Green (Y-axis): Positive direction to the left
      • Blue (Z-axis): Vertical upward direction
ServoP coordinate frame diagram 1 ServoP coordinate frame diagram 2
3.4.17.3.1 Request: request_servoP

Recommend controlling the robotic arm at the control frequency of ≥500 Hz in real-time systems to ensure control performance and stability.

{
  "accid": "HU_D04_01_001",
  "title": "request_servoP",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided simultaneously, both arms will be controlled
      "left_position": [0.0, 0.0, 0.0], # Target position of the left arm end-effector in meters, specified in the order x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # Target orientation of the left arm end-effector, represented as a quaternion(x, y, z, w)
      "right_position": [0.0, 0.0, 0.0], # Target position of the right arm end-effector in meters, specified in the order x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0] # Target orientation of the right arm end-effector, represented as a quaternion(x, y, z, w)
      
      # In In-place ServoP Mode only, providing the following data will control torso posture
      "torso_height": 0,  # Body height ratio: range[-1, 1]
      "torso_pitch": 0,   # Pitch motion ratio: range[-1, 1]
      "torso_roll": 0,    # Roll motion ratio: range[-1, 1]
      "torso_yaw": 0,     # Yaw motion ratio: range[-1, 1]
      
      # If the following data are provided simultaneously, the head will be controlled
      "head_yaw": 0.0,    # yaw joint target position (radians)
      "head_pitch": 0.0  # picth joint target position (radians)
  }
}
3.4.17.3.2 Response: none
3.4.17.3.3 Message Push: notify_servoP

The server will send this message to indicate the failure reason when a ServoP control operation fails.

{
  "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 Get Dual-Arm End Pose

3.4.17.4.1 Request: request_get_servo_pose

Retrieve the end-effector pose information of both robot arms via this interface.

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

Upon receiving the request, the system returns the current pose data for both arms.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_servo_pose",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      "left_position": [0.0, 0.0, 0.0], # The position of the left arm end-effector, in meters, ordered as x, y, z
      "left_quat": [0.0, 0.0, 0.0, 1.0], # The orientation of the left arm end-effector, as a quaternion x, y, z, w
      "right_position": [0.0, 0.0, 0.0], # The position of the right arm end-effector, in meters, ordered as x, y, z
      "right_quat": [0.0, 0.0, 0.0, 1.0], # The orientation of the right arm end-effector, as a quaternion x, y, z, w
      "result": "success"  # fail_not_data
  }
}
3.4.17.4.3 Message Push: none

3.4.18 Robot Joints State

3.4.18.1 Request: request_get_joint_state

This request is used to retrieve the status of all robot joints.

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

3.4.18.2 Response: response_get_joint_state

Upon receiving the request, the system returns the current state of all joints.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_joint_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "names": [], # Names of each joint
      "q": [],     # Positions of each joint
      "dq": [],    # Velocities of each joint
      "tau": [],   # Torques of each joint
      "result": "success"  # fail_not_data
  }
}

3.4.18.3 Message Push: none

3.4.19 Get IMU Data

3.4.19.1 Request: request_get_imu_data

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

3.4.19.2 Response: 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],     // Euler Angles [roll, pitch, yaw] in degrees
      "acc": [0.0, 0.0, 0.0],       // Acceleration [x, y, z] in m/s²
      "gyro": [0.0, 0.0, 0.0],      // Gyroscope Angular Velocity [x, y, z] in rad/s
      "quat": [0.0, 0.0, 0.0, 0.0]  // Quaternion [w, x, y, z]
  }
}

3.4.19.3 Message Push: none

3.5 Dexterous Hand and Gripper Protocol Interface

3.5.1 LimX 2-Finger Gripper

3.5.1.1 Gripper Control Command

3.5.1.1.1 Request: request_set_limx_2fclaw_cmd

This protocol controls the gripper’s grasping actions.

{
  "accid": "HU_D04_01_001",
  "title": "request_set_limx_2fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided simultaneously, the left gripper will be controlled
      "left_opening": 50,  # Gripper Opening,0-100,dimensionless(0 represents fully closed,100 represents fully open)
      "left_speed": 50,    # Gripper Speed,0~100 dimensionless(higher values indicate faster motion)
      "left_force": 50,   # Gripper Force,0~100 dimensionless(higher values represent stronger force)
      
      # If the following data are provided simultaneously, the right gripper will be controlled
      "right_opening": 50,  # Gripper Opening,0-100,dimensionless(0 represents fully close,100 represents fully open大)
      "right_speed": 50,    # Gripper Spee,0~100 dimensionless(higher values indicate faster motion)
      "right_force": 50,   #Gripper Forc,0~100 dimensionless(higher values represent stronger force)
  }
}

3.5.1.1.2 Response: 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 Message Push: none

3.5.1.2 Get Gripper Status Information

3.5.1.2.1 Request: 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: response_get_limx_2fclaw_state

Return Gripper Status Information.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_limx_2fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      
      "left_opening": 50,  # Gripper Opening,0-100,dimensionless(0 represents fully close,100 represents fully open)
      "left_speed": 50,    # Gripper Speed,0~100 dimensionless(higher values indicate faster motion)
      "left_force": 50,   # Gripper Force,0~100 dimensionless(higher values represent stronger force)
      
      "right_opening": 50,  # Gripper Opening,0-100,dimensionless(0 represents fully close,100 represents fully open)
      "right_speed": 50,    # Gripper Speed,0~100 dimensionless(higher vahigher values represent stronger force)
      
      "result": "success"  # success, fail_motor
  }
}
3.5.1.2.3 Message Push: none

3.5.2 Limx 3-Finger Gripper

3.5.2.1 Gripper Control Command

3.5.2.1.1 Request: request_set_limx_3fclaw_cmd

This protocol controls the gripper’s grasping actions.

{
  "accid": "HU_D04_01_001",
  "title": "request_set_limx_3fclaw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided simultaneously, the left gripper will be controlled
      "left_left": 0,    # Left Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"left_middle": 0,  # Middle Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"left_right": 0,   # Right Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"left_lr_rot": 0,  # Finger Rotation Angle,0~180 degrees,(0 = fingers adjacent,180 = fingers opposed, default: 180)
      
      # If the following data are provided simultaneously, the right gripper will be controlled
      "right_left": 0,   # Left Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"right_middle": 0, # Middle Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"right_right": 0,  # Right Finger Bend Angle,0-100,dimensionless(0 = fully closed,100 = fully open,default: 42"right_lr_rot": 1  # Finger Rotation Angl,0~180 degrees,(0 = fingers adjacent,180 = fingers opposed,default: 180}
}

3.5.2.1.2 Response: 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 Message Push: none

3.5.2.2 Get Gripper Status Information

3.5.2.2.1 Request: 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: esponse_get_limx_3fclaw_state

Return Gripper Status Information.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_limx_3fclaw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      "left_left": 0,    # Left Gripper Left Finger Bend Angle,0-100, dimensionless(0 = fully closed,100 = fully open, default: 42"left_middle": 0,  # Left Gripper Middle Finger Bend Angle,0-100, dimensionless(0 = fully closed,100 = fully open, default: 42"left_right": 0,   # Left Gripper Right Finger Bend Angle,0-100, dimensionless(0 = fully closed,100 = fully open, default: 42"left_lr_rot": 0,  # Left Gripper Finger Rotation Angle,0~180 degrees,0 = fingers adjacent贴,180 = fingers opposed,default: 180"right_left": 0,   # Right Gripper Left Finger Bend Angle,0-100, dimensionless(0 = fully closed,100 = fully open, default: 42"right_middle": 0, # Right Gripper Middle Finger Bend Angle,0-100, dimensionless(0 = fully closed,100= fully open, default: 42"right_right": 0,  # Right Gripper Right Finger Bend Angle,0-100, dimensionless(0 = fully closed,100= fully open, default: 42"right_lr_rot": 1  # Right Gripper Finger Rotation Angle,0~180 degrees,(0 = fingers adjacent,180 = fingers opposed,default: 180"result": "success"  # success, fail_motor
  }
}
3.5.2.2.3 Message Push: none

3.5.3 Inspire 2-Finger Gripper

3.5.3.1 Gripper Control Command

3.5.3.1.1 Request: request_set_claw_cmd

This protocol controls the gripper’s grasping actions.

{
  "accid": "HU_D04_01_001",
  "title": "request_set_claw_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided simultaneously, the left gripper will be controlled
      "left_opening": 100, # Left Gripper Opening, range[0, 1000], dimensionless
      "left_speed": 500,   # Left Gripper Speed,range[0, 1000], dimensionless
      "left_force": 500,   # Left Gripper Force,range[0, 1000], dimensionless
      "left_mode": 1,      # Left Gripper Control Mode(1:Grip;2:Release;3: Position Control)
      
      # 如If the following data are provided simultaneously, the right gripper will be controlled
      "right_opening": 100, # Right Gripper Opening,range[0, 1000], dimensionless
      "right_speed": 500,   # Right Gripper Speed,range[0, 1000], dimensionless
      "right_force": 500,   # Right Gripper Force,range[0, 1000], dimensionless
      "right_mode": 1       # Right Gripper Control Mode(1:Grip;2:Release;3: Position Control)
  }
}
3.5.3.1.2 Response: 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 Message Push: none

3.5.3.2 Get Gripper Status Information

3.5.3.2.1 Request: 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: response_get_claw_state

Return Gripper Status Information.

{
  "accid": "HU_D04_01_001",
  "title": "response_get_claw_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      "left_opening": 100, # Left Gripper Opening, range[0, 1000], dimensionless
      "left_speed": 500,   # Left Gripper Speed,range[0, 1000], dimensionless
      "left_force": 500,   # Left Gripper Force, range[0, 1000], dimensionless
      
      "right_opening": 100, # Right Gripper Opening,range[0, 1000], dimensionless
      "right_speed": 500,   # Right Gripper Speed,range[0, 1000], dimensionless
      "right_force": 500,   # Right Gripper Force,range[0, 1000], dimensionless
      
      "result": "success"  # success, fail_motor
  }
}
3.5.3.2.3 Message Push: none

3.5.4 BrainCo's Revo 1 Dexterous Hand

3.5.4.1 Dexterous Hand Control Command

3.5.4.1.1 Request: request_set_brainco_hand_cmd
{
  "accid": "HU_D04_01_001",
  "title": "request_set_brainco_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # If the following data are provided, the left hand will be controlled
      "left_thumb": 50,       # Left Thumb Flexion Angle,0-100, dimensionless
      "left_thumb_aux": 50,   # Left Thumb Adduction Angle,0-100, dimensionless
      "left_index": 50,       # Left Index Finger Flexion Angle,0-100, dimensionless
      "left_middle": 50,      # Left Middle Finger Flexion Angle,0-100, dimensionless
      "left_ring": 50,        # Left Ring Finger Flexion Angle,0-100, dimensionless
      "left_pinky": 50,       # Left Little Finger Flexion Angle,0-100, dimensionless
      "left_mode": 3,         # Force Level 1:low 2:medium 3:high, default:2
      
      # If the following data are provided, the right hand will be controlled
      "right_thumb": 50,       # Right Thumb Flexion Angle,0-100, dimensionless
      "right_thumb_aux": 50,   # Right Thumb Adduction Angle,0-100, dimensionless
      "right_index": 50,       # Right Index Finger Flexion Angle,0-100, dimensionless
      "right_middle": 50,      # Right Middle Finger Flexion Angle,0-100, dimensionless
      "right_ring": 50,        # Right Ring Finger Flexion Angle,0-100, dimensionless
      "right_pinky": 50,       # Right Little Finger Flexion Angle,0-100, dimensionless
      "right_mode": 3          # Force Level 1:low 2:medium 3:high, default:2
  }
}
3.5.4.1.2 Response: 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 Message Push: none

3.5.4.2 Get Dexterous Hand Status

3.5.4.2.1 Request: 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: response_get_brainco_hand_state
{
  "accid": "HU_D04_01_001",
  "title": "response_get_brainco_hand_state",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "timestamp": 1672373633989, # Represents the data timestamp, in milliseconds
      "left_thumb": 50,       # Left Thumb Flexion Angle,0-100, dimensionless
      "left_thumb_aux": 50,   # Left Thumb Adduction Angle,0-100, dimensionless
      "left_index": 50,       # Left Index Finger Flexion Angle,0-100, dimensionless
      "left_middle": 50,      # Left Middle Finger Flexion Angle,0-100, dimensionless
      "left_ring": 50,        # Left Ring Finger Flexion Angle,0-100, dimensionless
      "left_pinky": 50,       # Left Little Finger Flexion Angle,0-100, dimensionless
      
      "right_thumb": 50,       # Right Thumb Flexion Angle,0-100, dimensionless
      "right_thumb_aux": 50,   # Right Thumb Adduction Angle,0-100, dimensionless
      "right_index": 50,       # Right Index Finger Flexion Angle,0-100, dimensionless
      "right_middle": 50,      # Right Middle Finger Flexion Angle,0-100, dimensionless
      "right_ring": 50,        # Right Ring Finger Flexion Angle,0-100, dimensionless
      "right_pinky": 50        # Right Little Finger Flexion Angle,0-100, dimensionless
      "result": "success"  # success, fail_motor
  }
}
3.5.4.2.3 Message Push: none

3.5.5 BrainCo's Revo 2 Dexterous Hand

3.5.5.1 Dexterous Hand Control Command

3.5.5.1.1 Request: request_set_brainco2_hand_cmd
{
  "accid": "HU_D04_01_001",
  "title": "request_set_brainco2_hand_cmd",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      # Left Hand:
      # Finger Index Mapping (05): 0: Thumb Tip, 1: Thumb Base, 2: Index Finger, 3: Middle Finger, 4: Ring Finger, 5: Little Finger
      # left_mode: Control mode
      #            0:Exit control mode
      #            1:Position–Time Mode, requires specifying left_pos and left_time
      #            2:Position–Velocity Mode, requires specifying left_pos and left_vel
      #            3:Force Control Mode, requires specifying left_current
      # left_pos: Target position of each finger (unit: rad)
      #           Respective Ranges: 0-1.02970-1.57070-1.41370-1.41370-1.41370-1.4137
      # left_vel: Target velocity of each finger (unit: rad/s)
      #           Respective Ranges: 0-2.53670-2.61800-2.26890-2.26890-2.26890-2.2689
      # left_current: Target current of each finger (unit: mA), range: ±1000mA
      # left_time: Control time for each finger (unit: ms), range: 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],
      
      # Right Hand:
      # Finger Index Mapping (05): 0: Thumb Tip, 1: Thumb Base, 2: Index Finger, 3: Middle Finger, 4: Ring Finger, 5: Little Finger
      # right_mode: Control mod
      #            0:Exit control mode
      #            1:Position–Time Mod, requires specifying right_pos and right_time
      #            2:Position–Velocity Mode, requires specifying right_pos and right_vel
      #            3:Force Control Mode, requires specifying right_current
      # right_pos: Target position of each finger (unit: rad)
      #           Respective Ranges: 0-1.02970-1.57070-1.41370-1.41370-1.41370-1.4137
      # right_vel: Target velocity of each finger (unit: rad/s)
      #           Respective Ranges: 0-2.53670-2.61800-2.26890-2.26890-2.26890-2.2689
      # right_current: Target current of each finger (unit: mA), range: ±1000mA
      # right_time: Control time for each finger (unit: ms), range: 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: 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: motor error, fail_invalid_cmd: invalid command
  }
}
3.5.5.1.3 Message Push: none

3.5.5.2 Get Dexterous Hand Status

3.5.5.2.1 Request: 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: 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 Message Push: none

3.6 Global Message Protocol Interface

3.6.1 Robot Status Information

This protocol periodically reports the robot’s status information.

Status Infomation Description
accid The robot's unique serial number.
title notify_robot_info
timestamp The timestamp when the command is sent, in milliseconds.
guid The unique identifier of the message.
data Contains the message data.

Example:

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

3.6.1.1 Battery Data

{
  "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"
                    }
                ]
            },
    ]
  }
}
Field Description
bmsconn Battery connection status: OFF = not connected, ON = connected
bat_chg Battery charger status: OFF = not connected, ON = connected
bat_off Battery pre-shutdown status: OFF = power will be cut off after 1 s, ON = normal
bat_prt Battery fault code: 0 = normal, non-zero = fault
bat_vol Real-time battery voltage (unit: mV)
bat_cur Real-time battery current (unit: mA)
battery Battery level percentage (0–100)
bat_temp0 Battery temperature sensor 0 (range: 0–100, unit: ×10 °C)
bat_temp2 Battery temperature sensor 2 (range: 0–100, unit: ×10 °C)
bat_temp4 Battery temperature sensor 4 (range: 0–100, unit: ×10 °C)

3.6.1.2 System Information

{
  "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"
                }
              ]
        }
    ]
  }
}
Field Description
version Main controller firmware version
ecm_version Master station version
pms_version Power distribution board version
motor_version Motor firmware version
sn Robot serial number
robot_status Current robot status
ability_running Currently active controller

3.6.1.3 Motor Status Information

{
  "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!"
                    }
                ]
          }
    ]
  }
}
Field Description
level Fault severity level: 0 = OK, 1 = Warning, 2 = Error
name Fault type
message Severity description string
hardware_id Hardware ID
values Collection of all fault entries for the specified hardware

3.6.2 The Remote Controller Data

This protocol reports the robot’s remote controller data.

Data Information Description
accid The robot's unique serial number.
title notify_joy_data
timestamp The timestamp when the command is sent, in milliseconds.
guid The unique identifier of the message.
data Contains the message data.

Example:

{
  "accid": "HU_D04_01_001", 
  "title": "notify_joy_data", 
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "axes": [],     # joystick data
    "buttons": []   # button data
  }
}

3.7 Protocol Interface Usage Example

3.7.1 Python Example

  • Environment Setup: using Ubuntu 20.04 as an example, install the required dependencies
sudo apt install python3-dev python3-pip
sudo pip install websocket-client==1.8.0
  • Execute the Script
python humanoid.py
  • humanoid.py Implementation
    • ACCID: Replace with the actual software serial number (SN).
    • ROBOT_IP: Usually, use 127.0.0.1 for simulation and 10.192.1.2 for real hardware.
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++ Example

  • Environment Setup: using Ubuntu 20.04 as an example, install websocketpp, nlohmann/json and boost dependencies:
sudo apt-get install libboost-all-dev libwebsocketpp-dev nlohmann-json3-dev
  • Compile the Code
g++ -std=c++11 humanoid.cpp -o humanoid -lssl -lcrypto -lboost_system -lpthread
  • Execute the Program
./humanoid
  • humanoid.cpp Implementation
#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', '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 Example

  • Execute humanoid.html: Save the humanoid.html file to your computer and open it in a browser to run the demo.

图片

  • humanoid.html Implementation
<!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;
                            
                            // Attempt to parse the input as an integer
                            const n = parseInt(modeInput, 10);
                            if (!Number.isNaN(n) && (n === 0 || n === 1 || n === 2)) {
                              modeValue = n;
                            } else {
                              // If the input is invalid, return an error
                              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 Low-Level Motion Control Development Interface

The cross-platform low-level motion control API provides a unified C++/Python interface, compatible with ROS1, ROS2, and non-ROS systems, enabling rapid migration and deployment of motion control algorithms. Through a hardware abstraction layer and standardized communication protocols, developers can seamlessly switch between simulation and real hardware environments, significantly reducing multi-platform adaptation costs.

Notes:

  1. To use the low-level control API, press R1 + START to switch to Developer Mode. In this mode, high-level control interfaces are disabled, and the robot only responds to power-on/power-off and zeroing remote controller commands.
  2. Example code for the low-level interface can be found in the RL deployment and training section.
  3. When switched to Developer Mode, the robot will retain the power state. To exit Developer Mode, press L2 + ○.

4.1 C++ Motion Control Development Interface

4.1.1 getInstance Interface

Function Name getInstance
Function Prototype static Humanoid* getInstance();
Description Retrieves the singleton instance pointer of the Humanoid robot class.
Parameters None
Return Value Humanoid*, Pointer to the Humanoid instance
Remarks Implements the singleton pattern to ensure only one instance of the Humanoid class exists in the program.

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;

int main(int argc, char *argv[]){
  //obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }
   
  return 0;
}

4.1.2 init Interface

Function Name init
Function Prototype bool init(const std::string& robot_ip_address = "127.0.0.1");
Description Initializes the communication and runtime environment for the motion control algorithm, typically called before other interfaces in the main function.
Parameters robot_ip_address: The IP address of the robot. Use "127.0.0.1" for simulation and "10.192.1.2" for real robots.
Return Value true if initialization succeeds; otherwise false.
Remarks None

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.3 getMotorNumber Interface

Function Name getMotorNumber
Function Prototype uint32_t getMotorNumber();
Description Retrieves the motor number in the robot.
Parameters None
Return Value Returns an unsigned integer representing the total number of motors in the robot.
Remarks None

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Obtain the motor number of the robot
  uint32_t motor_num = robot->getMotorNumber();
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.4 subscribeImuData Interface

Function Name subscribeImuData
Function Prototype void subscribeImuData(std::function<void(const ImuDataConstPtr&)> cb);
Description Subscribes to the robot’s IMU data and triggers the specified callback function whenever new IMU data is received.
Parameters cb: Callback function to process the incoming IMU data.
Return Value None
Remarks - IMU Data structure prototype defined as follows:
/**
 * @struct ImuData
 *
 * @brief Represents a data structure for robot IMU information based on sensor feedback.
 *
 * This structure encapsulates IMU data, including the accelerometer, gyroscope, and quaternion information.
 */
struct ImuData {
  uint64_t stamp; // Timestamp: Recorded in nanoseconds, indicating the time at which the data was recorded or generated.
  float acc[3];   // Stores IMU accelerometer data to track linear acceleration along the X, Y, and Z axes.
  float gyro[3];  // Stores IMU gyroscope data to track angular velocity (rotational speed) along the X, Y, and Z axes.
  float quat[4];  // Stores IMU quaternion values representing the robot’s orientation in 3D space (w, x, y, z).
};

// Smart Pointer Type Alias
typedef std::shared_ptr<ImuData> ImuDataPtr;
typedef std::shared_ptr<ImuData const> ImuDataConstPtr;

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Subscribe to robot state updates and specify a callback function
  robot->subscribeImuData([&](const ImuDataConstPtr& msg) {
    // Handle the received ImuData within this callback
    // Note: The callback function is invoked when new ImuData is received
  });
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.5 subscribeRobotState Interface

Function Name subscribeRobotState
Function Prototype void subscribeRobotState(std::function<void(const RobotStateConstPtr&)> cb);
Description Subscribes to receive updates on robots' status.

Notes: This interface reports joint states based on the robot’s equivalent serial URDF.

This robot adopts a series-parallel hybrid configuration design, with parallel drive structures used at some joints. Unlike traditional serial joints (where a joint is directly driven by a single actuator), parallel joints have the following characteristics:
- Multiple actuators collaboratively driving a single joint DOF
- Higher load capacity, stiffness, and dynamic performance
- More compact mechanical design with higher torque density
- Improved fault tolerance through actuator redundancy

While parallel actuation offers significant mechanical advantages, it also introduces challenges for control and application development:
- More complex kinematic and dynamic modeling
- Incompatibility with conventional serial robot control algorithms
- Higher learning curve due to parallel mechanism theory
- Limited compatibility with existing robotics ecosystems (e.g., ROS, MoveIt!)

To eliminate these complexities, the robot controller transparently maps parallel joints to an equivalent serial joint model, allowing upper-layer applications to interact with the robot as if it were a standard serial manipulator.

State Feedback:
Parallel actuator states (position, velocity, current/torque) → Equivalent serial joint computation → Equivalent serial joint states (position, velocity, torque)
Parameters cb: callback function, invoked upon receiving a state update, with a constant pointer to a RobotState object as its parameter.
Return Value None
Remarks - RobotState data structure prototype defined as follows:
/**
 * @struct RobotState
 *
 * @brief Represents a data structure for robot state based on sensor feedback.
 *
 * This structure encapsulates various data points used for monitoring and controlling the robot, including IMU data (accelerometer, gyroscope, quaternion), output torques, current joint angles, velocities, and more.
 */
struct RobotState {
  // Default constructor
  RobotState() { } 
  // Parameterized constructor, initializes vectors tau, q, and dq with size motor_num, all elements set to 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;              // Timestamp (in nanoseconds), typically indicates the time at which the data was recorded or generated.
  std::vector<float> tau;      //  A vector for storing the current estimated output torques of the joints (unit: N·m).
  std::vector<float> q;        // A vector for storing the current joint angles (unit: radians)
  std::vector<float> dq;       // A vector for storing the current joint velocities (unit: radians per second)
   std::vector<std::string> motor_names; // A vector for storing the names of all joints.
};

// Smart Pointer Type Alias
typedef std::shared_ptr<RobotState> RobotStatePtr;
typedef std::shared_ptr<RobotState const> RobotStateConstPtr;

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Subscribe to robot state updates and specify a callback function
  robot->subscribeRobotState([&](const RobotStateConstPtr& msg) {
    // Handle the received ImuData within this callback
    // Note: The callback function is invoked when new ImuData is received
  });
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.6 publishRobotCmd Interface

Function Name publishRobotCmd
Function Prototype bool publishRobotCmd(const RobotCmd& cmd);
Description Publishes a command to control the robot’s actions.

Notes: This interface accepts joint commands for the robot’s equivalent serial URDF model.

The robot adopts a serial–parallel hybrid architecture, with parallel drive structures used at some joints. Unlike conventional serial joints, where each joint is driven by a single actuator, parallel joints have the following characteristics:
- Multiple actuators collaboratively drive a single joint DOF.
- Higher load capacity, stiffness, and dynamic performance.
- More compact mechanical design with higher joint torque output.
- Improved fault tolerance through actuator redundancy.

Although parallel joints offer significant mechanical advantages, they also introduce challenges for control and application development:
- More complex kinematic and dynamic models.
- Conventional serial robot control algorithms and toolchains cannot be applied directly.
- Higher learning curve due to the complexity of parallel mechanisms.
- Limited compatibility with existing robotics ecosystems (e.g., ROS and MoveIt!).

To address these challenges, the robot controller performs a transparent conversion from parallel joints to an equivalent serial joint model, completely hiding the complexity of the parallel mechanisms from upper-layer applications.

Command Flow:
Equivalent serial joint commands (position, velocity, torque) → Parallel actuator command computation → Actuator command execution

Note:
Since Kp and Kd gains cannot be efficiently converted to parallel actuator commands, users requiring force control are recommended to command joint torque (tau) directly instead of achieving force control indirectly through PD control. This is particularly relevant for reinforcement learning policies that output position or velocity actions, where the controller converts PD commands into torque commands internally.
Parameters cmd: a RobotCmd object specifying the desired robot command
Return Value None
Remarks - RobotCmd data structure prototype defined as follows:
/**
 * @struct RobotCmd
 *
 * @brief Represents a data structure for robot state based on sensor feedback.
 *
 * This structure contains various commands that can be used to control the robot, including the desired operating mode, target joint angles, target velocities, target output torques, desired position stiffness, and desired velocity stiffness.
 */
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;             // Timestamp (in nanoseconds), typically indicates the time at which the data was recorded or generated.
  std::vector<uint8_t> mode;  // 0: Torque control mode;1:Velocity control mode;2:Position control mode,default setting:0
  std::vector<float> q;       // A vector for storing desired joint angles (unit: radians)
  std::vector<float> dq;      // A vector for storing desired joint velocities (unit: radians per second)
  std::vector<float> tau;     // A vector for storing desired output torques (unit: N·m)
  std::vector<float> Kp;      // A vector for storing desired position stiffness values (unit: N·m per radian)
  std::vector<float> Kd;      // A vector for storing desired velocity stiffness values (unit: N·m per radian per second)
  std::vector<std::string> motor_names;   // Stores the names of the robot joints to be controlled
};

// Smart Pointer Type Alias     
typedef std::shared_ptr<RobotCmd> RobotCmdPtr;
typedef std::shared_ptr<RobotCmd const> RobotCmdConstPtr;  

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Obtain the motor number of the robot
  uint32_t motor_num = robot->getMotorNumber();
  
  // Create a RobotCmd object that includes the number of robot motors
  RobotCmd cmd(motor_num);
  
  // Publish the control command
  robot->publishRobotCmd(cmd);
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.7 subscribeSensorJoy Interface

Function Name subscribeSensorJoy
Function Prototype void subscribeSensorJoy(std::function<void(const SensorJoyConstPtr&)> cb);
Description Subscribes to the robot’s remote controller data during real-machine deployment. When data is received, the specified callback function is invoked and provided with a constant pointer to a SensorJoy structure for processing.
Parameters cb: Callback Function for receiving remote controller data. The parameter type is SensorJoyConstPtr, a shared pointer to a constant SensorJoy structure.
Return Value None
Remarks - SensorJoy data structure prototype defined as follows:
/**
 * @struct SensorJoy
 *
 * @brief The structure of the robot remote controller data
 *
 * This structure contains timestamp information related to the controller, as well as joystick and button values.
 */
struct SensorJoy {
    uint64_t stamp;                     // Timestamp corresponding to the sensor input time, in nanoseconds
    std::vector<float> axes;            //  Values representing controller joystick manipulation
    std::vector<int32_t> buttons;       // Values representing controller button operation states
};       

// SensorJoy Smart Pointer Type Alias
typedef std::shared_ptr<SensorJoy> SensorJoyPtr;
typedef std::shared_ptr<const SensorJoy> SensorJoyConstPtr;

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Subscribe to the remote controller data of the robot
  robot->subscribeSensorJoy([&](const limxsdk::SensorJoyConstPtr &joy) {
    // L1 & R1 press
    if (joy->buttons[4] == 1 && joy->buttons[7] == 1)
    {
      // Perform the corresponding operations here
    }

    // Process the joystick data
    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];
  });
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.8 subscribeDiagnosticValue Interface

Function Name subscribeDiagnosticValue
Function Prototype void subscribeDiagnosticValue(std::function<void(const DiagnosticValueConstPtr&)> cb);
Description Subscribes to the robots' diagnostic value and status information. When a diagnostic message is issued, the specified callback function is triggered and receives a constant pointer to a DiagnosticValue structure, enabling real-time monitoring of the robot’s health status and allowing timely handling of potential issues.
Parameters cb: Callback function for receiving diagnostic data. The parameter type is DiagnosticValueConstPtr, a shared pointer to a constant DiagnosticValue structure containing fields such as timestamp, level, name, code, and message.
Return Value None
Remarks - DiagnosticValue data structure prototype defined as follows:
/**
 * @struct DiagnosticValue
 *
 * @brief Structure representing diagnostic values
 *
 * This structure contains information about the diagnostic level, name, code, and message.
 */
struct DiagnosticValue {
  enum { OK = 0 };         // Diagnostic level for normal status
  enum { WARN = 1 };       // Diagnostic level for warning status
  enum { ERROR = 2 };      // Diagnostic level for error status

  uint64_t stamp;          // Timestamp, in nanoseconds
  int32_t level;           // Diagnostic level associated with the diagnostic value
  std::string name;        // Name identifying the diagnostic value
  int32_t code;            // Code corresponding to the diagnostic value
  std::string message;     // Detailed message related to the diagnostic value
};

// DiagnosticValue Smart pointer type definition
typedef std::shared_ptr<DiagnosticValue> DiagnosticValuePtr;
typedef std::shared_ptr<DiagnosticValue const> DiagnosticValueConstPtr;

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication environment for the motion control algorithm program
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Subscribe to robot diagnostic data
  robot->subscribeDiagnosticValue([&](const DiagnosticValueConstPtr& msg) {
    // Handle the robot diagnostic value here
    // For example, actions can be taken based on the diagnostic level and message
    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;
  });
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.9 publishJsonMessage Interface

Function Name publishJsonMessage
Function Prototype void publishJsonMessage(const std::string &json_payload);
Description Sends a JSON-formatted message to the robot following the High-Level Application Protocol Interface.
Parameters JSON_payload: A JSON string conforming to the High-Level Application Protocol specification. Example: {"accid": "xxx", "title": "request_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
Return Value None
Remarks Valid only in high-level development mode.

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "10.192.1.2";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication runtime environment
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
   // The JSON protocol content to be sent
  std::string json_payload = R"({
    "accid": "HU_D03_01",   # Replace with the real robot serial number (SN)
    "title": "request_get_joint_state",
    "timestamp": 1672373633989,
    "guid": "746d937cd8094f6a98c9577aaf213d98",
    "data": {}
  })";
  
  // Publish control command
  robot->publishJsonMessage(json_payload);
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.10 subscribeJsonMessage Interface

Function Name subscribeJsonMessage
Function Prototype void subscribeJsonMessage(std::function<void(const std::string &)> cb);
Description Registers a callback function to handle responses and notifications from the robot’s High-Level Application Protocol Interface.

The callback is triggered in the following cases:
1. When the robot returns a response to a previously sent JSON command (publishJsonMessage).
2. When the robot actively sends an unsolicited notification.
Parameters cb: callback function, prototype as void(const std::string &json_payload)
json_payload includes:
- Command response: {"accid": "xxx", "title": "response_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
- Notification: {"accid": "xxx", "title": "notify_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
Return Value None
Remarks Valid only in high-level development mode

Code Example:

#include <thread>

// include the limxsdk: Humanoid header file to import the Humanoid class
#include "limxsdk/humanoid.h"  

// use the limxsdk namespace to simplify references to the Humanoid class
using namespace limxsdk;  

int main(int argc, char *argv[]){
  // obtain the singleton instance of the Humanoid class
  Humanoid* robot = Humanoid::getInstance();  
  
  // default IP address of the robot
  std::string robot_ip = "10.192.1.2";
  if (argc > 1)
  {
    // If a command-line argument is provided, use it as the robot’s IP address
    robot_ip = argv[1];
  }
  
  // Initialize the communication runtime environment
  if (!robot->init(robot_ip))
  {
    // If initialization fails, terminate the program
    exit(1); 
  }
  
  // Handle responses and notifications from the robot’s “High-Level Application Protocol Interface” calls.
  robot->subscribeJsonMessage([&](const std::string & json_payload) {
    std::cout << json_payload << std::endl;
  });
  
  // Enter an infinite loop to keep the program running
  while (true)
  {
    // sleep for 1000 milliseconds
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 
  }
  return 0;
}

4.1.11 Reference Example

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

4.2 Python Motion Control API

Provides a Python motion-control API with equivalent functionality to the C++ motion-control interface, enabling developers who are not familiar with C++ to develop motion-control algorithms in Python.

4.2.1 Install Motion Control Development Library

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

4.2.2 init Interface

Function Name init
Function Prototype def init(self, robot_type: robot.RobotType)
Description Specifies the robot type during initialization and creates a local robot instance of the corresponding type.
Parameters robot_type: an enumeration value specifying the robot type, where RobotType.Humanoid represents a bipedal humanoid robot.
Return Value None
Remarks None

Code Example:

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

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

4.2.3 init Interface

Function Name init
Function Prototype def init(self, robot_ip: str = "127.0.0.1")
Description Initializes the communication and runtime environment for the motion control algorithm, typically called before other interfaces in the main function.
Parameters robot_ip: The IP address of the robot. Use "127.0.0.1" for simulation and "10.192.1.2" for real robots.
Return Value Success: return True
Failure: return False
Remarks None

Code Example:

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

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)
    
    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided as the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot’s communication runtime environment using the IP address
    if not robot.init(robot_ip):
        sys.exit()

4.2.4 getMotorNumber Interface

Function Name getMotorNumber
Function Prototype def getMotorNumber(self)
Description Retrieves the motor number in the robot.
Parameters None
Return Value Returns an unsigned integer representing the total number of motors in the robot.
Remarks Typically, a bipedal humanoid robot is equipped with 6 motors.

Code Example:

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

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided as the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Obtain the number of motors in the robot
    motor_number = robot.getMotorNumber()

4.2.5 subscribeImuData Interface

Function Name subscribeImuData
Function Prototype def subscribeImuData(self, callback: Callable[[datatypes.ImuData], Any])
Description Subscribes to the robot’s IMU data and triggers the specified callback function whenever new IMU data is received.
Parameters Callback: Callback function to process the new IMU data.
Return Value Success: return True Failure: return False
Remarks - datatypes.ImuData structure prototype defined as follows:
import sys

class ImuData(object):
    __slots__ = ['stamp','acc','gyro','quat']
    def __init__(self):
        self.stamp = 0 # Timestamp: Typically indicates the time at which the data was recorded or generated, in nanoseconds
        self.acc = [0. for x in range(0, 3)]  # Stores IMU (Inertial Measurement Unit) accelerometer data, used to track linear acceleration along the three axes
        self.gyro = [0. for x in range(0, 3)] # Stores IMU gyroscope data, used to track angular velocity or rotational speed
        self.quat = [0. for x in range(0, 4)] # Stores IMU quaternion data, representing orientation in 3D space

Code Example:

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:
    # Subscribe to the robot's IMU data
    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__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to handle callbacks
    receiver = RobotReceiver()

    # Create a partial function for the callback
    imuDataCallback = partial(receiver.imuDataCallback)

    # Subscribe to the robot's IMU data
    robot.subscribeImuData(imuDataCallback)

4.2.6 subscribeRobotState Interface

Function Name subscribeRobotState
Function Prototype def subscribeRobotState(self, callback: Callable[[datatypes.RobotState], Any])
Description Subscribes to receive updates on robots' status
Parameters Callback: callback function, invoked upon receiving a state update. Its parameter points to a datatypes.RobotState object.
- datatypes.RobotState structure fields:
  - stamp: Timestamp, indicates when the data was recorded or generated.
  - tau: Vector storing the estimated output torques (in newton-meters).
  - q: Vector storing the current joint positions (in radians).
  - dq: Vector storing the current joint velocities (in radians per second).
  - motor_names: store the corresponding joint name.

Notes: This interface reports joint states based on the robot’s equivalent serial URDF.

This robot adopts a series-parallel hybrid configuration design, with parallel drive structures used at some joints. Unlike traditional serial joints (where a joint is directly driven by a single actuator), parallel joints have the following characteristics:
- Multiple actuators collaboratively driving a single joint DOF
- Higher load capacity, stiffness, and dynamic performance
- More compact mechanical design with higher torque density
- Improved fault tolerance through actuator redundancy

While parallel actuation offers significant mechanical advantages, it also introduces challenges for control and application development:
- More complex kinematic and dynamic modeling
- Incompatibility with conventional serial robot control algorithms
- Higher learning curve due to parallel mechanism theory
- Limited compatibility with existing robotics ecosystems (e.g., ROS, MoveIt!)

To eliminate these complexities, the robot controller transparently maps parallel joints to an equivalent serial joint model, allowing upper-layer applications to interact with the robot as if it were a standard serial manipulator.

State Feedback:
Parallel actuator states (position, velocity, current/torque) → Equivalent serial joint computation → Equivalent serial joint states (position, velocity, torque)
Return Value Success: return True Failure: return False
Remarks - datatypes.RobotState structure prototype defined as follows:
import sys

class RobotState(object):
    __slots__ = ['stamp','tau','q','dq']
    def __init__(self):
        self.stamp = 0 # Timestamp: Typically indicates the time at which the data was recorded or generated, in nanoseconds
        self.tau = []  # Stores the vector of current estimated output torques (unit: N·m)
        self.q = []    # Stores the vector of current joint angles (unit: radians)
        self.dq = []   # Stores the vector of current joint velocities (unit: radians per second)
        self.motor_names = []   # Stores the corresponding joint names

Code Example:

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:
    # Callback function for receiving the robot’s state
    def robotStateCallback(self, robot_state: datatypes.RobotState):
        print("\n------\nrobot_state:" + \
              "\n  stamp: " + str(robot_state.stamp) + \
              "\n  tau: " + str(robot_state.tau) + \
              "\n  q: " + str(robot_state.q) + \
              "\n  dq: " + str(robot_state.dq))

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to handle callbacks
    receiver = RobotReceiver()

    # Create a partial function for the callback
    robotStateCallback = partial(receiver.robotStateCallback)

    # Subscribe to the robot's IMU data
    robot.subscribeRobotState(robotStateCallback)

4.2.7 publishRobotCmd Interface

Function Name publishRobotCmd
Function Prototype def publishRobotCmd (self, cmd: datatypes.RobotCmd)
Description Publishes a command to control the robot’s actions.
Parameters cmd: A datatypes.RobotCmd object representing the desired robot command, containing the following fields:
  - stamp: Timestamp in nanoseconds indicating when the data was recorded or generated.
  - q: Vector storing the desired joint positions (in radians).
  - dq: Vector storing the desired joint velocities (in radians per second).
  - tau: Vector storing the desired output torques (in newton-meters).
  - Kp: Vector storing the desired position stiffness (in newton-meters per radian).
  - Kd: Vector storing the desired velocity stiffness (in newton-meters per radian per second).
  - motor_names: store the joint names that need to be controlled.

Notes: This interface accepts joint commands for the robot’s equivalent serial URDF model.

The robot adopts a serial–parallel hybrid architecture, with parallel drive structures used at some joints. Unlike conventional serial joints, where each joint is driven by a single actuator, parallel joints have the following characteristics:
- Multiple actuators collaboratively drive a single joint DOF.
- Higher load capacity, stiffness, and dynamic performance.
- More compact mechanical design with higher joint torque output.
- Improved fault tolerance through actuator redundancy.

Although parallel joints offer significant mechanical advantages, they also introduce challenges for control and application development:
- More complex kinematic and dynamic models.
- Conventional serial robot control algorithms and toolchains cannot be applied directly.
- Higher learning curve due to the complexity of parallel mechanisms.
- Limited compatibility with existing robotics ecosystems (e.g., ROS and MoveIt!).

To address these challenges, the robot controller performs a transparent conversion from parallel joints to an equivalent serial joint model, completely hiding the complexity of the parallel mechanisms from upper-layer applications.

Command Flow:
Equivalent serial joint commands (position, velocity, torque) → Parallel actuator command computation → Actuator command execution

Note:
Since Kp and Kd gains cannot be efficiently converted to parallel actuator commands, users requiring force control are recommended to command joint torque (tau) directly instead of achieving force control indirectly through PD control. This is particularly relevant for reinforcement learning policies that output position or velocity actions, where the controller converts PD commands into torque commands internally.
Return Value Success: return True Failure: return False
Remarks - datatypes.RobotCmd structure prototype defined as follows:
import sys

class RobotCmd(object):
    __slots__ = ['stamp','mode','q','dq','tau','Kp','Kd']
    def __init__(self):
        self.stamp = 0 # Timestamp (in nanoseconds), typically indicates the time at which the data was recorded or generated.
        self.mode = [] # The robot's desired working mode
        self.q = []    # A vector for storing desired joint angles (unit: radians)
        self.dq = []   # A vector for storing desired joint velocities (unit: radians per second)
        self.tau = []  # A vector for storing desired output torques (unit: N·m)
        self.Kp = []   # A vector for storing desired position stiffness values (unit: N·m per radian)
        self.Kd = []   # A vector for storing desired velocity stiffness values (unit: N·m per radian per second)
        self.motor_names = []   # Stores the names of the robot joints to be controlled

Code Example:

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__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided as the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Obtain information on joint offsets, joint limits, and the number of motors
    joint_offset = robot.getJointOffset()
    joint_limit = robot.getJointLimit()
    motor_number = robot.getMotorNumber()
    
    # Main loop to continuously publish robot commands
    rate = Rate(500) # 1500 Hz
    cmd_msg = datatypes.RobotCmd()
    while True:
        # Set default values for timestamp, control mode, joint positions, velocities, torques, Kp, and 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)  # Publish the robot commands
        rate.sleep()  # Control the loop frequency

4.2.8 subscribeSensorJoy Interface

Function Name subscribeSensorJoy
Function Prototype def subscribeSensorJoy(self, callback: Callable[[datatypes.SensorJoy], Any])
Description Subscribes to the robot’s remote controller data during real-machine deployment. When data is received, the specified callback function is invoked and provided with a constant pointer to a datatypes.SensorJoy structure for processing.
Parameters callback: Callback Function for receiving remote controller data with parameter type SensorJoyConstPtr.
Return Value Success: return True Failure: return False
Remarks - datatypes.SensorJoy structure prototype defined as follows:
import sys

class SensorJoy(object):
    __slots__ = ['stamp','axes','buttons']
    def __init__(self):
        self.stamp = 0     # Timestamp corresponding to the sensor input time, in nanoseconds
        self.axes = []     # Values representing controller joystick manipulation
        self.buttons = []  # Values representing controller button operation states

Code Example:

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:
    # Callback function for receiving the remote controller data
    def sensorJoyCallback(self, sensor_joy: datatypes.SensorJoy):
        print("\n------\nsensor_joy:" + \
              "\n  stamp: " + str(sensor_joy.stamp) + \
              "\n  axes: " + str(sensor_joy.axes) + \
              "\n  buttons: " + str(sensor_joy.buttons))

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to handle callbacks
    receiver = RobotReceiver()

    # Create a partial function for the callback
    sensorJoyCallback = partial(receiver.sensorJoyCallback)

    # Subscribe to the robot's IMU data
    robot.subscribeSensorJoy(sensorJoyCallback)

4.2.9 subscribeDiagnosticValue Interface

Function Name subscribeDiagnosticValue
Function Prototype def subscribeDiagnosticValue(self, callback: Callable[[datatypes.DiagnosticValue], Any])
Description Subscribes to the robot’s diagnostic and status updates. Upon receiving a diagnostic message, the callback function is triggered with a datatypes.DiagnosticValue object, allowing continuous health monitoring and prompt response to detected issues.
Parameters Callback function for receiving diagnostic data. The parameter type is datatypes.DiagnosticValue, which contains fields such as timestamp, level, name, code, and message.
Return Value Success: return True Failure: return False
Remarks - datatypes.DiagnosticValue structure prototype defined as follows:
import sys

class DiagnosticValue(object):
    __slots__ = ['stamp','level','name','code','message']
    def __init__(self):
        self.stamp = 0 # Timestamp, in nanoseconds
        self.level = 0 # Diagnostic level associated with the diagnostic value - 0: OK, 1: WARN, 2: ERROR
        self.name = '' # Name identifying the diagnostic value
        self.code = 0  # Code corresponding to the diagnostic value
        self.message = ''  # Detailed message related to the diagnostic value

Code Example:

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:
    # Callback function for receiving diagnostic value
    def diagnosticValueCallback(self, diagnostic_value: datatypes.DiagnosticValue):
        print("\n------\ndiagnostic_value:" + \
              "\n  stamp: " + str(diagnostic_value.stamp) + \
              "\n  name: " + diagnostic_value.name + \
              "\n  level: " + str(diagnostic_value.level) + \
              "\n  code: " + str(diagnostic_value.code) + \
              "\n  message: " + diagnostic_value.message)

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "127.0.0.1"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to handle callbacks
    receiver = RobotReceiver()

    # Create a partial function for the callback
    diagnosticValueCallback = partial(receiver.diagnosticValueCallback)

    # Subscribe to the robot's IMU data
    robot.subscribeDiagnosticValue(diagnosticValueCallback)

4.2.10 publishJsonMessage Interface

Function Name publishJsonMessage
Function Prototype def publishJsonMessage(self, json_payload: str)
Description Sends a JSON-formatted message to the robot following the High-Level Application Protocol Interface.
Parameters json_payload: A JSON string conforming to the High-Level Application Protocol specification. Example: {"accid": "xxx", "title": "request_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
Return Value None
Remarks Valid only in high-level development mode.

Code Example:

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__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "10.192.1.2"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()
    
    # Set the protocol content to be sent
    json_payload = '''{
        "accid": "HU_D03_01", # Replace with the real robot serial number (SN)
        "title": "request_get_joint_state",
        "timestamp": 1672373633989,
        "guid": "746d937cd8094f6a98c9577aaf213d98",
        "data": {}
    }'''
    
    # Send the JSON protocol.
    robot.publishJsonMessage(json_payload)
    
    # Keep the program running
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("Program interrupted by the user")

4.2.11 subscribeJsonMessage Interface

Function Name subscribeJsonMessage
Function Prototype def subscribeJsonMessage(self, callback: Callable[[str], Any])
Description Registers a callback function to handle responses and notifications from the robot’s High-Level Application Protocol Interface.

The callback is triggered in the following cases:
1. When the robot returns a response to a previously sent JSON command (publishJsonMessage).
2. When the robot actively sends an unsolicited notification.
Parameters callback: callback function

json_payload includes:
- Command response: {"accid": "xxx", "title": "response_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
- Notification: {"accid": "xxx", "title": "notify_xxx", "timestamp": xxx, "guid": "xxx", "data": {}}
Return Value None
Remarks Valid only in high-level development mode

Code Example:

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:
    # Handle responses and notifications from the robot’s “High-Level Application Protocol Interface” calls.
    def jsonMessageCallback(self, json_payload: str):
        print("\n------\njson_payload:" + json_payload)

if __name__ == '__main__':
    # Create a Robot instance of type Humanoid
    robot = Robot(RobotType.Humanoid)

    robot_ip = "10.192.1.2"
    # Check whether a command-line argument is provided for the robot’s IP address
    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Initialize the robot using robot_ip
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to handle callbacks
    receiver = RobotReceiver()

    # Create a partial function for the callback
    jsonMessageCallback = partial(receiver.jsonMessageCallback)

    # Subscribe to the robot's IMU data
    robot.subscribeJsonMessage(jsonMessageCallback)

4.2.12 Reference Example

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

5 Check and Set the Robot Model

When compiling or running RL training, control algorithms and simulation programs, selecting the correct robot model is essential. Check the robot model and set it in the environment variable ROBOT_TYPE to ensure the correct model is identified and applied across different tasks.

Steps to View and Configure the Robot Model:

  1. Connect to the robot’s Wi-Fi hotspot and enter the password: 12345678

图片

  1. Open a web browser and navigate to http://10.192.1.2:8080. The page displays the SN (serial number) — for example, HU_D03_03_001 — where HU_D03_03 represents the robot model, as shown below.

图片

  1. Set the robot model: Open a Bash terminal and run the following shell command to set the robot model. This ensures that the correct robot model information is recognized during secondary development.
echo 'export ROBOT_TYPE=HU_D03_03' >> ~/.bashrc && source ~/.bashrc

6 Robot Simulator

MuJoCo is a lightweight, high-performance physics simulator designed for multi-joint robots and mechanical systems.
It features an efficient physics engine capable of accurately simulating contact and friction, and can operate independently without relying on ROS.

6.1 Running the Simulator

  1. Environment Requirements: Python 3.8 or higher is recommended
  2. Open a Bash Terminal
  3. Download the MuJoCo Simulator Code:
git clone --recurse git@github.com:limxdynamics/humanoid-mujoco-sim.git
  1. Install the Motion Control Development Library:
  • Linux x86_64 Environment
pip install humanoid-mujoco-sim/limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
  • Linux aarch64 Environment
pip install humanoid-mujoco-sim/limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
  1. Set the Robot Model: Please refer to the “check and set the Robot Model” section to check your robot model. If not yet configured, please follow the steps below:
  • List available robot types using the shell command 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
  • For example, to set the robot model type HU_D04_01 (replace with your actual model):
echo 'export ROBOT_TYPE=HU_D04_01' >> ~/.bashrc && source ~/.bashrc
  1. Run the MuJoCo simulator:
python humanoid-mujoco-sim/simulator.py

6.2 Demonstration Results

Actual performance may vary depending on your system configuration.
图片

7 RL Algorithm Deployment

7.1 Deployment with Standard C++

7.2 Deployment with Python

7.3 Deployment with ROS2

7.4 Deployment with ROS1

8 Logs and Data Packages

  • Automatic Data Recording: The robot system automatically records essential data, including IMU data (ImuData), state data (/joint/state), control data (/joint/cmd), and runtime logs. These records are critical for motion control analysis and performance evaluation.
  • Accessing and Downloading Data: The robot stores runtime log data for troubleshooting and performance optimization. To access the data, connect your computer to the robot’s Wi-Fi hotspot, then open a browser at http://10.192.1.2:8090 to download the desired datasets.

图片

8.1 Data Package Visualization and Analysis

  • Downloading Data Packages: After downloading a .bag file, you can use PlotJuggler to visualize and analyze the data. If the downloaded file is in .bag.active format, run the following shell command to reindex it and generate a new .bag file for PlotJuggler to load properly.

    rosbag reindex your_file.bag.active
    mv your_file.bag.active your_file.bag
    
  • Visualization: Launch the PlotJuggler visualization tool using the shell command rosrun plotjuggler plotjuggler -n, then load the data packages as shown below:

    图片

8.2 Logs and Diagnostic Trace Data

Structured log and diagnostic trace data are recorded for system monitoring, as shown below. These datasets can be used for troubleshooting and performance optimization when needed.

图片

9 Robot Software Upgrade

You can upgrade the robot software via the web management interface using a locally downloaded software package. The steps are as follows:

  1. Connect to Wi-Fi:

    • Connect to your robot's Wi-Fi hotspot, password: 12345678

    图片

  2. Access The Management Page:

  3. Select and Upgrade Software:

    • Navigate to "Version Management" -> "Browse" -> "Upgrade"

    • After the upgrade is complete, the robot's main control computer will restart automatically.

图片

10 Developer Computer

The developer computer is primarily used for developing robot-related algorithms and applications. It can be accessed via the robot’s onboard Wi-Fi network. The steps are as follows:

  1. Connect to Wi-Fi:

    • Connect to your robot's Wi-Fi hotspot, password: 12345678

    图片

  2. SSH Login:

    • IP Address: 10.192.1.3

    • Password: 123456

    • Use the following command in the terminal (fingerprint confirmation needed on first login)

      ssh guest@10.192.1.3
      
    • System Configuration:

      • Operating System: Ubuntu 22.04 (Jetpack 6.2.1)

      • ROS2: ROS2 Humble (default installation)

      • ROS1: ROS1 Noetic (Docker-based environment), to enter the ROS1 Docker environment:

        sudo docker exec -it ros_noetic /bin/bash
        

11 RealSense Camera Data Acquisition

Note:

  1. The camera driver starts automatically at power-on. Disable camera driver auto-start before acquiring camera data to prevent interference. (Supported in main controller software V2.0.33 or later.)
  2. Disabling camera driver auto-start will prevent the provided data acquisition kit from functioning.

11.1 Disable Camera Auto-Start

  1. Enter 10.192.1.2:8080 in a web browser to access the interface:

    图片

  2. Set to disable and save:

    图片

11.2 Camera Data Acquisition

  1. Log in to the Developer Computer:

    • Please follow the procedures described in the “Developer Computer” section to log in to the computer
  2. Launch the Realsense ROS Node to Acquire Camera Data:

  3. Example Code Description:

  • Camera Naming Convention:

    • Default Naming: The script automatically assigns topic name prefixes for multiple cameras using the format “camera” followed by an index (e.g., camera0, camera1).

    • Custom Naming: The script can be modified to assign topic prefixes based on the camera's Serial Number instead of using the default “camera + counter” naming scheme.

The following code example demonstrates how to acquire data from multiple cameras:

  1. Log in to the Developer PC via SSH.

  2. Log in to the ROS 1 Environment.

    sudo docker exec -it ros_noetic /bin/bash
    
  3. Save the script below as: 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
    
    
  4. Execute the script in the terminal to launch the camera node

    /bin/bash rs_camera.sh
    
  5. In another terminal, use rostopic list to verify the results

    rostopic list