# TRON 1 SDK Development Guide

> Source: https://www.limxdynamics.com/en/documents/799664773997400064
> Published: 2026-01-15
> Updated: 2026-05-21
> Tags: TRON 1 EDU Ed.

## Update Records

## 2025.07.23

### Documentation Structure Optimization

- Merged C++ interface and Python interface
- pointfoot_sdk_lowlevel  change to limxsdk_lowlevel

## 2025.04.29

### New Features

- 5.5.7 Enable Marktime Mode
- 5.5.11 Fall Recovery

### Updates

- 5.5.13 Global Messages: Added fall recovery related content

## 2025.04.15

### New Features

- 5.5.4 Adjust Robot Height
- 5.5.8 Enable Odometry
- 5.5.10 Set Light Effects

### Updates

- 8.1 Python-based Deployment: Changed to run based on Conda environment
- 7.2 RL Model Training: Modified the parameter file location for `--load_run`

---

## 1. Pointfoot SDK Overview

The Pointfoot SDK is primarily divided into two levels:

- **pointfoot-sdk-lowlevel (Low-level SDK)**: In developer mode, users can use this interface to develop their own motion control algorithms and seamlessly deploy algorithms in simulation and on real machines. The low-level SDK provides direct access to and control of robot joints and sensors.

- **High-level Application Development Interface**: Users can utilize the high-level application development interface to develop their own software business functions such as mapping, navigation, and robot management based on the pre-installed motion control algorithms of the robot. The high-level SDK allows developers to focus on the application functionality of the robot without having to delve into the details of the underlying motion control.

## 1.1 Communication Architecture

The following diagram presents the system composition and interaction between the developer's computer and the robot body. The development computer part covers the motion control algorithm node and software business logic implementation module, which controls the movement of the robot body through the high-level application development interface and lowlevel data communication. The robot body consists of a data switch, a main control computer, and various hardware components, with the main control computer responsible for coordinating the operation of each component.

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768465561605_SDK2.png)

---

## 2. View/Set Robot Model

It is crucial to select the correct robot model while compiling and running RL training, control algorithms, and simulator programs. You can ensure accurate identification and application of the corresponding robot model in different tasks by viewing the robot model and setting it in the environment variable `ROBOT_TYPE`. Below are the steps to view and configure the robot model.

  - Choose and connect to your robot's Wi-Fi hotspot, which generally follows the naming convention of "PF_TRON1A_001", using the password `12345678`.

- Enter `http://10.192.1.2:8080` in your browser to access the "Robot Information" page and view details about the robot. As shown in the image below, the SN (Serial Number) is `PF_P441C_037` , and `PF_P441C` is the robot model.

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449183837_image.png)

- Set Robot Model: Open the Bash terminal and set the robot model using the following shell command. This will ensure that you obtain the correct robot model when compiling and running RL training algorithms, control algorithms, and simulator programs.

  - ```Bash
    echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
    ```

---

# 3. Simulation and Real Machine Debugging

## 3.1 Pointfoot SDK Core Functions

:::tip

- **pointfoot-sdk-lowlevel**: In developer mode, users can use this interface to develop their own motion control algorithms and achieve seamless deployment of algorithms in simulation and on real machines.
- **pointfoot-sdk-highlevel**: Users can utilize the high-level application development interface to develop their own software business functions such as mapping, navigation, and robot management based on the pre-installed motion control algorithms of the robot.
  :::

:::tip
This part mainly introduces how to use the examples in our SDK for **simulation and real machine debugging**. Developers can refer to the examples and subsequent interface introductions to develop their own control programs to replace our examples for simulation and real machine debugging. It is recommended to first run the developed control programs in Gazebo, and only after the effects meet expectations should you proceed to testing on real machines.
:::

## 3.2 Setting up the Development Environment

On an algorithm developer's personal computer, we recommend setting up a ROS Noetic-based algorithm development environment running **the Ubuntu 20.04** operating system. ROS Noetic provides a series of tools and libraries, including core libraries, communication libraries, and simulation tools like Gazebo, greatly facilitating the development, testing, and deployment of robotic algorithms. These resources collectively offer users a comprehensive and robust algorithm development environment.

Of course, even without ROS, you can choose to develop your own motion control algorithms in other environments. The motion control development interface we provide is a dependency-free SDK that is based on standard C++11 and Python. It supports cross-operating system and cross-platform invocation development, offering developers more flexible options.

- For ROS Noetic installation, please refer to the documentation at https://wiki.ros.org/noetic/Installation/Ubuntu/ and select "ros-noetic-desktop-full" for installation.

- Once ROS Noetic is installed, open the Bash terminal and enter the following Shell command to install the libraries that are required for the development environment.

  - ```Bash
    sudo apt-get update
    sudo apt install ros-noetic-urdf \
                     ros-noetic-kdl-parser \
                     ros-noetic-urdf-parser-plugin \
                     ros-noetic-hardware-interface \
                     ros-noetic-controller-manager \
                     ros-noetic-controller-interface \
                     ros-noetic-controller-manager-msgs \
                     ros-noetic-control-msgs \
                     ros-noetic-ros-control \
                     ros-noetic-robot-state-* \
                     ros-noetic-joint-state-* \
                     ros-noetic-gazebo-* \
                     ros-noetic-rqt-gui \
                     ros-noetic-rqt-controller-manager \
                     ros-noetic-plotjuggler* \
                     ros-noetic-joy-teleop ros-noetic-joy \
                     cmake build-essential libpcl-dev libeigen3-dev libopencv-dev libmatio-dev \
                     python3-pip libboost-all-dev libtbb-dev liburdfdom-dev liborocos-kdl-dev -y
    ```

## 3.3 Creating a Workspace

You can create an algorithm development workspace by following these steps:

- Open a Bash terminal.

- Create a new directory to hold the workspace. For example, you can create a directory called "limx_ws" under the user's home directory.

  - ```Bash
    mkdir -p ~/limx_ws/src
    ```

- Download the motion control development interface.

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/pointfoot-sdk-lowlevel.git
    ```

- Download the Gazebo simulator.

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/pointfoot-gazebo-ros.git
    ```

- Download the robot model description file.

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/robot-description.git
    ```

- Download the visualization debugging tool.

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/robot-visualization.git
    ```

- Compile the Project:

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    cd ~/limx_ws
    catkin_make install
    ```

## 3.4 Simulation Debugging

- Set the Robot Model: To view or set your robot model, refer to the "View/Set the Robot Model" section. If it has not been set yet, follow these steps to do so.

  - Use the Shell command `tree -L 1 src/robot-description/pointfoot` to list the available robot types.

  - ```Plain
    limx@limx:~$ tree -L 1 src/robot-description/pointfoot
    src/robot-description/pointfoot
    ├── PF_P441A
    ├── PF_P441B
    ├── PF_P441C
    └── PF_P441C2
    └── PF_TRON1A
    └── SF_TRON1A
    └── WF_TRON1A
    ```

  - - Set the robot model type using `PF_TRON1A` (please replace with your actual robot type) as an example.

  - ```Plain
    echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
    ```

- Run the simulator:

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    cd ~/limx_ws
    source install/setup.bash
    roslaunch pointfoot_gazebo empty_world.launch
    ```

- Run the control routine to verify that the robot in the simulator exhibits movement, thereby indicating that the simulation environment is set up completely.

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    cd ~/limx_ws
    source install/setup.bash
    rosrun limxsdk_lowlevel pf_groupJoints_move
    ```

  - ![](https://support.limxdynamics.com/en/assets/images/image-11-91e2035e960010aedffda4d188c0d410.png)

- Note: To better observe the robot's movement, uncomment the `robot.urdf` file in the robot model. This will lift the robot by anchoring its base link to the world coordinate system, suspending it in the air for easier monitoring of its movements.

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449284057_image.png)

## 3.5 Real Machine Debugging

- To activate Developer Mode: Once the robot is powered on, simultaneously press the `R1 and Left` buttons on the remote controller. This will prompt the robot o automatically restart and switch to Developer Mode. In this mode, users have the ability to develop their own motion control algorithms. Importantly, the Developer Mode setting will persist even after a power failure or reboot. Below is a list of remote controller buttons used to switch between working modes:

  | **Buttons** | **Mode**                             | **Description**                                              |
  | ----------- | ------------------------------------ | ------------------------------------------------------------ |
  | R1+Left     | Developer Mode (Under authorization) | Users can develop their own motion control algorithms using the motion control development interface. |
  | R1+Right    | Remote Control Mode                  | The robot runs the pre-installed motion control algorithm to walk smoothly in complex terrains including going up and down steps and crossing obstacles. |

- Modify the IP of the developer computer: Make sure that your development computer and the robot are connected through an external Ethernet port.

- Set the IP address of your computer to 10.192.1.200, and verify that the robot can successfully respond to the Shell command ping 10.192.1.2 to ensure network communication with your computer.
  Set the IP for your development computer as shown in the following image:

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449314449_image.png)

- Perform Zero Calibration: After the robot is turned on, before executing the motion control program, perform zero calibration to make each joint of the robot return to the initial position. The remote controller buttons for zero calibration are L1 and R1.

- Set the Robot Model: To view or set your robot model, refer to the "View/Set the Robot Model" section. If it has not been set yet, follow these steps to do so.

  - Use the Shell command `tree -L 1 src/robot-description/pointfoot` to list the available robot types.

  - ```Plain
    limx@limx:~$ tree -L 1 src/robot-description/pointfoot
    src/robot-description/pointfoot
    ├── PF_P441A
    ├── PF_P441B
    ├── PF_P441C
    └── PF_P441C2
    └── PF_TRON1A
    └── SF_TRON1A
    └── WF_TRON1A
    ```

  - Set the robot model type using `PF_TRON1A` (please replace with your actual robot type) as an example.

  - ```Plain
    echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
    ```

- Robot deployment and operation. Assign the robot's IP address as specified below to execute the routine and complete the deployment process (**ensuring proper lifting and installation of the robot is crucial** during deployment and operation).

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    source install/setup.bash
    rosrun limxsdk_lowlevel pf_groupJoints_move 10.192.1.2
    ```

## 3.6 Visualization Tools

We provide a set of visual debugging tools that can be utilized for both simulation and real-world robot deployment. These tools leverage RViz and Plotjuggler to visually present data. You can easily access and use these tools through our GitHub link: https://github.com/limxdynamics/robot-visualization. These tools are intuitive and easy to use, boosting development productivity and facilitating the code debugging process. Follow these steps to create a visualization tool workspace:

- Open a Bash terminal.

- Create a new directory to hold the workspace. For example, you can create a directory called "limx_ws" under the user's home directory:

  - ```Bash
    mkdir -p ~/limx_ws/src
    ```

- Download the robot model description file.

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/robot-description.git
    ```

- Download the visualization tool

  - ```Bash
    cd ~/limx_ws/src
    git clone https://github.com/limxdynamics/robot-visualization.git
    ```

- Compile the visualization tool:

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    cd ~/limx_ws
    catkin_make install
    ```

### 3.6.1 Using Plotjuggler for Visualization

PlotJuggler is a powerful data visualization tool that provides users with an intuitive interface for loading, displaying, and analyzing various types of data. Users can present data through charts, curves, and graphs to better understand the relationships and trends among the data. PlotJuggler not only supports visualization of real-time data, but also loads and processes large amounts of historical data. Website address: https://plotjuggler.io

- Simulation deployment: Here's how to run Plotjugler

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    source install/setup.bash
    roslaunch robot_visualization pointfoot_plot_sim.launch
    ```

- Real-world Robot Deployment: Here's how to run Plotjugler

  - Open the `pointfoot_plot_hw.launch` file and change the IP address of the robot to the actual robot address:

    - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449367891_image.png)

  - Then recompile the tool and execute the following commands:

    - ```Bash
      # If you have Conda installed, deactivate the Conda environment temporarily
      # Because Conda may interfere with the configuration of ROS's runtime environment
      conda deactivate

      cd ~/limx_ws
      catkin_make install
      ```

  - Once compiled, you can run the PlotJuggler tool with the following Shell command:

    - ```Bash
      # If you have Conda installed, deactivate the Conda environment temporarily
      # Because Conda may interfere with the configuration of ROS's runtime environment
      conda deactivate

      cd ~/limx_ws
      source install/setup.bash
      roslaunch robot_visualization pointfoot_plot_hw.launch
      ```

  - Below is how Plotjugler operates in action.

    - | **Data Topics**      | **Description**                          |
      | -------------------- | ---------------------------------------- |
      | /ImuData             | IMU real-time data                       |
      | /RobotCmdPointFoot   | Real-time data of robot control commands |
      | /RobotStatePointFoot | Real-time data of the robot state        |

    - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449398297_image.png)

### 3.6.2 Using RViz for Visualization

During actual robot deployment, in addition to using PlotJuggler for data visualization, you can also utilize RViz to view the robot's operation in real time. RViz is a powerful 3D visualization tool that can display the robot's sensor data, movement status, and environmental perception. Using RViz, users can observe the robot's movement trajectories and changes in sensor data in real time, helping to debug and monitor the robot's operation status. During deployment, RViz can be configured to subscribe to the robot's sensor data topics and display them in a 3D scene, enabling real-time monitoring of the robot's behavior and facilitating necessary adjustments and optimizations. Follow these steps to run RViz:

- Open the `pointfoot_rviz_hw.launch` file and change the IP address of the robot to the actual robot address:

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449412594_image.png)

- Then recompile the tool and execute the following commands:

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    catkin_make install
    ```

- Once compiled, you can run RViz with the following Shell command:

  - ```Bash
    # If you have Conda installed, deactivate the Conda environment temporarily
    # Because Conda may interfere with the configuration of ROS's runtime environment
    conda deactivate

    source install/setup.bash
    roslaunch robot_visualization pointfoot_rviz_hw.launch
    ```

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449429500_image.png)

## 3.7 Package Data, Log Data, and Embedded Data Points

The robot system automatically records important data such as the IMU data of the robot (/ImuData), the robot state data (RobotStatePointFoot), and the robot command data (RobotCmdPointFoot). These data are crucial for the analysis of the robot's motion control. In addition, the robot also records runtime log data and structured data from diagnostic embedding points for troubleshooting and performance optimization when needed.

When your computer connects to the robot's WiFi hotspot, you can access and download these data from `http://10.192.1.2:8090`. This process facilitates monitoring, maintenance, and debugging of the robot.

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449448950_image.png)

### 3.7.1 Package Data Visualization Analysis Method

- **Package Download**: After downloading the .bag file, you can use the PlotJuggler visualization tool to load and analyze the package data. Notably, if you download a .bag.active file, you need to re-index it using the following Shell command to generate a new .bag file that PlotJuggler can load:

  - ```Bash
    rosbag reindex your_file.bag.active
    mv your_file.bag.active your_file.bag
    ```

- `Visualization Viewing`: Launch the PlotJuggler visualization tool with the Shell command `rosrun plotjuggler plotjuggler -n`. As shown in the figure below, load the package data and analyze it.

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449483748_image.png)

### 3.7.2 Log Data and Diagnostic Data

The following figures show the log data and the diagnostic data respectively. The data can be used for troubleshooting and performance optimization when needed.

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449503013_image.png)

## 3.8 MuJoCo Simulation

MuJoCo is a lightweight and high-performance physics simulator specifically designed for multi-joint robots and mechanical systems. It boasts an efficient physics engine capable of accurately handling contact and friction, and it can operate independently without relying on ROS. Thanks to its high-speed computing capabilities, MuJoCo is widely applied in robot simulation and reinforcement learning, particularly in scenarios demanding high simulation efficiency. Below are the steps to conduct a simulation using MuJoCo:

### 3.8.1 Running the MuJoCo Simulation

- Open a Bash terminal.

- Download the MuJoCo Simulator code:

  - ```Plain
    git clone --recurse https://github.com/limxdynamics/pointfoot-mujoco-sim.git
    ```

- Install the motion control development libraries:

  - Linux x86_64 Environment

  - ```Plain
    pip install pointfoot-mujoco-sim/limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
    ```

  - Linux aarch64 Environment

  - ```Plain
    pip install pointfoot-mujoco-sim/limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
    ```

- Set Robot Model: Refer to the "View/Set Robot Model" section to see your robot model. If it has not been set yet, follow these steps to do so.

  - List the available robot types with the Shell command `tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot`:

  - ```Plain
    limx@limx:~$ tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot
    pointfoot-mujoco-sim/robot-description/pointfoot
    ├── PF_P441A
    ├── PF_P441B
    ├── PF_P441C
    └── PF_P441C2
    └── PF_TRON1A
    └── SF_TRON1A
    └── WF_TRON1A
    ```

  - Set the robot model type using `PF_TRON1A` (please replace with your actual robot type) as an example.

  - ```Plain
    echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
    ```

- Run the MuJoCo simulator:

  - ```Plain
    python pointfoot-mujoco-sim/simulator.py
    ```

### 3.8.2 Run the Controller Program

- Open a Bash terminal.

- Install the environment required for compilation

  - ```Plain
    sudo apt update
    sudo apt install -y cmake build-essential
    ```

- Compiling an Example of the Controller SDK:

  - ```Plain
    cd pointfoot-mujoco-sim/pointfoot-sdk-lowlevel

    cd build
    cmake ..
    make
    ```

- Set Robot Model: Refer to the "View/Set Robot Model" section to see your robot model. If it has not been set yet, follow these steps to do so.

  - List the available robot types with the Shell command `tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot`:

  - ```Plain
    limx@limx:~$ tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot
    pointfoot-mujoco-sim/robot-description/pointfoot
    ├── PF_P441A
    ├── PF_P441B
    ├── PF_P441C
    └── PF_P441C2
    └── PF_TRON1A
    └── SF_TRON1A
    └── WF_TRON1A
    ```

  - Set the robot model type using `PF_TRON1A` (please replace with your actual robot type) as an example.

  - ```Plain
    echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
    ```

- Run the Controller SDK Example:

  - ```Plain
    ./examples/pf_groupJoints_move
    ```

### 3.8.3 Demonstration of the Simulator's Operational Effect

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768449537021_image.png)

---

## 4. Low-Level Motion Control Development Interface

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

## 4.1 C++ Interface User Guide

### 4.1.1 Introduction to getInstance Interface

| Function | getInstance |
| --- | --- |
| Function prototype | static PointFoot* getInstance(); |
| Function overview | Obtains a pointer to a singleton instance of the PointFoot class |
| Parameters | None |
| Return value | PointFoot *, a pointer to a PointFoot instance |
| Notes | The singleton pattern is used to ensure that only one instance of the PointFoot class exists in the program |

**Code example:**

```cpp
#include <thread>

//Include limxsdk::PointFoot header to import PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  //Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

 //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 Introduction to init Interface

| Function | init |
| --- | --- |
| Function prototype | bool init(const std::string& robot_ip_address = "127.0.0.1"); |
| Function overview | **Initializes** the communication runtime environment of the motion control algorithm program. This function is usually invoked to complete the initialization before other interfaces are called in the main function. |
| Parameters | robot_ip_address: The IP address of the robot. It is usually set to "127.0.0.1" for simulation and probably "10.192.1.2" for a physical robot. |
| Return value | Returns true if the initialization is successful and false otherwise. |
| Notes | None |

**Code example:**

```cpp
#include <thread>

// Include limxsdk:: PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment of the motion control algorithm program
  if (!pf->init(robot_ip))
  {
   // If initialization fails, exit the program
    exit(1);
  }

  // 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 Introduction to getMotorNumber Interface

| Function | getMotorNumber |
| --- | --- |
| Function prototype | uint32_t getMotorNumber(); |
| Function overview | This function retrieves the total number of motors in the robot. |
| Parameters | None |
| Return value | Returns an unsigned integer representing the total motor count. |
| Notes | Generally, there are six motors in a point-foot robot |

**Code example:**

```cpp
#include <thread>

// Include limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment of the motion control algorithm program
  if (!pf->init(robot_ip))
  {
   // If initialization fails, exit the program
    exit(1);
  }

  // Get the number of motors in the robot
  uint32_t motor_num = pf->getMotorNumber();

  // 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 Introduction to subscribeImuData Interface

| Function | subscribeImuData |
| --- | --- |
| Function prototype | `void subscribeImuData(std::function cb);` |
| Function overview | Subscribe to the robot's **IMU data** and invoke the specified callback function when the new IMU data is received. |
| Parameters | cb: Callback function to process new IMU data. |
| Return value | None |

**Notes:**

The prototype of the ImuData data structure is as follows:

```cpp
/**
 * @struct ImuData
 *
 * @brief denotes the structure of the robot's IMU data based on sensor feedback.
 *
 * This structure encapsulates the IMU data, including the accelerometer, gyroscope, and quaternion.
 */

struct ImuData {
  uint64_t stamp; // Timestamp, in nanoseconds, generally denotes the time when the data was recorded or generated.
  float acc[3];  // Used to store the accelerometer data in the IMU to track the linear acceleration along the three axes (X, Y and Z).
  float gyro[3];  // Used to store the gyroscope data in the IMU to track the angular or rotational velocity along the three axes (X, Y and Z).
  float quat[4];  // Used to store the quaternion data in the IMU, representing orientation in 3D space (w, x, y, z).
};

// Aliases of smart pointer types
typedef std::shared_ptr<ImuData> ImuDataPtr;
typedef std::shared_ptr<ImuData const> ImuDataConstPtr;
```

**Code example:**

```cpp
#include <thread>

// include limxsdk::PointFoot header to import PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify the reference to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
   // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

 // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
   // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
   // If initialization fails, exit the program
    exit(1);
  }

  // Subscribe to the robot state updates and specify the callback function
  pf->subscribeImuData([&](const ImuDataConstPtr& msg) {
    // Process the received RobotState data here
    // Note: The callback function will be invoked when receiving the ImuData
  });

  // 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 Introduction to the subscribeRobotState Interface

| Function | subscribeRobotState |
| --- | --- |
| Function prototype | `void subscribeRobotState(std::function cb);` |
| Function overview | Subscribe to **robot state updates**. |
| Parameters | cb: A callback function that will be invoked when the robot state updates are received. Arguments of the callback function point to the constant pointers of the RobotState object. |
| Return value | None |

**Notes:**

- The prototype of the RobotState data structure is as follows:

```cpp
/**
 * @struct RobotState
 *
 * @brief represents the structure of the robot's state based on sensor feedback.
 *
 * This structure encapsulates various data points that can be used to monitor and control the robot, including the IMU data (accelerometer, gyroscope and quaternion), output torque, current angle and speed.
 */
struct RobotState {
  // Default constructor
  RobotState() { }
 // Constructor with arguments to initialize the tau, q and dq vectors with vector size of motor_num and initial value of 0.0
  RobotState(int motor_num)
  : tau(motor_num, 0.0)
  , q(motor_num, 0.0)
  , dq(motor_num, 0.0) { }
  uint64_t stamp;              // Timestamp, in nanoseconds, generally denotes the time when the data was recorded or generated
  std::vector<float> tau;      // Vector to store the current estimated output torque (in Newton-meters)
  std::vector<float> q;        // Vector to store the current angle (in radians)
  std::vector<float> dq;       // Vector to store the current speed (in radians per second)
};

// Aliases of smart pointer types
typedef std::shared_ptr<RobotState> RobotStatePtr;
typedef std::shared_ptr<RobotState const> RobotStateConstPtr;
```

- The order of motors corresponding to the tau, q and dq array of robot state data is as follows:
  - Point-Foot

    0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint

    3: abad_R_Joint, 4: hip_R_Joint, 5: knee_R_Joint

  - Wheel-Foot

    0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: wheel_L_Joint

    4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: wheel_R_Joint

  - Sole-Foot

    0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: ankle_L_Joint

    4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: ankle_R_Joint

**Code example:**

```cpp
#include <thread>

// Include limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

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

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
    // If initialization fails, exit the program
    exit(1);
  }

  // Subscribe to the robot state updates and specify the callback function
  pf->subscribeRobotState([&](const RobotStateConstPtr& msg) {
    // Process the received RobotState data here
    // Note: The callback function will be invoked when the robot state updates are received
  });

  // 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 Introduction to publishRobotCmd Interface

| Function | publishRobotCmd |
| --- | --- |
| Function prototype | bool publishRobotCmd(const RobotCmd& cmd); |
| Function overview | Publish a command to control **robot movements**. |
| Parameters | cmd: Denote the RobotCmd object of the required robot command.<br> The order of corresponding motors for the command data array is as follows: - Point-Foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint<br> 3: abad_R_Joint, 4: hip_R_Joint, 5: knee_R_Joint<br> - Wheel-Foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: wheel_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: wheel_R_Joint<br> - Sole-Foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: ankle_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: ankle_R_Joint<br> |
| Return value | None |

**Notes:**

- The prototype RobotCmd data structure is as follows:

```cpp
/**
  * @struct RobotCmd
  *
  *  @brief denotes the structure of the commands that control the robot.
  *
  * This structure contains various commands that can be used to control the robot, including the desired working mode, angle, speed, output torque, position stiffness, and speed 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) { }
  uint64_t stamp;             // Timestamp (in nanoseconds), generally denoting the time when data was recorded or generated.
  std::vector<uint8_t> mode;  // 0: torque mode control; 1: speed mode control; 2: position mode control, default value: 0
  std::vector<float> q;       // Vector to store the desired angles (in radians).
  std::vector<float> dq;      // Vector to store desired speed (in radians per second).
  std::vector<float> tau;     // Vector to store desired output torque (in Newton meters).
  std::vector<float> Kp;      // Vector to store desired position stiffness (in Newton meters per radians).
  std::vector<float> Kd;      // Vector to store desired speed stiffness (in Newton meters per radians per second)
};

// Aliases of smart pointer types
typedef std::shared_ptr<RobotCmd> RobotCmdPtr;
typedef std::shared_ptr<RobotCmd const> RobotCmdConstPtr;
```

**Code example:**

```cpp
#include <thread>

// Include the limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
    // If initialization fails, exit the program
    exit(1);
  }

  // Get the number of motors in the robot
  uint32_t motor_num = pf->getMotorNumber();

  // Create a RobotCmd object containing the number of motors in the robot
  RobotCmd cmd(motor_num);

  // Publish a command
  pf->publishRobotCmd(cmd);

  // 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 Introduction to subscribeSensorJoy Interface

| Function | subscribeSensorJoy |
| --- | --- |
| Function prototype | `void subscribeSensorJoy(std::function cb);` |
| Function overview | This method is used for subscribing to data from the robot's remote controller in real-world deployments. When the robot receives data from the remote controller, the specified callback function is invoked, and the robot system then passes pointers to the SensorJoy structure, which contains the remote controller data, to the callback function for processing. |
| Parameters | cb: A callback function used for receiving data from the robot's remote controller. The parameter type of this callback function is SensorJoyConstPtr, which is a shared pointer to a constant SensorJoy structure. |
| Return value | None |

**Notes:**

- The prototype of the SensorJoy data structure is as follows:

```cpp
/**
 * @struct SensorJoy
 *
 * @brief A structure containing data from the robot remote controller.
 *
 * This struct contains the timestamp information associated with the remote controller, as well as the joystick and button values.
 */
struct SensorJoy {
    uint64_t stamp;                     // Timestamp associated with the sensor input, in nanoseconds.
    std::vector<float> axes;            // Value denoting the joystick.
    std::vector<int32_t> buttons;       // Value denoting the button state.
};

// Definition of the types of SensorJoy smart pointers
typedef std::shared_ptr<SensorJoy> SensorJoyPtr;
typedef std::shared_ptr<const SensorJoy> SensorJoyConstPtr;
```

- Mapping of remote controller joysticks

| Joysticks      | Axes Index  |
|----------------|-------------|
| left_horizon   |      0      |
| left_vertival  |      1      |
| right_horizon  |      2      |
| right_vertival |      3      |

- Mapping of the remote controller buttons

|      Button    | Buttons Index  |
|----------------|----------------|
|      X(A)      |      0         |
|      〇(B)     |      1         |
|      口(X)     |      2         |
|      △(Y)      |      3         |
|       L1       |      4         |
|       L2       |      6         |
|       R1       |      7         |
|       R2       |      5         |
|     SELECT     |      8         |
|      START     |      9         |
|       UP       |      12        |
|      DOWN      |      13        |
|      LEFT      |      14        |
|      RIGHT     |      15        |
|      MENU      |      16        |
|      BACK      |      17        |

- When configuring your own button logic, it is recommended not to conflict with the system's reserved button functions. The system's reserved button functions in the developer mode:

| Button                  | Function Description
|-------------------------|-------------------------------
| L1 + R1                 | Zero Calibration
| R1 + Left               | Developer Mode
| R1 + Right              | Turn on Motion Control Mode
| Left + Right Joysticks  | Emergency Stop
| Right Joystick          | Release Emergency Stop

**Code example:**

```cpp
#include <thread>

// Include the limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // /If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
    // If initialization fails, exit the program
    exit(1);
  }

  // Subscribe to the robot's remote control data
  pf->subscribeSensorJoy([&](const limxsdk::SensorJoyConstPtr &joy) {
    // Press L1 & R1
    if (joy->buttons[4] == 1 && joy->buttons[7] == 1)
    {
      // Execute related operations here
    }

    // Process stick 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];
  });

  // 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 Introduction to subscribeDiagnosticValue Interface

| Function | subscribeDiagnosticValue |
| --- | --- |
| Function prototype | `void subscribeDiagnosticValue(std::function cb);` |
| Function overview | This method is used to subscribe to the robot's **diagnostic values and state information** in the physical robot deployment. When the robot delivers a diagnostic value, the system invokes the specified callback function, which then process the diagnostic values contained in pointers of the DiagnosticValue structure constant. It enables real-time monitor of robot health and reacts promptly to deal with possible problems. |
| Parameters | cb: A callback function to receive the robot diagnostic values. The parameter type is `DiagnosticValueConstPtr`, a shared pointer to the `DiagnosticValue` structure constant. The `DiagnosticValue` structure contains information about the robot diagnostic values, including timestamp, level, name, code, and message field. |
| Return value | None |

**Notes:**

- The prototype of the DiagnosticValue data structure is as follows:

```cpp
C++
/**
 * @struct DiagnosticValue
 *
 * @brief A structure that denotes diagnostic values
 *
 * This structure contains information about the diagnostic level, name, code, and message.
 */
struct DiagnosticValue {
  enum { OK = 0 };         // Diagnostic level for normal state
  enum { WARN = 1 };       // Diagnostic level for warning state
  enum { ERROR = 2 };      // Diagnostic level for error state

  uint64_t stamp;          // Timestamp in nanoseconds
  int32_t level;           // Level associated with the diagnostic value
  std::string name;        // Name of the diagnostic value
  int32_t code;            // Code corresponding to the diagnostic value.
  std::string message;     // Detailed message associated with the diagnostic value.
};

// DiagnosticValue Definition of types of DiagnosticValue smart pointers
typedef std::shared_ptr<DiagnosticValue> DiagnosticValuePtr;
typedef std::shared_ptr<DiagnosticValue const> DiagnosticValueConstPtr;
```

- Common diagnostic data

```bash
| name        | level  | code | message
|-------------|--------|------|---------------------------------
| version     | OK     |  0   | - Contains software version information
|-------------|--------|------|---------------------------------
| imu         | OK     |  0   | - IMU in normal operation
| imu         | ERROR  | -1   | - IMU in abnormal operation, containing abnormal information
|-------------|--------|------|---------------------------------
| ethercat    | OK     |  0   | - EtherCAT in normal operation
| ethercat    | ERROR  | -1   | - EtherCAT in abnormal operation, containing abnormal information
|-------------|--------|------|--------------------
| calibration | OK     |  0   | - Zero calibration completed
| calibration | WARN   |  1   | - Warning: Robot in zero calibration
| calibration | ERROR  | -1   | - Error: Robot zero calibration fails, containing causes of failure
|-------------|--------|------|----------------------------------
```

**Code example:**

```cpp
#include <thread>

// Include the limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"
// Use the limxsdk namespace to simplify references to the PointFoot
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address

  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

    // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
    // If initialization fails, exit the program
    exit(1);
  }

  // Subscribe to the robot diagnostic data
  pf->subscribeDiagnosticValue([&](const DiagnosticValueConstPtr& msg) {
    // Robot diagnostic values are processed here
    // For example, appropriate action can be taken based on the level and message of the diagnostic value
    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;
  });

  // 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 Introduction to the setRobotLightEffect Interface

| Function | setRobotLightEffect |
| --- | --- |
| Function prototype | bool setRobotLightEffect(int effect); |
| Function overview | This method is used to **set the robot's light effect** in the robot deployment. |
| Parameters | effect: An integer denoting the desired robot light effect, as defined in the enumeration of `PointFoot:: LightEffect`. |
| Return value | bool: Indicate whether the robot light effect has been set successfully. |

**Notes:**

Enumerated definition of PointFoot::LightEffect:

```cpp
enum LightEffect : int {
    STATIC_RED = 0,     // Static red light
    STATIC_GREEN,       // Static green light
    STATIC_BLUE,        // Static blue light
    STATIC_CYAN,        // Static cyan light
    STATIC_PURPLE,      // Static purple light
    STATIC_YELLOW,      // Static yellow light
    STATIC_WHITE,       // Static white light
    LOW_FLASH_RED,      // Flashing red light (slow)
    LOW_FLASH_GREEN,    // Flashing green light (slow)
    LOW_FLASH_BLUE,     // Flashing blue light (slow)
    LOW_FLASH_CYAN,     // Flashing cyan light (slow)
    LOW_FLASH_PURPLE,   // Flashing purple light (slow)
    LOW_FLASH_YELLOW,   // Flashing yellow light (slow)
    LOW_FLASH_WHITE,    // Flashing white light (slow)
    FAST_FLASH_RED,     // Flashing red light (fast)
    FAST_FLASH_GREEN,   // Flashing green light (fast)
    FAST_FLASH_BLUE,    // Flashing blue light (fast)
    FAST_FLASH_CYAN,    // Flashing cyan light (fast)
    FAST_FLASH_PURPLE,  // Flashing purple light (fast)
    FAST_FLASH_YELLOW,  // Flashing yellow light (fast)
    FAST_FLASH_WHITE    // Flashing white light (fast)
};
```

**Code example:**

```cpp
#include <thread>

// Include the limxsdk::PointFoot header to import the PointFoot class
#include "limxsdk/pointfoot.h"

// Use the limxsdk namespace to simplify references to the PointFoot class
using namespace limxsdk;

int main(int argc, char *argv[]){
  // Get the singleton instance of the PointFoot class
  PointFoot* pf = PointFoot::getInstance();

  // Default robot IP address
  std::string robot_ip = "127.0.0.1";
  if (argc > 1)
  {
    // If command line arguments are provided, use the command line arguments as the robot IP address
    robot_ip = argv[1];
  }

  // Initialize the communication runtime environment for the motion control algorithm program
  if (!pf->init(robot_ip))
  {
    // If initialization fails, exit the program
    exit(1);
  }

  // Set robot light effect as static red light
  robot->setRobotLightEffect(limxsdk::PointFoot::STATIC_RED);

  // 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 Reference Routines

   - Single-joint control routines:

   https://github.com/limxdynamics/pointfoot-sdk-lowlevel/blob/master/examples/pf_joint_move.cpp

   - Multi-joint control routines:

   https://github.com/limxdynamics/pointfoot-sdk-lowlevel/blob/master/examples/pf_groupJoints_move.cpp

## 4.2 Python Interface User Guide

### 4.2.1 Overview

[The Python motion control algorithm development interfaces](https://github.com/limxdynamics/pointfoot-sdk-lowlevel/tree/master/python3), which provide the same functions as the C++ interfaces, **enable developers unfamiliar with the C++ programming language to develop motion control algorithms with Python**.

As an easy-to-learn programming language, Python features a concise and clear syntax and rich third-party libraries, allowing developers to get started more quickly and implement algorithms promptly. Through the Python interfaces, developers can leverage the language's dynamic characteristics to conduct prototype design and experimental verification in a quick manner and to accelerate the iteration and optimization of algorithms. Moreover, the cross-platform nature and strong ecosystem of Python make it possible to widely apply motion control algorithms to diverse platforms and environments.

In addition, the flexibility of Python facilitates the quick deployment of reinforcement learning (RL) models to both the simulation and physical robot environments. Developers can use Python to effortlessly integrate RL models into various simulation platforms and physical hardware, thus, to swiftly iterate and verify the performance of algorithms.

### 4.2.2 Installing Motion Control Development Libraries

- Linux x86_64 environment

  ```Bash
  git clone https://github.com/limxdynamics/pointfoot-sdk-lowlevel.git
  pip install pointfoot-sdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
  ```

- Linux aarch64 environment

  ```Bash
  git clone https://github.com/limxdynamics/pointfoot-sdk-lowlevel.git
  pip install pointfoot-sdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
  ```

- Windows environment

  ```Bash
  git clone https://github.com/limxdynamics/pointfoot-sdk-lowlevel.git
  pip install pointfoot-sdk-lowlevel/python3/win/limxsdk-*-py3-none-any.whl
  ```

### 4.2.3 Introduction to `__init__`  Interface

| Function | `__init__` |
| --- | --- |
| Function prototype | def `__init__`(self, robot_type: robot.RobotType) |
| Function overview | Specifies the robot type and create a corresponding local robot instance of the type during the initialization. |
| Parameters | robot_type: An enumerated value that denotes the robot type, with the point-foot robot as RobotType.PointFoot. |
| Return value | None |
| Notes | None |

**Code example:**

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

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

### 4.2.4 Introduction to init Interface

| Function | init |
| --- | --- |
| Function prototype | def init(self, robot_ip: str = "127.0.0.1") |
| Function overview | This function initializes the communication runtime environment of the motion control algorithm program. The function is invoked to complete initialization before the main function calls other interfaces. |
| Parameters | robot_ip_address: The IP address of the robot. It is usually set to "127.0.0.1" for simulation and probably "10.192.1.2" for a physical robot. |
| Return value | Success: Return True<br>Failure: Return False |
| Notes | None |

**Code example:**

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

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

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

    # Use the IP address to initialize the robot's communication runtime environment
    if not robot.init(robot_ip):
        sys.exit()
```

### 4.2.5 Introduction to getMotorNumber Interface

| Function | getMotorNumber |
| --- | --- |
| Function prototype | def getJointLimit(self, timeout: float = -1.0) |
| Function overview | This function retrieves the number of motors in the robot. |
| Parameters | None |
| Return value | Returns an unsigned integer representing the total motor count. |
| Notes | Generally, there are six motors in a point-foot robot |

**Code example:**

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

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

    robot_ip = "127.0.0.1"
    # Check if the command-line argument is provided as the robot IP

    if len(sys.argv) > 1:
        robot_ip = sys.argv[1]

    # Use robot_ip to initialize the robot

    if not robot.init(robot_ip):
        sys.exit()

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

### 4.2.6 Introduction to subscribeImuData Interface

| Function | subscribeImuData |
| --- | --- |
| Function prototype | def subscribeImuData(self, callback: Callable[[datatypes.ImuData], Any]) |
| Function overview | Subscribe to the robot's IMU data and invoke the specified callback function when the new IMU data is received. |
| Parameters | callback: Callback function to process new IMU data. |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

The prototype of the datatypes.ImuData data structure is as follows:

```python
import sys

class ImuData(object):
    __slots__ = ['stamp','acc','gyro','quat']
    def __init__(self):
        self.stamp = 0 # Timestamp, in nanoseconds, generally denotes the time when the data was recorded or generated.
        self.acc = [0. for x in range(0, 3)]  # Used to store the accelerometer data in the inertial measurement unit (IMU) to track the linear acceleration along the three axes.
        self.gyro = [0. for x in range(0, 3)] # Used to store the gyroscope data in the IMU to track the angular or rotational velocity.
        self.quat = [0. for x in range(0, 4)] # Used to store the quaternion data in the IMU, representing orientation in 3D space (w, x, y, z).
```

**Code example:**

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

class RobotReceiver:
    # 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 PointFoot
    robot = Robot(RobotType.PointFoot)

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to process the callback
    receiver = RobotReceiver()

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

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

    # Sleep for 1 second to prevent program exit
    import time
    while True:
        time.sleep(1)
```

### 4.2.7 Introduction to subscribeRobotState Interface

| Function | subscribeRobotState |
| --- | --- |
| Function prototype | def subscribeRobotState(self, callback: Callable[[datatypes.RobotState], Any]) |
| Function overview | Subscribe to robot state updates. |
| Parameters | callback: A callback function that will be invoked when the robot state updates are received. Arguments of the callback function point to the datatypes.RobotState object. - The order of corresponding motors for the command data array is as follows: - Point - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint<br> 3: abad_R_Joint, 4: hip_R_Joint, 5: knee_R_Joint - Wheel - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: wheel_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: wheel_R_Joint - Sole - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: ankle_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: ankle_R_Joint - The data structure fields of datatypes.RobotState are as follows: - stamp: Timestamp, which generally denotes the time when data was recorded or generated. - tau: Vector to store currently estimated output torque (in Newton meters). - q: Vector to store the current angle (in radians). - dq: Vector to store the current speed (in radians per second). |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

The prototype of the datatypes.RobotState data structure is as follows:

```python
import sys

class RobotState(object):
    __slots__ = ['stamp','tau','q','dq']
    def __init__(self):
        self.stamp = 0 # Timestamp, in nanoseconds, generally denotes the time when the data was recorded or generated.
        self.tau = []  # Vector to store currently estimated output torque (in Newton meters).
        self.q = []    # Vector to store the current angle (in radians)
        self.dq = []   # Vector to store the current speed (in radians per second)
```

**Code example:**

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

class RobotReceiver:
    # Callback function to receive the robot 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 PointFoot
    robot = Robot(RobotType.PointFoot)

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to process the callback
    receiver = RobotReceiver()

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

    # Subscribe to the robot state
    robot.subscribeRobotState(robotStateCallback)

    # Sleep for 1 second to prevent program exit
    import time
    while True:
        time.sleep(1)
```

### 4.2.8 Introduction to publishRobotCmd Interface

| Function | publishRobotCmd |
| --- | --- |
| Function prototype | def publishRobotCmd(self, cmd: datatypes.RobotCmd) |
| Function overview | Publish a command to control robot movements. |
| Parameters | cmd: Denote the RobotCmd object of the required robot command, which includes the following fields:  - stamp: Timestamp that generally denotes the time when the data was recorded or generated, in nanoseconds. - mode：robot mode control: 0: torque mode control; 1: speed mode control; 2: position mode control, default value: 0 - q: Vector to store the desired angles (in radians). - dq: Vector to store desired speed (in radians per second). - tau: Vector to store desired output torque (in Newton meters). - Kp: Vector to store desired position stiffness (in Newton meters per radians). - Kd: Vector to store desired speed stiffness (in Newton meters per radians per second)  The order of corresponding motors for the command data array is as follows:  - Point - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint<br> 3: abad_R_Joint, 4: hip_R_Joint, 5: knee_R_Joint - Wheel - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: wheel_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: wheel_R_Joint - Sole - foot<br> 0: abad_L_Joint, 1: hip_L_Joint, 2: knee_L_Joint, 3: ankle_L_Joint<br> 4: abad_R_Joint, 5: hip_R_Joint, 6: knee_R_Joint, 6: ankle_R_Joint |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

The prototype of the datatypes.RobotCmd data structure is as follows:

```python
import sys

class RobotCmd(object):
    __slots__ = ['stamp','mode','q','dq','tau','Kp','Kd']
    def __init__(self):
        self.stamp = 0 # Timestamp (in nanoseconds), generally denoting the time when data was recorded or generated.
        self.mode = [] # The robot's desired working mode.
        self.q = []    # Vector to store the desired angles (in radians).
        self.dq = []   # Vector to store desired speed (in radians per second).
        self.tau = []  # Vector to store desired output torque (in Newton meters).
        self.Kp = []   # Vector to store desired position stiffness (in Newton meters per radians).
        self.Kd = []   # Vector to store desired speed stiffness (in Newton meters per radians per second)
```

**Code example:**

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

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

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Get information about joint offset, joint limit, and motor number
    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 the timestamp, control mode, joint position, speed, torque, 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 robot command
        rate.sleep()  # Control loop frequency
```

### 4.2.9 Introduction to subscribeSensorJoy Interface

| Function | subscribeSensorJoy |
| --- | --- |
| Function prototype | def subscribeSensorJoy(self, callback: Callable[[datatypes.SensorJoy], Any]) |
| Function overview | This method is used to subscribe to data from the robot's remote controller in the robot deployment. When the robot receives data from the remote controller, the specified callback function is invoked, and the robot system then passes pointers to the datatypes.SensorJoy, which contains the remote controller data, to the callback function for processing. |
| Parameters | callback: A callback function that receives data from the robot's remote control. The parameter type of the callback function is datatypes.SensorJoy. |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

- The prototype of the datatypes.SensorJoy data structure is as follows:

```python
import sys

class SensorJoy(object):
    __slots__ = ['stamp','axes','buttons']
    def __init__(self):
        self.stamp = 0     # Timestamp associated with the sensor input, in nanoseconds.
        self.axes = []     # Value denoting the stick.
        self.buttons = []  # Value denoting button state.
```

- Mapping of remote controller sticks

```bash

| Joystick       | Axes Index  |
|----------------|-------------|
| left_horizon   |      0      |
|----------------|-------------|
| left_vertival  |      1      |
|----------------|-------------|
| right_horizon  |      2      |
|----------------|-------------|
| right_vertival |      3      |
|----------------|-------------|

```

- Mapping of remote controller buttons

```bash

|     Button     | Buttons Index  |
|----------------|----------------|
|       X(A)     |      0         |
|----------------|----------------|
|       〇(B)    |      1         |
|----------------|----------------|
|       口(X)    |      2         |
|----------------|----------------|
|       △(Y)     |      3         |
|----------------|----------------|
|        L1      |      4         |
|----------------|----------------|
|        L2      |      6         |
|----------------|----------------|
|        R1      |      7         |
|----------------|----------------|
|        R2      |      5         |
|----------------|----------------|
|      SELECT    |      8         |
|----------------|----------------|
|       START    |      9         |
|----------------|----------------|
|        UP      |      12        |
|----------------|----------------|
|       DOWN     |      13        |
|----------------|----------------|
|       LEFT     |      14        |
|----------------|----------------|
|       RIGHT    |      15        |
|----------------|----------------|
|       MENU     |      16        |
|----------------|----------------|
|       BACK     |      17        |
|----------------|----------------|

```

- When configuring your own button logic, it is recommended not to conflict with the system's reserved button functions. The system's reserved button functions in the developer mode:

```bash

| Button                  | Function Description
|-------------------------|-------------------------------
| L1 + R1                 | Zero Calibration
|-------------------------|-------------------------------
| R1 + Left               | Developer Mode
|-------------------------|-------------------------------
| R1 + Right              | Turn on RL Motion Control Mode
|-------------------------|-------------------------------
| R1 + Up                 | Turn on MB Motion Control Mode
|-------------------------|-------------------------------
| Left + Right Joysticks  | Emergency Stop
|-------------------------|-------------------------------
| Right Joystick          | Release Emergency Stop
|-------------------------|-------------------------------

```

**Code example:**

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

class RobotReceiver:
    # Callback function to receive the remote control 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 PointFoot
    robot = Robot(RobotType.PointFoot)

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to process the callback
    receiver = RobotReceiver()

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

    # Subscribe to the robot remote control data
    robot.subscribeSensorJoy(sensorJoyCallback)

    # Sleep for 1 second to prevent program exit
    import time
    while True:
        time.sleep(1)
```

### 4.2.10 Introduction to the subscribeDiagnosticValue interface

| Function | subscribeDiagnosticValue |
| --- | --- |
| Function prototype | def subscribeDiagnosticValue(self, callback: Callable[[datatypes.DiagnosticValue], Any]) |
| Function overview | This method is used to subscribe to the robot's diagnostic values and state information in the robot deployment. When the robot delivers a diagnostic value, the system invokes the specified callback function, which then process the diagnostic values contained in structure object of datatypes.DiagnosticValue. It enables real-time monitor of robot health and reacts promptly to deal with possible problems. |
| Parameters | callback: A callback function to receive the robot diagnostic values. The parameter type is datatypes.DiagnosticValue, whose structure contains information about the robot diagnostic values, including timestamp, level, name, code, and message field. |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

The prototype of the datatypes.DiagnosticValue data structure is as follows:

```python
import sys

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

- Common diagnostic data

```bash
| name        | level  | code | message
|-------------|--------|------|--------------------
| version     | OK     |  0   | - Contains software version information
|-------------|--------|------|--------------------
| imu         | OK     |  0   | - IMU in normal operation
| imu         | ERROR  | -1   | - IMU in abnormal operation, containing abnormal information
|-------------|--------|------|--------------------
| ethercat    | OK     |  0   | - EtherCAT in normal operation
| ethercat    | ERROR  | -1   | - EtherCAT in abnormal operation, containing abnormal information

|-------------|--------|------|--------------------
| calibration | OK     |  0   | - Zero calibration completed
| calibration | WARN   |  1   | - Warning: Robot in zero calibration
| calibration | ERROR  | -1   | - Error: Robot zero calibration fails, containing causes of failure
|-------------|--------|------|--------------------
```

**Code example:**

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

class RobotReceiver:
    # Callback function to receive the 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 PointFoot
    robot = Robot(RobotType.PointFoot)

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Create a RobotReceiver instance to process the callback

    receiver = RobotReceiver()

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

    # Subscribe to the robot diagnostic information
    robot.subscribeDiagnosticValue(diagnosticValueCallback)

    # Sleep for 1 second to prevent program exit
    import time
    while True:
        time.sleep(1)
```

### 4.2.11 Introduction to setRobotLightEffect Interface

| Function | setRobotLightEffect |
| --- | --- |
| Function prototype | def setRobotLightEffect(self, effect: datatypes.LightEffect) |
| Function overview | This method is used to set the robot's light effect in the physical robot deployment. |
| Parameters | effect: An integer denoting the desired robot light effect, as defined in the enumeration of `PointFoot:: LightEffect`. |
| Return value | Success: Return True<br>Failure: Return False |

**Notes:**

Enumerated definition of datatypes.LightEffect:

```python
class LightEffect(Enum):
    STATIC_RED        = 0   # Static red light
    STATIC_GREEN      = 1   # Static green light
    STATIC_BLUE       = 2   # Static blue light
    STATIC_CYAN       = 3   # Static cyan light
    STATIC_PURPLE     = 4   # Static purple light
    STATIC_YELLOW     = 5   # Static yellow light
    STATIC_WHITE      = 6   # Static white light
    LOW_FLASH_RED     = 7   # Flashing red light (slow)
    LOW_FLASH_GREEN   = 8   # Flashing green light (slow)
    LOW_FLASH_BLUE    = 9   # Flashing blue light (slow)
    LOW_FLASH_CYAN    = 10  # Flashing cyan light (slow)
    LOW_FLASH_PURPLE  = 11  # Flashing purple light (slow)
    LOW_FLASH_YELLOW  = 12  # Flashing yellow light (slow)
    LOW_FLASH_WHITE   = 13  # Flashing white light (slow)
    FAST_FLASH_RED    = 14  # Flashing red light (fast)
    FAST_FLASH_GREEN  = 15  # Flashing green light (fast)
    FAST_FLASH_BLUE   = 16  # Flashing blue light (fast)
    FAST_FLASH_CYAN   = 17  # Flashing cyan light (fast)
    FAST_FLASH_PURPLE = 18  # Flashing purple light (fast)
    FAST_FLASH_YELLOW = 19  # Flashing yellow light (fast)
    FAST_FLASH_WHITE  = 20  # Flashing white light (fast)
```

**Code example:**

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

class RobotReceiver:

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

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

    # Use robot_ip to initialize the robot
    if not robot.init(robot_ip):
        sys.exit()

    # Set the robot light effect as static right light
    robot.setRobotLightEffect(datatypes.LightEffect.STATIC_RED)
```

### 4.2.12 Reference Routines

- Reference routines of Python interfaces
  -   https://github.com/limxdynamics/pointfoot-sdk-lowlevel/blob/master/python3/amd64/example.py

---

## 5. High-Level Application Development Interface

## 5.1 Overview

In `Remote Control Mode`, the robot receives user commands via WebSocket communication on port `5000`. These commands include actions such as making the robot stand up, squat, or walk. WebSocket is a real-time communication protocol that establishes a persistent connection between the robot and the user client, enabling fast and efficient transmission of control information and data. As illustrated in the diagram below:

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457429377_image.png)

## 5.2 Communication Protocol Format

When the robot receives client commands via WebSocket, it uses a JSON-based data protocol for information exchange. This approach offers significant advantages: WebSocket is a full-duplex communication protocol that establishes a real-time, low-latency connection between the client and server, making it ideal for applications requiring frequent interactions. JSON, with its concise and highly readable structure, ensures intuitive and clear data transmission while offering cross-platform and cross-language compatibility. The combination of WebSocket and JSON not only transcends programming language constraints, making it suitable for various devices and systems, but also enhances development flexibility and ease of maintenance.

- Request Data Format Includes the Following Fields：

  - `accid`：A unique identifier for the robot, representing its unique identity.；

  - `title`：he message title, prefixed with"`request_`"；

  - `timestamp`：Timestamp when the message was sent, in milliseconds；

  - `guid`： A unique identifier for the command, used to distinguish between different requests. For synchronous interfaces, the guid value must be included in the "response_xxx" response message, so that the value can be returned to the client. After receiving the response message, the client can compare the guid values in the request and response to determine if the command has been completed.

  - `data`：Contains the data content for the request command. Depending on specific requirements, it can include multiple subfields to store necessary data, such as parameters for actions or text content for messages.

  - Example:

  - ```Plain
    {
      "accid": "PF_TRON1A_042", # The unique serial number of the robot.
      "title": "request_xxx",   # The message title, prefixed with "`request_`".
      "timestamp": 1672373633989, # Timestamp when the message was sent, in milliseconds.
      "guid": "746d937cd8094f6a98c9577aaf213d98", # A unique identifier for the command, allowing differentiation between various command requests.
      "data": {}  # Contains the data content for the request.
    }
    ```

- Response Data Format Includes the Following Fields：

  - `accid`：A unique identifier for the robot, representing its unique identity；

  - `title`：he message title, prefixed with"`response_`"；

  - `timestamp`：Timestamp when the message was sent, in milliseconds；

  - `guid`：Same as `guid` value in Request Data Format；

  - `data`：At least one "result" subfield should be included to store the execution result data of the requested command. If necessary, additional subfields can also be included, such as error codes and error messages, to describe information about the operation result.

  - Example:

  - ```JSON
    {
      "accid": "PF_TRON1A_042",   # Robot's unique serial number, identifying its unique identity;
      "title": "response_xxx",  # The command name, prefixed with"`response_`"
      "timestamp": 1672373633989, # TTimestamp when the message was sent, in milliseconds；
      "guid": "746d937cd8094f6a98c9577aaf213d98", # Same as `guid` value with corresponding Request Data Format；
      "data": { # To store the specific data content of the response command.
        "result": "success"  # "result" Used to store whether the request command was successfully processed. Its value can be either: "success" or "fail_xxx".
      }
    }
    ```

- Message Push:

  It refers to the process where the robot actively sends information to the client. This information may include the robot's serial number, current operational status, actions being performed, and other related data. By actively pushing this information to the client in a timely manner, the robot helps the client better understand its operational state, thereby enhancing the client's ability to utilize the services it provides.

  The data format for message pushing includes the following fields:

  - `accid`：Robot's unique serial number, identifying its unique identity；

  - `title`：The message title, prefixed with"`notify_`"；

  - `timestamp`：Timestamp when the message was sent, in milliseconds;

  - `guid`：Unique identifier for the message, to match responses；

  - `data`：specific data related to the push message；

  - example:

  - ```JSON
    {
      "accid": "PF_TRON1A_042",   # Robot's unique serial number, identifying its unique identity;
      "title": "notify_xxx",  # he message title, prefixed with"`notify_`"
      "timestamp": 1672373633989, # Timestamp when the message was sent, in milliseconds;
      "guid": "746d937cd8094f6a98c9577aaf213d98", # Unique identifier for the message, to match responses；
      "data": { } # specific data related to the push message；
    }
    ```

## 5.3 View Software Serial Number (ACCID)

   - Connect to the Robot's Wireless Network:
     - After powering on the robot, connect your personal computer to the robot's Wi-Fi. The network name typically follows the format "PF_TRON1A_xxx".
     - Enter the Wi-Fi password：`12345678`
   - Open a browser and go to`http://10.192.1.2:8080`to access the "Robot Information Page"，where you can view the robot's details. As shown below, the displayed SN (serial number) on the page will be `PF_TRON1A_131`，and `PF_TRON1A_131` is the robot's software serial number (ACCID).。
      - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457470593_image.png)

## 5.4 Communication Testing Method

Postman is a popular API development environment that can be used to test WebSocket interfaces. To test the WebSocket interface using Postman, follow the steps below:

- Install Postman:Download the installation file from：https://www.postman.com/downloads/?utm_source=postman-home；
- Open Postman and create a new WebSocket request.
- Connect to the Robot's Wireless Network:
  - After powering on the robot, use your personal computer to connect to the robot's Wi-Fi. The network name typically follows the format "W P_TRON1A_xxx".
  - Enter the Wi-Fi password:`12345678`
- Enter the WebSocket Interface Address: In the request's URL field, input the WebSocket interface address, for example, ws://10.192.1.2:5000.
- nput the Command Request in the "Message" Field:
- Click the "Send" Button to send the command request;
- After sending the command, you can receive a response message from the server. Use Postman's response window to view the data returned by the server and check if it matches the expected result.
- ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457517465_image.png)

## 5.5 Protocol Interface Definition

该The robot interface design follows the same process and state transitions as the remote control, ensuring that the invocation order, response timing, and state transitions strictly align with the remote control's control logic. Users can experience a seamless interface interaction that mimics the intuitive operation of using a remote control. Additionally, the interface supports smooth switching between the remote control and the API, enabling a unified and stable robot control experience.

![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457539465_image.png)

### 5.5.1 prepare mode

#### 5.5.1.1  request：request_stand_mode

   ```JSON
{
  "accid": "PF_TRON1A_075",  // Robot serial number, please modify to your robot's serial number
  "title": "request_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
   ```

#### 5.5.1.2  response：response_stand_mode

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"
}
   ```

#### 5.5.1.3 Message Push：notify_stand_mode

This message is actively pushed by the robot after the standing process either fails or completes.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_stand_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"
  }
}
   ```

### 5.5.2 Waking mode

#### 5.5.2.1 request：request_walk_mode

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
  }
}
   ```

#### 5.5.2.2 response：response_walk_mode

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"
}
   ```

#### 5.5.2.2 Message Push：notify_walk_mode

   The robot actively pushes this message after the standing process either fails or completes.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_walk_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"
  }
}
   ```

### 5.5.3 Control Walking

#### 5.5.3.1 Request: request_twist

   Please send commands at 30Hz or higher.

   ```JSON
{
  "accid": "PF_TRON1A_075",
  "title": "request_twist",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "x": 0.0,   // Forward/backward speed ratio, range [-1, 1]
    "y": 0.0,   // Lateral walking speed ratio, range [-1, 1]
    "z": 0.0    // Rotational angular velocity ratio, range [-1, 1]
  }
}
   ```

#### 5.5.3.2 response：none

#### 5.5.3.3 message push：notify_twist

The robot actively pushes this message after the walking process is failed.

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

### 5.5.4 Adjust Robot Height

#### 5.5.4.1 Request: request_base_height

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_base_height",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "direction": -1  // 1: Increase height, -1: Decrease height
                     // Each call to this request increases or decreases the robot's height by 5cm
  }
}
   ```

#### 5.5.4.2 Response: response_base_height

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_base_height",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_status: Indicates the robot's current state does not allow height adjustment
  }
}
   ```

#### 5.5.4.3 Push Notification: None

### 5.5.5 Sit Down

#### 5.5.5.1 Request: request_sitdown

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_sitdown",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
   ```

#### 5.5.5.2 Response: response_sitdown

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

#### 5.5.5.3 message push：notify_sitdown

    The robot actively pushes this message after the process is failed.

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

### 5.5.6 Enable Stair Mode

#### 5.5.6.1 request：request_stair_mode

This function is only applicable to TRON1 in Wheel-foot mode.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_stair_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: Start stair mode, false: end stair mode
  }
}
   ```

#### 5.5.6.2 response：response_stair_mode

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

#### 5.5.6.3 message push：none

### 5.5.7 Enable Marktime Mode

#### 5.5.7.1 Request: request_marktime_mode

This function is only applicable to TRON1 biped robots.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_marktime_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: Enable marktime mode, false: Disable marktime mode
  }
}
   ```

#### 5.5.7.2 Response: response_marktime_mode

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_marktime_mode",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_imu: IMU error, fail_motor: Motor error
  }
}
   ```

#### 5.5.7.3 Push Notification: none

### 5.5.8 Emergency Stop

#### 5.5.8.1 Request: request_emgy_stop

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
   ```

#### 5.5.8.2 Response: response_emgy_stop

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_emgy_stop",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_imu: IMU error, fail_motor: Motor error
  }
}
   ```

#### 5.5.8.3 Push Notification: none

### 5.5.9 Enable Odometry

#### 5.5.9.1 Request: request_enable_odom

This function is used to enable odometry pushing. Once enabled, the system will actively push odometry data (Note: Only wheel-legged robots have odometry).

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_enable_odom",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: Enable odometry, false: Disable odometry
  }
}
   ```

#### 5.5.9.2 Response: response_enable_odom

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_enable_odom",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_odom: Not supported
  }
}
   ```

#### 5.5.9.3 Push Notification: notify_odom

After enabling odometry, the system will actively push messages containing odometry data.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_odom",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "pose_orientation": [0.0, 0.0, 0.0, 0.0], // Pose [x, y, z, w]
      "pose_position": [0.0, 0.0, 0.0],         // Position [x, y, z] in m
      "twist_linear": [0.0, 0.0, 0.0],          // Linear velocity [x, y, z] in m/s
      "twist_angular": [0.0, 0.0, 0.0]          // Angular velocity [x, y, z] in rad/s
  }
}
   ```

### 5.5.10 Enable IMU Data

#### 5.5.10.1 Request: request_enable_imu

   This function is used to enable IMU data pushing. Once enabled, the system will actively push IMU data.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_enable_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "enable": true  // true: Enable IMU, false: Disable IMU
  }
}
   ```

#### 5.5.10.2 Response: response_enable_imu

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_enable_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_imu: IMU error
  }
}
   ```

#### 5.5.10.3 Push Notification: notify_imu

   After enabling IMU data, the system will actively push messages containing IMU status.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_imu",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "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]
  }
}
   ```

### 5.5.11 Fall Recovery

#### 5.5.11.1 Request: request_recover

   When the robot falls over, this interface can be called to make the robot automatically get up and return to walking mode.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "request_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {}
}
   ```

#### 5.5.11.2 Response: response_recover

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Command received successfully, starting recovery, fail_no_fallover: Not fallen over
  }
}
   ```

#### 5.5.11.3 Push Notification: notify_recover

   This message is pushed after the recovery process is completed.

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_recover",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
      "result": "success"  // success: Recovery successful, fail_recover: Recovery failed
  }
}
   ```

### 5.5.12 Light Effect

#### 5.5.12.1 Request: request_light_effect

This interface is used to set the robot's light effect. Users can send specific light effect commands to the robot through this interface, and the robot will adjust its light display according to the commands.

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

Request Parameter Description:

| Parameter Name | Type   | Description                                                  |
| -------------- | ------ | ------------------------------------------------------------ |
| data.effect    | Number | Light effect number, corresponding to different light display modes, with the following mapping:<br>1: STATIC_RED (Static red)<br>2: STATIC_GREEN (Static green)<br>3: STATIC_BLUE (Static blue)<br>4: STATIC_CYAN (Static cyan)<br>5: STATIC_PURPLE (Static purple)<br>6: STATIC_YELLOW (Static yellow)<br>7: STATIC_WHITE (Static white)<br>8: LOW_FLASH_RED (Low frequency flashing red)<br>9: LOW_FLASH_GREEN (Low frequency flashing green)<br>10: LOW_FLASH_BLUE (Low frequency flashing blue)<br>11: LOW_FLASH_CYAN (Low frequency flashing cyan)<br>12: LOW_FLASH_PURPLE (Low frequency flashing purple)<br>13: LOW_FLASH_YELLOW (Low frequency flashing yellow)<br>14: LOW_FLASH_WHITE (Low frequency flashing white)<br>15: FAST_FLASH_RED (High frequency flashing red)<br>16: FAST_FLASH_GREEN (High frequency flashing green)<br>17: FAST_FLASH_BLUE (High frequency flashing blue)<br>18: FAST_FLASH_CYAN (High frequency flashing cyan)<br>19: FAST_FLASH_PURPLE (High frequency flashing purple)<br>20: FAST_FLASH_YELLOW (High frequency flashing yellow)<br>21: FAST_FLASH_WHITE (High frequency flashing white) |

#### 5.5.12.2 Response: response_light_effect

   ```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "response_light_effect",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "result": "success"  // success: Successful, fail_light_effect: Failed
  }
}
   ```

#### 5.5.12.3 Push Notification: none

### 5.5.13 Global Messages

#### 5.5.13.1 Robot Basic Information

The robot's basic information is reported once per second, including the following:

- accid: Robot's serial number
- title: notify_robot_info
- timestamp: Timestamp when the message was sent, in milliseconds
- guid: Unique identifier for the message
- data: Contains the message content, example as follows:

```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_robot_info",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": {
    "accid": "PF_TRON1A_042",
    "sw_version": "robot-tron1-2.0.10.20241111103012",
    "imu": "OK",    # Robot IMU diagnostic information
    "camera": "OK", # Robot camera diagnostic information
    "motor": "OK",  # Robot motor diagnostic information
    "battery": 95,  # Robot battery level
    "status": "WALK" # Robot operating mode
  }
}
```

| Field      | Description                                                  |
| ---------- | ------------------------------------------------------------ |
| accid      | Robot serial number                                          |
| sw_version | Robot software version information                           |
| imu        | Robot IMU diagnostic information                             |
| camera     | Robot camera diagnostic information                          |
| motor      | Robot motor diagnostic information                           |
| battery    | Robot battery level                                          |
| status     | Robot operating mode: STAND, WALK, SIT, DAMPING, ROTATE, STAIR, ERROR_FALLOVER (fallen), RECOVER (recovering from fall), ERROR_RECOVER (fall recovery failed) |

#### 5.5.13.2 Illegal command message

When the robot receives a request instruction in an illegal format, it sends this message, which contains the following:
- accid: Robot's serial number
- title: notify_invalid_request
- timestamp: Timestamp when the message was sent, in milliseconds
- guid: Unique identifier for the message
- data: Contains the message content

Example:

```JSON
{
  "accid": "PF_TRON1A_042",
  "title": "notify_invalid_request",
  "timestamp": 1672373633989,
  "guid": "746d937cd8094f6a98c9577aaf213d98",
  "data": "Returns the original request instruction content to facilitate client troubleshooting"
}
   ```

## 5.6 Protocol interface call example

### 5.6.1 Linux C++ Example

- Installation dependencies: Taking Ubuntu 20.04 system as an example, install websocketpp, nlohmann/json and boost dependencies:

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

- Compile code

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

- Run program

- ```Bash
  ./websocket_client
  ```

- websocket_client.cpp

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

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

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

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

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

  // Connection handle for sending messages
  static connection_hdl current_hdl;

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

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

      // 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
  static void handle_commands() {
      while (!should_exit) {
          std::string command;
          std::cout << "Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:" << std::endl;
          std::getline(std::cin, command);  // Read user input

          if (command == "exit") {
              should_exit = true;  // Exit flag to stop the loop
              break;
          } else if (command == "stand") {
              send_request("request_stand_mode");  // Send stand mode request
          } else if (command == "walk") {
              send_request("request_walk_mode");  // Send walk mode request
          } else if (command == "twist") {
              float x, y, z;
              std::cout << "Enter x, y, z values:" << std::endl;
              std::cin >> x >> y >> z;  // Get twist values from user
              send_request("request_twist", {{"x", x}, {"y", y}, {"z", z}});
          } else if (command == "sit") {
              send_request("request_sitdown");  // Send sit down request
          } else if (command == "stair") {
              bool enable;
              std::cout << "Enable stair mode (true/false):" << std::endl;
              std::cin >> enable;  // Get stair mode enable flag from user
              send_request("request_stair_mode", {{"enable", enable}});
          } else if (command == "stop") {
              send_request("request_emgy_stop");  // Send emergency stop request
          } else if (command == "imu") {
              std::string enable;
              std::cout << "Enable IMU (true/false):" << std::endl;
              std::cin >> enable;  // Get IMU enable flag from user
              send_request("request_enable_imu", {{"enable", enable == "true" ? true : false}});
          }
      }
  }

  // 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 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>();
    }

      std::cout << "Received: " << msg->get_payload() << std::endl;  // Print received message
  }

  // 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

      // 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

      std::string server_uri = "ws://10.192.1.2: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;
  }
  ```

### 5.6.2 Python Example

- Environment preparation: Taking Ubuntu 20.04 system as an example, install the following dependencies

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

- run script

- ```Python
  python3 websocket_client.py
  ```

- websocket_client.py

- ```Python
  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

  # 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):
      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 ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:\n")

          if command == "exit":
              should_exit = True  # Set exit flag to stop the loop
              break
          elif command == "stand":
              send_request("request_stand_mode")  # Send stand mode request
          elif command == "walk":
              send_request("request_walk_mode")  # Send walk mode request
          elif command == "twist":
              # Get twist values from user
              x = float(input("Enter x value:"))
              y = float(input("Enter y value:"))
              z = float(input("Enter z value:"))
              send_request("request_twist", {"x": x, "y": y, "z": z})
          elif command == "sit":
              send_request("request_sitdown")  # Send sit down request
          elif command == "stair":
              # Get stair mode enable flag from user
              enable = input("Enable stair mode (true/false):").strip().lower() == 'true'
              send_request("request_stair_mode", {"enable": enable})
          elif command == "stop":
              send_request("request_emgy_stop")  # Send emergency stop request
          elif command == "imu":
              # Get IMU enable flag from user
              enable = input("Enable IMU (true/false):").strip().lower() == 'true'
              send_request("request_enable_imu", {"enable": enable})

  # WebSocket on_open callback
  def on_open(ws):
      print("Connected!")
      # Start handling commands in a separate thread
      threading.Thread(target=handle_commands, daemon=True).start()

  # WebSocket on_message callback
  def on_message(ws, message):
      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(
          "ws://10.192.1.2:5000",  # WebSocket server URI
          on_open=on_open,
          on_message=on_message,
          on_close=on_close
      )

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

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

### 5.6.3 JavaScript Example

- HTML page: To interact with the user, you can add an input box in the HTML page where the user can enter commands. The following is the index.html implementation:

- ```HTML
  <!DOCTYPE html>
  <html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>WebSocket Robot Control</title>
      <style>
          #commandInput {
              width: 400px; /* Adjust the width to make it wider */
              padding: 10px;
              font-size: 14px;
          }
      </style>
  </head>
  <body>
      <h2>Robot Control Commands</h2>
      <input type="text" id="commandInput" placeholder="Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu')">
      <p>Type a command and press Enter.</p>

      <script src="robotControl.js"></script>
  </body>
  </html>
  ```

- robotControl.js 实现：

- ```JavaScript
  // 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(),  // Current timestamp in milliseconds
          guid: generateGuid(),
          data: data
      };

      // Send the message through WebSocket if client is connected
      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 = '';  // Clear input field

              switch (command) {
                  case 'stand':
                      sendRequest('request_stand_mode');
                      break;
                  case 'walk':
                      sendRequest('request_walk_mode');
                      break;
                  case 'twist':
                      const x = parseFloat(prompt("Enter x value:"));
                      const y = parseFloat(prompt("Enter y value:"));
                      const z = parseFloat(prompt("Enter z value:"));
                      sendRequest('request_twist', {"x": x, "y": y, "z": z});
                      break;
                  case 'sit':
                      sendRequest('request_sitdown');
                      break;
                  case 'stair':
                      const enableStair = prompt("Enable stair mode (true/false):").toLowerCase() === 'true';
                      sendRequest('request_stair_mode', {"enable": enableStair});
                      break;
                  case 'stop':
                      sendRequest('request_emgy_stop');
                      break;
                  case 'imu':
                      const enableImu = prompt("Enable IMU (true/false):").toLowerCase() === 'true';
                      sendRequest('request_enable_imu', {"enable": enableImu});
                      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) {
      console.log("Received message:", event.data);
      const message = JSON.parse(event.data);
      if (!ACCID && message.accid) {
          ACCID = message.accid;
          console.log(`ACCID set to: ${ACCID}`);
      }
  }

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

  // Initialize WebSocket client
  function initWebSocket() {
      // Replace this URL with your WebSocket server URI
      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;
  ```

- Example Run program

  -  Save the `index.html` and `robotControl.js` files to the same directory, then open `index.html` in a browser and run it. You can view the received detailed information through the browser's developer tools.
  -  ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457620982_image.png)

### 5.6.4 Go Example

- First you need to install the `gorilla/websocket` package：

- ```JavaScript
  go get github.com/gorilla/websocket
  ```

- The Go implementation code is as follows:

- ```JavaScript
  package main

  import (
          "encoding/json"
          "fmt"
          "github.com/gorilla/websocket"
          "time"
          "strings"
          "github.com/google/uuid"
  )

  // Global variables
  var wsClient *websocket.Conn
  var shouldExit bool

  // Replace with your robot's serial number
  var ACCID = ""

  // Generate dynamic GUID
  func generateGUID() string {
          return uuid.New().String()
  }

  // Send WebSocket request with title and data
  func sendRequest(title string, data map[string]interface{}) {
          if data == nil {
                  data = make(map[string]interface{})
          }

          message := map[string]interface{}{
                  "accid":    ACCID,
                  "title":    title,
                  "timestamp": time.Now().UnixMilli(),
                  "guid":     generateGUID(),
                  "data":     data,
          }

          messageBytes, err := json.Marshal(message)
          if err != nil {
                  fmt.Println("Error marshaling message:", err)
                  return
          }

          // Send the message through WebSocket if client is connected
          if wsClient != nil {
                  err = wsClient.WriteMessage(websocket.TextMessage, messageBytes)
                  if err != nil {
                          fmt.Println("Error sending message:", err)
                  }
          }
  }

  // Handle user commands
  func handleCommands() {
          var command string
          for !shouldExit {
                  fmt.Println("Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:")
                  fmt.Scanln(&command)

                  command = strings.TrimSpace(command)
                  switch command {
                  case "exit":
                          shouldExit = true
                          return
                  case "stand":
                          sendRequest("request_stand_mode", nil)
                  case "walk":
                          sendRequest("request_walk_mode", nil)
                  case "twist":
                          var x, y, z float64
                          fmt.Println("Enter x value:")
                          fmt.Scanln(&x)
                          fmt.Println("Enter y value:")
                          fmt.Scanln(&y)
                          fmt.Println("Enter z value:")
                          fmt.Scanln(&z)
                          sendRequest("request_twist", map[string]interface{}{"x": x, "y": y, "z": z})
                  case "sit":
                          sendRequest("request_sitdown", nil)
                  case "stair":
                          var enable bool
                          fmt.Println("Enable stair mode (true/false):")
                          var input string
                          fmt.Scanln(&input)
                          enable = strings.ToLower(input) == "true"
                          sendRequest("request_stair_mode", map[string]interface{}{"enable": enable})
                  case "stop":
                          sendRequest("request_emgy_stop", nil)
                  case "imu":
                          var enable bool
                          fmt.Println("Enable IMU (true/false):")
                          var input string
                          fmt.Scanln(&input)
                          enable = strings.ToLower(input) == "true"
                          sendRequest("request_enable_imu", map[string]interface{}{"enable": enable})
                  }
          }
  }

  // WebSocket onOpen callback
  func onOpen(ws *websocket.Conn) {
          fmt.Println("Connected!")
          wsClient = ws
          go handleCommands()
  }

  // WebSocket onMessage callback
  func onMessage(ws *websocket.Conn, message []byte) {
          fmt.Println("Received message:", string(message))

          if ACCID == "" {
                  var msgMap map[string]interface{}
                  if err := json.Unmarshal(message, &msgMap); err != nil {
                          fmt.Println("Error parsing message:", err)
                          return
                  }
                  ACCID = msgMap["accid"].(string)
                  fmt.Printf("ACCID initialized from server: %s\n", ACCID)
          }
  }

  // WebSocket onClose callback
  func onClose(ws *websocket.Conn, code int, text string) {
          fmt.Println("Connection closed. Code:", code, "Message:", text)
  }

  // Connect to the WebSocket server
  func connectWebSocket() {
          url := "ws://10.192.1.2:5000" // WebSocket server URI

          conn, _, err := websocket.DefaultDialer.Dial(url, nil)
          if err != nil {
                  fmt.Println("Error connecting to WebSocket server:", err)
                  return
          }

          onOpen(conn)

          // Start receiving messages from the server
          go func() {
                  for {
                          _, message, err := conn.ReadMessage()
                          if err != nil {
                                  fmt.Println("Error reading message:", err)
                                  break
                          }
                          onMessage(conn, message)
                  }
          }()

          // Wait until WebSocket connection is closed
          select {}
  }

  // Main function
  func main() {
          defer func() {
                  if wsClient != nil {
                          wsClient.Close()
                  }
          }()

          // Connect to WebSocket server
          go connectWebSocket()

          // Block main goroutine to allow handling commands
          select {}
  }
  ```

### 5.6.5 Java Example

   ```java
import org.java-websocket.client.WebSocketClient;
import org.java-websocket.handshake.ServerHandshake;
import org.json.JSONObject;

import java.net.URI;
import java.util.Scanner;
import java.util.UUID;

public class WebSocketExample {

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

    // Atomic flag for graceful exit
    private static volatile boolean shouldExit = false;

    // WebSocket client instance
    private static WebSocketClient wsClient = null;

    // Generate dynamic GUID
    private static String generateGuid() {
        return UUID.randomUUID().toString();
    }

    // Send WebSocket request with title and data
    private static void sendRequest(String title, JSONObject data) {
        if (data == null) {
            data = new JSONObject();
        }

        // Create message structure with necessary fields
        JSONObject message = new JSONObject();
        message.put("accid", ACCID);
        message.put("title", title);
        message.put("timestamp", System.currentTimeMillis());  // Current timestamp in milliseconds
        message.put("guid", generateGuid());
        message.put("data", data);

        String messageStr = message.toString();

        // Send the message through WebSocket if client is connected
        if (wsClient != null && wsClient.isOpen()) {
            wsClient.send(messageStr);
        }
    }

    // Handle user commands
    private static void handleCommands() {
        Scanner scanner = new Scanner(System.in);
        while (!shouldExit) {
            System.out.println("Enter command ('stand', 'walk', 'twist', 'sit', 'stair', 'stop', 'imu') or 'exit' to quit:");
            String command = scanner.nextLine().trim();

            switch (command) {
                case "exit":
                    shouldExit = true;
                    break;
                case "stand":
                    sendRequest("request_stand_mode", null);
                    break;
                case "walk":
                    sendRequest("request_walk_mode", null);
                    break;
                case "twist":
                    System.out.print("Enter x value: ");
                    double x = scanner.nextDouble();
                    System.out.print("Enter y value: ");
                    double y = scanner.nextDouble();
                    System.out.print("Enter z value: ");
                    double z = scanner.nextDouble();
                    scanner.nextLine();  // Consume the newline
                    JSONObject twistData = new JSONObject();
                    twistData.put("x", x);
                    twistData.put("y", y);
                    twistData.put("z", z);
                    sendRequest("request_twist", twistData);
                    break;
                case "sit":
                    sendRequest("request_sitdown", null);
                    break;
                case "stair":
                    System.out.print("Enable stair mode (true/false): ");
                    boolean enableStair = scanner.nextLine().trim().equalsIgnoreCase("true");
                    JSONObject stairData = new JSONObject();
                    stairData.put("enable", enableStair);
                    sendRequest("request_stair_mode", stairData);
                    break;
                case "stop":
                    sendRequest("request_emgy_stop", null);
                    break;
                case "imu":
                    System.out.print("Enable IMU (true/false): ");
                    boolean enableImu = scanner.nextLine().trim().equalsIgnoreCase("true");
                    JSONObject imuData = new JSONObject();
                    imuData.put("enable", enableImu);
                    sendRequest("request_enable_imu", imuData);
                    break;
                default:
                    System.out.println("Invalid command. Try again.");
                    break;
            }
        }
    }

    // WebSocket onOpen callback
    private static void onOpen() {
        System.out.println("Connected!");
        // Start handling commands in a separate thread
        new Thread(WebSocketExample::handleCommands).start();
    }

    // WebSocket onMessage callback
    private static void onMessage(String message) {
        System.out.println("Received message: " + message);

        if (ACCID.isEmpty()) {
            try {
                JSONObject jsonMessage = new JSONObject(message);
                ACCID = jsonMessage.getString("accid");
                System.out.printf("ACCID initialized from server: %s%n", ACCID);
            } catch (Exception e) {
                System.out.println("Error parsing ACCID from message: " + e.getMessage());
            }
        }
    }

    // WebSocket onClose callback
    private static void onClose(int code, String reason, boolean remote) {
        System.out.println("Connection closed. Reason: " + reason);
    }

    public static void main(String[] args) {
        // WebSocket server URI
        URI serverUri = URI.create("ws://10.192.1.2:5000");

        // Create WebSocket client instance
        wsClient = new WebSocketClient(serverUri) {

            @Override
            public void onOpen(ServerHandshake handshakedata) {
                onOpen();
            }

            @Override
            public void onMessage(String message) {
                onMessage(message);
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                onClose(code, reason, remote);
            }

            @Override
            public void onError(Exception ex) {
                System.out.println("Error: " + ex.getMessage());
            }
        };

        // Connect to the WebSocket server
        wsClient.connect();
        System.out.println("Press Ctrl+C to exit.");
    }
}
   ```

---

## 6. Reinforcement Learning Environment Setup

## 6.1 Recommened hardware

| **Hardware components** | **Recommended specifications**     | **Instruction**                                              |
| ----------------------- | ---------------------------------- | ------------------------------------------------------------ |
| **GPU**                 | GeForce RTX 3080 (12 GB) or higher | 12 GB of GDDR6 video memory or higher, suitable for deep learning and reinforcement learning tasks.<br>[GeForce RTX 30 Series:](https://www.nvidia.com/en-us/geforce/graphics-cards/30-series/)![](https://support.limxdynamics.com/en/assets/images/image-26-e701069de4e66c389b26b3b70ad0382e.png)[GeForce RTX 40 Series：](https://www.nvidia.com/en-us/geforce/graphics-cards/40-series/)![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457741588_image.png)
 |
| **CPU**                 | At least a six-core processor      | Recommended Model:• Intel: Core i7-10700K or higher• AMD: Ryzen 5 5600X or higher |
| **Memory**              | 16 GB to 32 GB                     | System memory: 16 GB to 32 GB. Larger memory can support more complex simulation and training tasks, while improving system responsiveness. |
| **Storage**             | 512 GB to 1 TB NVMe SSD            | The SSD improves read and write speed, reduces load time and improves system performance. |
| **Operating System**    | Ubuntu 20.04 LTS                   | Ensure compatibility with GPU drivers.                       |
| **GPU Drivers**         | /                                  | Select the recommended driver version to install.            |
| **Python**              | Python 3.8 or higher               | Install the required Python packages and libraries (such as PyTorch). |
| **Network environment** | Github is accessible.              | The environment setup process requires downloading the model training code via GitHub. |

## 6.2 Installation

   - Install RL Environment in One Click

     - ```bash
       mkdir -p ~/limx_rl && cd ~/limx_rl && \
       sudo apt update && sudo apt install -y git && \
       if [ ! -d "pointfoot-legged-gym" ]; then \
         git clone https://github.com/limxdynamics/pointfoot-legged-gym.git; \
       fi && \
       cd pointfoot-legged-gym && bash install.sh && source ~/.bashrc
       ```

   - Verify Whether NVIDIA Driver is Installed Successfully

     - ```shell
       nvidia-smi
       ```

     - If the NVIDIA driver version and GPU information are displayed, the driver is installed successfully.

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457813111_image.png)

   - Verify Whether Isaac Gym is Installed Successfully

     - Run the Isaac Gym example to confirm installation and configuration.

     - ```bash
       conda activate pointfoot_legged_gym
       cd ~/limx_rl/isaacgym/python/examples
       python 1080_balls_of_solitude.py
       ```

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768457832974_image.png)

   - Verify Whether pointfoot-legged-gym is Installed Successfully

When you have installed the RL environment, the following contents are included under the `~/limx_rl` directory:

   ```bash
cd ~/limx_rl
.
├── isaacgym
└── pointfoot-legged-gym
   ```

## 6.3 Environment Setup

### 6.3.1 Install Graphics Card Drivers

   - Check the Current NVIDIA Drivers

     - If NVIDIA drivers are already installed on your system, you can check with the following command:

     - ```Bash
       nvidia-smi
       ```

     - If the NVIDIA driver version and GPU information are displayed, the driver is installed. If not, continue with the following steps.

   - Add the official NVIDIA driver PPA (Personal Package Archive):

     - ```Bash
       sudo add-apt-repository ppa:graphics-drivers/ppa
       sudo apt update
       ```

   - Use the following command to list the available NVIDIA driver versions:

     - ```Bash
       ubuntu-drivers devices
       ```

   - Install NVIDIA Drivers

Select the recommended driver version to install, for example:

   ```Bash
sudo apt install nvidia-driver-XXX
   ```

Replace `XXX` with the recommended driver version number, for example `nvidia-driver-535`.

   - Disable Nouveau Drivers (if Necessary): In some cases, you may need to disable the default Nouveau driver:

     - ```Bash
       sudo bash -c "echo blacklist nouveau > /etc/modprobe.d/blacklist-nouveau.conf"
       sudo bash -c "echo options nouveau modeset=0 >> /etc/modprobe.d/blacklist-nouveau.conf"
       sudo update-initramfs -u
       ```

   - Reboot System: After the driver installation is complete, reboot the system to activate the driver:

     - ```Bash
       sudo reboot
       ```

   - Verify Driver Installation: After the reboot, verify that the NVIDIA driver is installed successfully with the following command:

     - ```Bash
       nvidia-smi
       ```

     - If the NVIDIA driver version and GPU information are displayed, the driver is installed successfully.

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459019003_image.png)

### 6.3.2 Conda Environment Configuration

Conda is a cross-platform, open source package and environment management system that enables rapid creation of virtual environments where packages can be installed, run, and updated, effectively addressing the Python and dependent library version requirements of different software packages. Follow the following steps to download and install Conda:

- Download the Anaconda installation script:

  - Download the installation script for Anaconda using `wget` (select the version that is appropriate for your needs):

  - ```Bash
    cd && wget  https://repo.anaconda.com/archive/Anaconda3-2024.06-1-Linux-x86_64.sh
    ```

- Run the installation script:

  - Execute the downloaded installation script:

  - ```Bash
    bash Anaconda3-2024.06-1-Linux-x86_64.sh
    ```

  - Press `Enter` to view the license agreement.

  - Enter `yes` to accept the license agreement.

  - Select the default installation path (typically `~/anaconda3`), or customize the path as needed.

  - When the script asks if you want to initialize Conda, select `yes` to automatically configure the environment.

- Initialize Conda (if not automatically configured during installation):

  - ```Bash
    ~/anaconda3/bin/conda init
    ```

 Make the `.bashrc` configuration take effect immediately:

```Bash
source ~/.bashrc
```

- Configure the Conda Environment for RL Training

  - Create a Conda environment dedicated to RL training (for example, `pointfoot_legged_gym`) and specify the Python version:

  - ```Bash
    conda create --name pointfoot_legged_gym python=3.8
    ```

- Activate the environment you have just created:

  - ```Bash
    conda activate pointfoot_legged_gym
    ```

- Follow these steps to install the PyTorch library:

  1.Visit the [PyTorch installation page](https://pytorch.org/get-started/locally/).

  2.Select the following options:

    - Operating System: Linux
    - Package Manager: Conda
    - Language: Python
    - CUDA Version: 12.1

    ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459050428_image.png)

  3.Generate the installation command and run the following command to install:

  - ```Bash
    conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia
    ```

### 6.3.3 Install Isaac Gym

#### 6.3.3.1 Download Isaac Gym

   - **Download Page**: Go to the [Isaac Gym download page](https://developer.nvidia.com/isaac-gym/download).
   - **Accept Terms and Conditions**: You may need to accept some terms and conditions.
   - **Download**: Select the appropriate version to download the installation package for Isaac Gym.
   - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459079186_image.png)

#### 6.3.3.2 Install Isaac Gym

   - Install setuptools:

     - Setuptools is a library for managing and distributing Python packages. It simplifies the process of packaging Python projects, making them easier to install and distribute. Additionally, Setuptools handles the projects' dependencies. By specifying the version `59.5.0`, you can ensure compatibility and stability with other dependencies.

     - ```Bash
       pip install setuptools==59.5.0
       ```

   - Install TensorBoard

     TensorBoard is a tool for visualizing TensorFlow charts, metrics and other relevant data. To ensure compatibility and stability, we specify the installation version as `2.12.0`.

     ```Bash
     pip install tensorboard==2.12.0
     ```

   - Activate the conda environment for pointfoot_legged_gym

     - ```Bash
       conda activate pointfoot_legged_gym
       ```

   - Create a directory path to store the training code (e.g.: `limx_rl`)

     - ```Bash
       mkdir -p ~/limx_rl
       ```

   - Extract File: Extract the installation zip file downloaded in the previous step into the `limx_rl` directory. Note: Replace`/path/to/IsaacGym_Preview_4_Package.tar.gz` with the actual path to the package you have installed.

     - ```Bash
       tar -xzvf /path/to/IsaacGym_Preview_4_Package.tar.gz -C ~/limx_rl/
       ```

   - Install Isaac Gym and configure:

     - ```Bash
       cd ~/limx_rl/isaacgym/python
       pip install -e .
       ```

   - Troubleshoot NumPy compatibility issues:

     - In newer versions of NumPy, the use of `np.float` is discouraged because it is ambiguous and can lead to warnings or errors. To avoid these issues, it is recommended to use Python's built-in `float` type directly.

     - ```Bash
       cd ~/limx_rl/isaacgym/python
       sed -i 's/np.float/float/' isaacgym/torch_utils.py
       ```

   - Verify Installation

     - Run the examples for Isaac Gym, which can help confirm that Isaac Gym is installed and configured correctly.

     - ```Bash
       cd ~/limx_rl/isaacgym/python/examples
       python 1080_balls_of_solitude.py
       ```

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459107537_image.png)

### 6.3.4 Install pointfoot-legged-gym

   - Activate the conda environment for pointfoot_legged_gym

     - ```Bash
       conda activate pointfoot_legged_gym
       ```

   - Install pointfoot-legged-gym

     - ```Bash
       cd ~/limx_rl
       git clone https://github.com/limxdynamics/pointfoot-legged-gym.git
       cd ~/limx_rl/pointfoot-legged-gym && pip install -e .
       ```

   - Verify Installation

When you have completed all the previous steps, include the following under the `~/limx_rl` directory:

   ```Bash
cd ~/limx_rl
.
├── isaacgym
└── pointfoot-legged-gym
   ```

### 6.4 Deploy RL Docker Container Environment

### 6.4.1 Install Docker Service

Update package index and install dependencies:

```Bash
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
```

Install Docker Engine:

```Bash
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

### 6.4.2 Install NVIDIA Docker Tools

```Bash
#### Set up NVIDIA Docker repository ###
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list

#### Update package index and install NVIDIA Docker tools
sudo apt-get update
sudo apt-get install -y nvidia-docker2

#### Restart Docker service
sudo systemctl restart docker
```

### 6.4.3 Verify Docker Environment Installation

```Bash
#### Run a test container with NVIDIA support to check if nvidia-smi command works in the container
sudo docker run -it --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu20.04 nvidia-smi
```

If you see output similar to the following image, it indicates that the Docker environment is installed correctly:

- ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459150540_image.png)

### 6.4.4 Pull RL Docker Environment Base Image

The following RL environment base image comes pre-installed with conda, IsaacGym, pointfoot-legged-gym, and other environments. After pulling the image, you can use it directly without needing to configure the related environments:

```Bash
sudo docker pull crpi-yznuflhnzo12tr44.cn-shanghai.personal.cr.aliyuncs.com/limx-rl/base:v10
```

---

## 7. Reinforcement Learning Model Training

## 7.1 Start Training

   - Open a Terminal (`Ctrl + Alt + T`)

   - Set Robot Model

     [Training TRON1 Point-foot robot]:
     No need to set robot model, but you need to switch the training code branch

     ```Bash
     cd ~/limx_rl/pointfoot-legged-gym
     git checkout encoder-actor-critic
     ```

     [2. Training other robot models]

       Refer to the "View/Set Robot Model" section to check your robot model. If it has not been set yet, follow these steps to set it.

     - List the available robot models via the shell command `tree -L 1 resources/robots/pointfoot`:

       ```Plain
       limx@limx:~$ cd ~/limx_rl/pointfoot-legged-gym
       limx@limx:~$ tree -L 1 resources/robots/pointfoot
       resources/robots/pointfoot
       ├── PF_P441A
       ├── PF_P441B
       ├── PF_P441C
       └── PF_P441C2
       └── PF_TRON1A
       └── SF_TRON1A
       └── WF_TRON1A
       ```

     - Set the robot model type using `PF_TRON1A` (please replace with your actual robot type) as an example.

       ```Plain
       echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
       ```

   - Activate the conda environment for pointfoot_legged_gym

     ```Bash
     conda activate pointfoot_legged_gym
     ```

   - Enter the path where the training code is located and start training.

   ​      Parameter options:

   - --task=pointfoot_flat: It specifies that the task or environment type is pointfoot_flat.
   - --headless: This means running the training in the headless mode, in which no graphical interface will be displayed. This mode is typically used to run on a server without a display, or in situations where efficient computation is required while graphical rendering is not needed.
   - --num_envs 1024: It specifies that the number of environments to create is 1024.

   **Command 1**: Run in the headless mode

   ```Bash
cd ~/limx_rl/pointfoot-legged-gym
python legged_gym/scripts/train.py --task=pointfoot_flat --headless
   ```

   **Command 2**: Run in the graphical mode

   ```Bash
cd ~/limx_rl/pointfoot-legged-gym
python legged_gym/scripts/train.py --task=pointfoot_flat
   ```

   ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459256706_image.png)

## 7.2 Training Process

If your training stops halfway, you can specify the location of a checkpoint file and later resume training.

- First, you need to activate the pointfoot_legged_gym conda environment

  ```Bash
  conda activate pointfoot_legged_gym
  ```

- **Command 1**: Resume training in the headless mode

  - ```Bash
    cd ~/limx_rl/pointfoot-legged-gym
    python legged_gym/scripts/train.py --task=pointfoot_flat --resume --headless --load_run Apr16_16-51-06_ --checkpoint 200
    ```

- **Command 2**: Resume training in the graphical mode

  - ```Bash
    cd ~/limx_rl/pointfoot-legged-gym
    python legged_gym/scripts/train.py --task=pointfoot_flat --resume --load_run Apr16_16-51-06_ --checkpoint 200
    ```

- **Parameter options:**

  - --load_run:

    Description: It specifies the identifier of the training run to load (e.g., the name or ID of the training run). This identifier is typically associated with the training process and is used to locate the corresponding run record or configuration from the logs directory.

    How to get it:

    - View `logs` directory: Go into the `logs` directory and view the subdirectories or files, which are typically named after the identifier of the training run.

    - Example paths:

      - ```Bash
        ls -l ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat
        ```

      - You will see directories named after robot types, and training results are stored in their corresponding directories. Taking PF_TRON1A as an example, you will see a directory like `Apr16_16-51-06_`, where `Apr16_16-51-06_` is the value of the `--load_run` parameter.

  - --checkpoint:

    Description: It specifies the checkpoint file to load. The checkpoint file stores an intermediate state of the model and can be used to resume training or make inferences.

    How to get it:

    - View `logs` directory: Usually there are files that save checkpoints in the corresponding `--load_run` directory under the `logs` directory. These files typically have a `.pt` or similar extension, and the filenames may include the training epoch or a timestamp.

    - Example paths:

      ```Bash
      ls -l ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat/PF_TRON1A/Apr16_16-51-06_
      ```

      You will see a file like `model_200.pt`, where `200` is the value of the `--checkpoint` parameter.

## 7.3 Training Results

   - Open a Terminal (`Ctrl + Alt + T`)

   - Activate the conda environment for pointfoot_legged-gym

     ```Bash
     conda activate pointfoot_legged_gym
     ```

   - Start tensorboard

     ```Bash
     cd ~/limx_rl/pointfoot-legged-gym
     tensorboard --logdir=logs/pointfoot_flat
     ```

   - View training status

     Type `http://127.0.0.1:6006` in your browser's address bar to view training status.

## 7.4 Export Training Results

   - Open a Terminal (`Ctrl + Alt + T`)

   - Install ONNX

     If you haven't installed the ONNX library yet, please install it. ONNX allows easy conversion of models between different deep learning frameworks (e.g., PyTorch, TensorFlow, Caffe2, MXNet, etc.).

      ```Bash
     pip install onnx
      ```

   - Activate the conda environment for pointfoot_legged-gym

     ```Bash
     conda activate pointfoot_legged_gym
     ```

   - View training results after completing training

     The latest runs and checkpoints are read by default. To select a specific run and checkpoint, enter the `--load_run` and `--checkpoint` parameters.

     - **Parameter option description:**

       - --load_run:

         Description: It specifies the identifier of the training run to load (e.g., the name or ID of the training run). This identifier is typically associated with the training process and is used to find the corresponding run record or configuration from the `logs` directory.

         How to get it:

         - View the `logs` directory: Go into the `logs` directory and view the subdirectories or files, which are typically named after the identifier of the training run.

         - Example path:

           - ```Bash
             ls -l ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat
             ```

           - You will see directories named after robot types, and training results are stored in their corresponding directories. Taking PF_TRON1A as an example, you will see a directory like `Apr16_16-51-06_`, where `Apr16_16-51-06_` is the value of the `--load_run` parameter.

       - --checkpoint:

         Description: Specifies the checkpoint file to load. Checkpoint files hold an intermediate state of the model and can be used to resume training or for inference.

         How to get it:

         - View the `logs` directory: Files that save checkpoints are typically found in the corresponding `--load_run` directory under the `logs` directory. These files typically have a `.pt` or similar extension, and the filenames may include the training epoch or a timestamp.

         - Example path:

           - ```Bash
             ls -l ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat/PF_TRON1A/Apr16_16-51-06_
             ```

           - You will see a file like `model_200.pt`, where `200` is the value of the `--checkpoint` parameter.

     - Usage example:

       - ```Bash
         cd ~/limx_rl/pointfoot-legged-gym
         python legged_gym/scripts/export_policy_as_onnx.py --task=pointfoot_flat --load_run Apr16_16-51-06_ --checkpoint 200
         ```

       Taking PF_TRON1A as an example, the converted ONNX files are saved in the directory:

       ```Bash
       ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat/PF_TRON1A/exported/policies
       ```

       To view the exported ONNX files:

       ```Bash
       ls -l ~/limx_rl/pointfoot-legged-gym/logs/pointfoot_flat/PF_TRON1A/exported/policies
       ```

## 7.5 Start Training in Docker Environment

  - Open a Terminal (`Ctrl + Alt + T`)

  - Pull the RL Docker container base image

    ```Bash
    sudo docker pull crpi-yznuflhnzo12tr44.cn-shanghai.personal.cr.aliyuncs.com/limx-rl/base:v10
    ```

  - Run the RL container environment

    ```Bash
    sudo docker run -it --gpus all --rm crpi-yznuflhnzo12tr44.cn-shanghai.personal.cr.aliyuncs.com/limx-rl/base:v10
    ```

  - Set Robot Model: Refer to the "View/Set Robot Model" section to check your robot model. If it has not been set yet, follow these steps to set it.

    ```Bash
    $ cd limx_rl/pointfoot-legged-gym
    $ tree -L 1 resources/robots/pointfoot/
    resources/robots/pointfoot/
    ├── PF_P441A
    ├── PF_P441B
    ├── PF_P441C
    ├── PF_P441C2
    ├── PF_TRON1A
    ├── SF_TRON1A
    └── WF_TRON1A
    ```

    - Using `PF_TRON1A` (please replace with your actual robot type) as an example, set the robot model type:

      ```Bash
      export ROBOT_TYPE=PF_TRON1A
      ```

      Note: Setting environment variables using the export command in the container only takes effect in the current container environment and will not persist after restarting the container. You can use the `-e` parameter to include environment variable information, making the environment variables automatically take effect each time you run the RL container environment.

      ```Bash
      sudo docker run -it --gpus all --rm -e ROBOT_TYPE=PF_TRON1A crpi-yznuflhnzo12tr44.cn-shanghai.personal.cr.aliyuncs.com/limx-rl/base:v10
      ```

  - Enter the path where the training code is located and start training

    Parameter options:

    - --task=pointfoot_flat: Specifies that the task or environment type is pointfoot_flat.
    - --headless: Runs in headless mode, meaning no graphical interface will be displayed. This mode is typically used on servers without displays or when efficient computation is required without graphical rendering.
    - --num_envs 1024: Specifies that the number of environments to create is 1024.

    Command 1: Run in headless mode

    ```Bash
    cd limx_rl/pointfoot-legged-gym
    python legged_gym/scripts/train.py --task=pointfoot_flat --headless
    ```

---

## 8. Reinforcement Learning Training Results Deployment

## 8.1 Python based Deployment

The Python motion control algorithm development interface facilitates the development of algorithms for developers who are not familiar with C++ and ROS. The Python language is easy to learn, features a concise syntax, and boasts a rich collection of third-party libraries that allow developers to quickly get started and efficiently implement algorithms.

Through the Python interface, developers can leverage its dynamic characteristics for rapid prototyping and experimental verification, thereby accelerating the iteration and optimization of algorithms. Additionally, Python's cross-platform nature and robust ecosystem enable motion algorithms to be widely deployed across different platforms and environments.

In addition, the flexibility of Python greatly simplifies the rapid deployment of RL models. Developers can easily integrate RL models into simulated and real hardware to quickly verify and optimize algorithm performance.

### 8.1.1 Deployment Environment Configuration

  - Configure the Conda environment for Python deployment
    Create a dedicated Conda environment for Python deployment runtime (e.g., pointfoot_deploy) and specify the Python version:

    ```Bash
    conda create --name pointfoot_deploy python=3.8
    ```

  - Activate the newly created environment:

    ```Bash
    conda activate pointfoot_deploy
    ```

### 8.1.2 Creating a Workspace

You can follow these steps to create an RL deployment development workspace:

   - Open a Bash terminal.

   - Create a new directory to hold the workspace. For example, you can create a directory called "limx_ws" under the user's home directory:

     - ```Bash
       mkdir -p ~/limx_ws
       ```

   - Download the MuJoCo simulator

     - ```Bash
       cd ~/limx_ws
       git clone --recurse https://github.com/limxdynamics/pointfoot-mujoco-sim.git
       ```

   - Download the deployment implementation

     - ```Bash
       cd ~/limx_ws
       git clone --recurse https://github.com/limxdynamics/rl-deploy-with-python.git
       ```

   - Install the motion control development library (if not already installed):

     - Linux x86_64 environment

       ```Bash
       # Activate the pointfoot_deploy conda environment
       conda activate pointfoot_deploy

       # Install limxsdk
       pip install rl-deploy-with-python/pointfoot-sdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
       ```

     - Linux aarch64 environment

       ```Bash
       # Activate the pointfoot_deploy conda environment
       conda activate pointfoot_deploy

       # Install limxsdk
       pip install rl-deploy-with-python/pointfoot-sdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
       ```

   - Set robot model: Refer to the "View/Set robot model" section to view your robot model. If it has not been set yet, follow these steps to do so.

     - List the available robot types via the Shell command `tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot`:

     - ```Plain
       limx@limx:~$ tree -L 1 pointfoot-mujoco-sim/robot-description/pointfoot
       pointfoot-mujoco-sim/robot-description/pointfoot
       ├── PF_P441A
       ├── PF_P441B
       ├── PF_P441C
       ├── PF_P441C2
       ├── PF_TRON1A
       ├── SF_TRON1A
       └── WF_TRON1A
       ```

     - Set the robot model type using `PF_TRON1A` as an example. Please replace `PF_TRON1A` with your actual robot type.

     - ```Plain
       echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
       ```

### 8.1.3 Update the RL Training Model

In your workspace, using the `PF_TRON1A` robot type as an example, the RL model and configuration file are located in the path: `~/limx_ws/rl-deploy-with-python/controllers/model/PF_TRON1A`, as shown below. Replace the robot type according to your training results.

```Bash
tree ~/limx_ws/rl-deploy-with-python/controllers/model/PF_TRON1A
.
├── params.yaml
└── policy
    ├── policy.onnx
    └── encoder.onnx
```

### 8.1.4 Simulation Debugging

   - Open a Bash terminal and run the MuJoCo simulator.

     - ```Bash
       # Activate the pointfoot_deploy conda environment
       conda activate pointfoot_deploy

       # Run the simulator
       cd ~/limx_ws
       python pointfoot-mujoco-sim/simulator.py
       ```

   - Open a Bash terminal and run the motion control algorithm.

     - If your robot is in a falling state, once you have initiated motion control, click the "Reset" button located on the left menu bar of the MuJoCo simulator to restore the robot to its original position. Subsequently, you will observe the robot recovering and resuming its walking motion.

     - ```Bash
       # Activate the pointfoot_deploy conda environment
       conda activate pointfoot_deploy

       # Run the control algorithm
       python rl-deploy-with-python/main.py
       ```

   - Open a Bash terminal and run the virtual remote controller. During the simulation, you can use this controller to manipulate the robot's motion. The left joystick controls the robot's forward and backward movement, as well as its left or right turning. The right joystick, on the other hand, controls the robot's lateral motion, allowing it to move left and right.

     - ```Bash
       ./pointfoot-mujoco-sim/robot-joystick/robot-joystick
       ```

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459508473_image.png)

### 8.1.5 Robot Debugging

   - Modify the developer computer IP: Make sure that your development computer and the robot are connected through an external Ethernet port. Set the IP address of your computer to `10.192.1.200` and verify that the robot can successfully respond to the Shell command `ping 10.192.1.2` to ensure network communication with your computer. Set the IP for your development computer as shown in the following image:

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768459530256_image.png)

   - Turn on the developer mode: Press the `R1 + Left` buttons on the remote controller simultaneously after the robot boots up. This will automatically restart the robot's host computer and switch it to the developer mode. In this mode, users can develop their own motion control algorithms. The setting will persist even after a power failure or reboot, meaning the robot will remain in developer mode until it is manually switched to another mode. Below is a list of remote controller buttons used to switch between working modes.

     - | **Buttons** | **Mode**                             | **Instruction**                                              |
       | ----------- | ------------------------------------ | ------------------------------------------------------------ |
       | R1+Left     | Developer Mode (under authorization) | Users can develop their own motion control algorithms using the motion control development interfaces. |
       | R1+Right    | Remote Control Mode                  | The robot runs the pre-installed motion control algorithm to walk smoothly in complex terrains including going up and down steps and crossing obstacles. |

   - Perform zero calibration: After the robot is turned on, before executing the motion control program, perform zero calibration to make each joint of the robot return to the initial position. The remote controller buttons for zero calibration are L1 and R1.

   - Robot deployment and operation. In the Bash terminal, just run the following Shell command to start the control algorithm (ensuring proper lifting and installation of the robot is crucial during deployment and operation).

     - ```Bash
       # Activate the pointfoot_deploy conda environment
       conda activate pointfoot_deploy

       # Specify the robot IP address and run the control algorithm
       python rl-deploy-with-python/main.py 10.192.1.2
       ```

         - At this time, you can press the remote control buttons `L1 + △ (Y)` to turn on the walking function of the robot. The left joystick controls the robot's forward and backward movement, as well as its left or right turning. The right joystick, on the other hand, controls the robot's lateral motion, allowing it to move left and right.

         - Press buttons `L1 + □ (X)` to turn off the walking function of the robot.

### 8.1.6 Robot Deployment

After completing simulation and robot debugging, you can deploy your algorithm program to the robot. Always ensure the robot is properly lifted for safety before starting deployment testing, and maintain this safety precaution throughout the entire deployment process. Here are the detailed steps:

- Preparation work

  - Keep the robot in the developer mode: Make sure that the robot is still in the developer mode, so that you can easily deploy and debug the program.
  - Network connection: Ensure that the development computer and the robot are connected via an external network (Ethernet), and that the network is stable and communication is normal. Once deployment is complete, the network connection will no longer be required.

- Copy the algorithm program to the robot

  - On the development computer, open the terminal and enter the working directory where the algorithm program is stored, for example, `~/limx_ws`.

  - Use the scp command to copy the directory containing the algorithm to the robot. The default robot user is "`guest`" and the password is "`123456`".

  - ```Bash
    cd ~/limx_ws
    scp -r rl-deploy-with-python guest@10.192.1.2:/home/guest
    ```

- Install the motion control development library (if not already installed):

  - ```Bash
    ssh guest@10.192.1.2 "pip install /home/guest/rl-deploy-with-python/pointfoot-sdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl"
    ```

- Configure the algorithm to start automatically

  - SSH into the robot: Use the ssh command to remotely log into the robot system with the password "`123456`".

    - ```Bash
      ssh guest@10.192.1.2
      ```

  - Modify the auto-launch script:

    - Open the auto-launch script `/home/guest/autolaunch/autolaunch.sh` for editing:

      - ```Bash
        busybox vi /home/guest/autolaunch/autolaunch.sh
        ```

    - Inside the script, find the command to start `main.py`. Make sure the line `python3 /home/guest/rl-deploy-with-python/main.py 10.192.1.2` is uncommented (no `#` comment symbol). When finished, save and exit the editor.

      - ```Bash
        #!/bin/bash

        while true; do
          # To start the Python controller script for controlling the point foot robot
          # The parameter 10.192.1.2 represents the IP address where the robot is located
          # This line is currently commented out. To enable automatic controller startup, you need to uncomment it.
          # Modify this path based on the actual path of your controller script
          # Note: If limxsdk version doesn't match the robot body version, please uninstall limxsdk with pip uninstall and then reinstall with pip install!
          # python3 /home/guest/rl-deploy-with-python/main.py 10.192.1.2

          # Start the robot control algorithm with roslaunch
          # To start the robot control algorithm, uncomment the following line
          # Modify this path based on the actual path of your installation
          # source /home/guest/install/setup.bash
          # roslaunch robot_hw pointfoot_hw.launch

          # Wait 3 seconds before restarting
          sleep 3
        done
        ```

    - Restart the robot: After completing the modifications, power off and restart the robot.

- Controlling the robot's motion

  - Control Robot Movement
    :::warning
    During the first time of deployment, the robot may exhibit unexpected movement behavior. Please ensure both the TRON1 and the operator are in a safe state.
    :::
    - Once the system has booted, you will be able to control the robot using the remote controller:
    - L1 + △ : Activate the robot's walking function;
    - L1 + □ : Deactivate the robot's walking function;
    - Left joystick: Controls the robot's forward, backward, left, and right movements;
    - Right joystick: Controls the robot's lateral (side-to-side) movements.

## 8.2 ROS C++ Based Deployment

### 8.2.1 Deployment Environment Configuration

   - Install ROS Noetic: We recommend setting up an algorithm development environment based on ROS Noetic on the Ubuntu 20.04 operating system. ROS provides a series of tools and libraries, such as core libraries, communication libraries, and simulation tools (such as Gazebo), which greatly facilitate the development, testing, and deployment of robot algorithms. These resources provide users with a rich and complete algorithm development environment.

     - For ROS Noetic installation, please refer to the documentation: https://wiki.ros.org/noetic/Installation/Ubuntu and select "ros-noetic-desktop-full" for installation.

     - After installing ROS Noetic, enter the following Shell command in the Bash terminal to install the libraries required for the development environment:

       - ```Bash
         sudo apt-get update
         sudo apt install ros-noetic-urdf \
                          ros-noetic-kdl-parser \
                          ros-noetic-urdf-parser-plugin \
                          ros-noetic-hardware-interface \
                          ros-noetic-controller-manager \
                          ros-noetic-controller-interface \
                          ros-noetic-robot-state-* \
                          ros-noetic-joint-state-* \
                          ros-noetic-controller-manager-msgs \
                          ros-noetic-control-msgs \
                          ros-noetic-ros-control \
                          ros-noetic-gazebo-* \
                          ros-noetic-rqt-gui \
                          ros-noetic-rqt-controller-manager \
                          ros-noetic-plotjuggler* \
                          ros-noetic-joy-teleop ros-noetic-joy \
                          cmake build-essential libpcl-dev libeigen3-dev libopencv-dev libmatio-dev \
                          python3-pip libboost-all-dev libtbb-dev liburdfdom-dev liborocos-kdl-dev -y
         ```

   - Install onnxruntime dependency, download link: https://github.com/microsoft/onnxruntime/releases/tag/v1.10.0. Please choose the appropriate version according to your operating system and platform. For example, on Ubuntu 20.04 x86_64, follow these steps to install:

     - ```Bash
       wget https://github.com/microsoft/onnxruntime/releases/download/v1.10.0/onnxruntime-linux-x64-1.10.0.tgz

       tar xvf onnxruntime-linux-x64-1.10.0.tgz

       sudo cp -a onnxruntime-linux-x64-1.10.0/include/* /usr/include
       sudo cp -a onnxruntime-linux-x64-1.10.0/lib/* /usr/lib
       ```

### 8.2.2 Creating a Workspace

    You can follow these steps to create an RL deployment development workspace:

   - Open a Bash terminal.

   - Create a new directory to hold the workspace. For example, you can create a directory called "limx_ws" under the user's home directory:

     - ```Bash
       mkdir -p ~/limx_ws/src
       ```

   - Download the motion control development interface:

     - ```Bash
       cd ~/limx_ws/src
       git clone https://github.com/limxdynamics/pointfoot-sdk-lowlevel.git
       ```

   - Download the Gazebo simulator:

     - ```Bash
       cd ~/limx_ws/src
       git clone https://github.com/limxdynamics/pointfoot-gazebo-ros.git
       ```

   - Download the robot model description file:

     - ```Bash
       cd ~/limx_ws/src
       git clone https://github.com/limxdynamics/robot-description.git
       ```

   - Download visualization tools:

     - ```Bash
       cd ~/limx_ws/src
       git clone https://github.com/limxdynamics/robot-visualization.git
       ```

   - Download the RL deployment source code:

     - ```Bash
       cd ~/limx_ws/src
       git clone https://github.com/limxdynamics/rl-deploy-ros-cpp.git
       ```

   - Set the robot model: Refer to the "View/Set Robot Model" section to view your robot model. If it has not been set yet, follow these steps to set it.

     - List the available robot types via the Shell command `tree -L 1 src/robot-description/pointfoot`:

     - ```Plain
       limx@limx:~$ tree -L 1 src/robot-description/pointfoot
       src/robot-description/pointfoot
       ├── PF_P441A
       ├── PF_P441B
       ├── PF_P441C
       └── PF_P441C2
       └── PF_TRON1A
       └── SF_TRON1A
       └── WF_TRON1A
       ```

     - Set the robot model type using `PF_TRON1A` as an example. Please replace `PF_TRON1A` with your actual robot type:

     - ```Plain
       echo 'export ROBOT_TYPE=PF_TRON1A' >> ~/.bashrc && source ~/.bashrc
       ```

   - Compile the project:

     - ```Bash
       # If you have Conda installed, temporarily disable the Conda environment
       # Because Conda may interfere with the ROS runtime environment settings
       conda deactivate

       cd ~/limx_ws
       catkin_make install
       ```

### 8.2.3 Update the RL Training Model

In your workspace, using the `PF_TRON1A` robot type as an example, the RL model and configuration file are located at: ~/limx_ws/src/rl-deploy-ros-cpp/robot_controllers/config/pointfoot/PF_TRON1A, as shown below. Please update and replace according to your training results.

```Bash
tree ~/limx_ws/src/rl-deploy-ros-cpp/robot_controllers/config/pointfoot/PF_TRON1A
.
├── params.yaml
└── policy
    └── policy.onnx
```

### 8.2.4 Simulation Debugging

   - Go to your workspace and complete the compilation:

     - ```Bash
       # If you have Conda installed, temporarily disable the Conda environment
       # Because Conda may interfere with the ROS runtime environment settings
       conda deactivate

       cd ~/limx_ws
       catkin_make install
       ```

   - Run the deployment simulation: After starting, you will see an interface as shown below, including the Gazebo simulator and the Robot Steering interaction window.

     - In the Gazebo window, you can use the shortcut `Ctrl + Shift + R` to reset the robot;

     - You can also set and publish the topic /cmd_vel through the Robot Steering interaction window to control the robot's movement.

     - ```Bash
       # If you have Conda installed, temporarily disable the Conda environment
       # Because Conda may interfere with the ROS runtime environment settings
       conda deactivate

       # Run robot_hw
       source install/setup.bash
       roslaunch robot_hw pointfoot_hw_sim.launch
       ```

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460485255_image.png)

   - Virtual Remote Controller: If you find it inconvenient to control the robot using `Robot Steering`, you can use a virtual remote controller to simplify the operation. Here are the specific steps for using the virtual remote controller.

     - Download and run the virtual remote controller:

       - ```Bash
         # Download the virtual remote controller
         git clone https://github.com/limxdynamics/robot-joystick.git

         # Run the virtual remote controller
         ./robot-joystick/robot-joystick
         ```

       - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460500269_image.png)

     - Now you can use the virtual remote controller to control the robot's movements. The left joystick controls forward/backward/left turn/right turn movements; the right joystick can control the robot's left and right lateral movements.

### 8.2.5 Robot Debugging

   - Modify the developer computer IP: Make sure that your development computer and the robot are connected through an external Ethernet port. Set the IP address of your computer to `10.192.1.200` and verify that the robot can successfully respond to the Shell command `ping 10.192.1.2` to ensure network communication with your computer. Set the IP for your development computer as shown in the following image:

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460516836_image.png)

   - Go to your workspace, find the `pointfoot_hw.launch` launch file, and modify the robot's IP address to `10.192.1.2`, as shown in the following image:

     - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460528417_image.png)

     - After completing the modifications, compile:

     - ```Bash
       # If you have Conda installed, temporarily disable the Conda environment
       # Because Conda may interfere with the ROS runtime environment settings
       conda deactivate

       cd ~/limx_ws
       catkin_make install
       ```

   - Turn on the developer mode: Press the `R1 and Left` buttons on the remote controller simultaneously after the robot boots up. This will automatically restart the robot's host computer and switch it to the developer mode. In this mode, users can develop their own motion control algorithms. The setting will persist even after a power failure or reboot, meaning the robot will remain in developer mode until it is manually switched to another mode. Below is a list of remote controller buttons used to switch between working modes:

     - | **Buttons** | **Mode**                             | **Instruction**                                              |
       | ----------- | ------------------------------------ | ------------------------------------------------------------ |
       | R1+Left     | Developer Mode (under authorization) | Users can develop their own motion control algorithms using the motion control development interfaces. |
       | R1+Right    | Remote Control Mode                  | The robot runs the pre-installed motion control algorithm to walk smoothly in complex terrains including going up and down steps and crossing obstacles. |

   - Perform zero calibration: After the robot is turned on, before executing the motion control program, perform zero calibration to make each joint of the robot return to the initial position. The remote controller buttons for zero calibration are L1+R1.

   - Robot deployment and operation. In the Bash terminal, just run the following Shell command to start the control algorithm (ensuring proper lifting and installation of the robot is crucial during deployment and operation):

     - ```Bash
       # If you have Conda installed, temporarily disable the Conda environment
       # Because Conda may interfere with the ROS runtime environment settings
       conda deactivate

       # Run robot_hw
       source install/setup.bash
       roslaunch robot_hw pointfoot_hw.launch
       ```

   - At this time, you can press the remote control buttons `L1 + △` to turn on the walking function of the robot. The left joystick controls the robot's forward and backward movement, as well as its left or right turning. The right joystick, on the other hand, controls the robot's lateral motion, allowing it to move left and right.

   - Press buttons `L1 + □ ` to turn off the walking function of the robot.

### 8.2.6 Robot Deployment

After completing simulation and robot debugging, you can deploy your algorithm program to the robot. Always ensure the robot is properly lifted for safety before starting deployment testing, and maintain this safety precaution throughout the entire deployment process. Here are the detailed steps:

- Preparation work

  - Keep the robot in the developer mode: Make sure that the robot is still in the developer mode, so that you can easily deploy and debug the program.
  - Network connection: Ensure that the development computer and the robot are connected via an external network (Ethernet), and that the network is stable and communication is normal. Once deployment is complete, the network connection will no longer be required.

- Copy the algorithm program to the robot

  - On the development computer, open the terminal and enter the working directory where the algorithm program is stored, for example, `~/limx_ws`.

  - Use the scp command to copy the directory containing the algorithm to the robot. The default robot user is "`guest`" and the password is "`123456`".

  - ```Bash
    cd ~/limx_ws
    scp -r install guest@10.192.1.2:/home/guest
    ```

- Configure the algorithm to start automatically

  - SSH into the robot: Use the ssh command to remotely log into the robot system with the password "`123456`".

    - ```Bash
      ssh guest@10.192.1.2
      ```

  - Modify the auto-launch script:

    - Open the auto-launch script `/home/guest/autolaunch/autolaunch.sh` for editing:

      - ```Bash
        busybox vi /home/guest/autolaunch/autolaunch.sh
        ```

    - Inside the script, find the command to start `roslaunch`. Make sure the lines `source /home/guest/install/setup.bash` and `roslaunch robot_hw pointfoot_hw.launch` are uncommented (no `#` comment symbol). When finished, save and exit the editor.

      - ```Bash
        #!/bin/bash

        while true; do
          # To start the Python controller script for controlling the point foot robot
          # The parameter 10.192.1.2 represents the IP address where the robot is located
          # This line is currently commented out. To enable automatic controller startup, you need to uncomment it.
          # Modify this path based on the actual path of your controller script
          # Note: If limxsdk version doesn't match the robot body version, please uninstall limxsdk with pip uninstall and then reinstall with pip install!
          # python3 /home/guest/rl-deploy-with-python/main.py 10.192.1.2

          # Start the robot control algorithm with roslaunch
          # To start the robot control algorithm, uncomment the following line
          # Modify this path based on the actual path of your installation
          source /opt/ros/noetic/setup.bash
          source /home/guest/install/setup.bash
          roslaunch robot_hw pointfoot_hw.launch

          # Wait 3 seconds before restarting
          sleep 3
        done
        ```

    - Restart the robot: After completing the modifications, power off and restart the robot.

- Controlling the robot's motion

  - Control Robot Movement
    :::warning
    During the first robot deployment, the machine may exhibit unexpected movement behavior. Please ensure both the TRON1 and the operator are in a safe state.
    :::
    - Once the system has booted, you will be able to control the robot using the remote controller:
    - L1 + △ (Y): Activate the robot's walking function;
    - L1 + □ (X): Deactivate the robot's walking function;
    - Left joystick: Controls the robot's forward, backward, left, and right movements;
    - Right joystick: Controls the robot's lateral (side-to-side) movements.

---

## 9. RealSense Camera

## 9.1 Overview

The robot is equipped with the RealSense D435i camera, a powerful depth camera that provides both depth and image data.

<div style="display: flex; gap: 10px;">
  <img src="https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460576644_image.png" width="200" alt="图片1描述">
  <img src="https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460591337_image.png" width="200" alt="图片2描述">
</div>

## 9.2 Installation

In this chapter, you will learn how to acquire data from the RealSense D435i camera using the Noetic version of ROS. The specific steps are as follows:

  - Ensure that your computer is equipped with the Noetic version of ROS (Please refer to https://wiki.ros.org/noetic/Installation/Ubuntu, choose "ros-noetic-desktop-full" and install it.)

  - Configure Network Connection: Ensure that your development computer is connected to the robot via a network interface, and set its IP address to `10.192.1.200`. Verify that the robot can successfully respond to the Shell command `ping 10.192.1.2` to ensure network communication with your computer. Set the IP address for your development computer as shown in the image below.

  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460608991_image.png)

- Open the camera on the "LimX Robot Manager" page.

  If the camera is not activated, you can take the following steps:

  - Enter `http://10.192.1.2:8080` in the browser to access the "LimX Robot Manager" page.
  - Navigate to the "Robot Information" page.
  - Click "Open the camera" to activate Camera D435i.
  - Configure the ROS environment to obtain camera data.
  - ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460621661_image.png)

- Configure the ROS environment to acquire camera data.

To enable your computer to receive camera data from the robot through ROS, you need to set up the ROS environment variables correctly:

- To ensure that your computer can correctly connect to the robot's ROS master node, you need to set the **ROS_MASTER_URI** and **ROS_IP** environment variables. You can add the commands to set these variables to your `.bashrc` profile on your computer, so that the settings are automatically applied each time you open a terminal.

  - ```Bash
    export ROS_MASTER_URI=http://10.192.1.2:11311
    export ROS_IP=10.192.1.200
    ```

- View data topics published by the Camera D435i via ROS: Use the following command to list all topics published by the camera. You can subscribe to the topics that interest you:

  - ```Bash
    rostopic list
    ```

- Visualize camera data with rqt or rviz:

  Both rqt_image_view and rviz can be utilized to visualize the images and depth data captured by the Camera D435i.

  - Install rqt_image_view:

    ```Bash
    sudo apt install ros-<ros版本>-rqt-image-view
    ```

    Then, activate this tool and choose the topic to view data.

      ```Bash
    rqt_image_view
      ```

  - Use rviz to view depth data:

    ```Bash
    rviz
    ```

    Add the corresponding Image and PointCloud2 displays in rviz, and select the relevant camera topics to visualize the data.

---

## 10. Robot Software Upgrade

 Please go to the LimX Robot Manager page through the browser and select the version of the robot software that you have downloaded in advance to upgrade. The steps are as follows:

- Select the network and connect to your robot's Wi-Fi hotspot with the password "`12345678`".

1. Visit the LimX Robot Manager page:
   1. Enter [http://10.192.1.2:8080](http://10.192.1.2:8080/) in the browser address bar to enter the robot management page.
2. Select the software and upgrade:
   1. Select Version Management - > Select the File - > Upgrade
   2. Once the upgrade is complete, the robot's host computer will restart automatically.
   3. ![](https://limx-ows.oss-cn-beijing.aliyuncs.com/uploads/image/2026/01/15/1768460646429_image.png)
