Low-level · Python API
2.2 Python Motion Control Development Interface
2.2.1 Overview
The Python Motion Control Development Interface provides the same functionality as the C++ interface, allowing developers who are unfamiliar with C++ to develop motion-control algorithms in Python.
Python is easy to learn and offers concise syntax and a rich collection of third-party libraries, allowing developers to get started quickly and implement algorithms efficiently. Through the Python interface, developers can use Python's dynamic features for rapid prototyping and experimental validation, accelerating algorithm iteration and optimization. Python's cross-platform capabilities and extensive ecosystem also make motion-control algorithms easier to deploy across different platforms and environments.
Python's flexibility also simplifies rapid deployment to simulation and physical-robot environments. Developers can integrate algorithm models with simulation platforms and physical hardware for rapid iteration and performance validation.
2.2.2 Install the Motion Control Development Library
- Linux x86_64 environment
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/amd64/limxsdk-*-py3-none-any.whl
- Linux aarch64 environment
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/aarch64/limxsdk-*-py3-none-any.whl
- Windows environment
git clone https://github.com/limxdynamics/limxsdk-lowlevel.git
pip install limxsdk-lowlevel/python3/win/limxsdk-*-py3-none-any.whl
2.2.3 init Interface Description
| Item | Description |
|---|---|
| Function Name | __init__ |
| Function Prototype | def __init__(self, robot_type: robot.RobotType) |
| Function Overview | Specifies the robot type during initialization and creates a local robot instance of the corresponding type. |
| Parameters | robot_type: enumeration value representing the robot type, type is RobotType.Tron2. |
| Return Value | None |
| Remarks | None |
Code Example:
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
if __name__ == '__main__':
# Create a Robot instance of type Tron2
robot = Robot(RobotType.Tron2)
2.2.4 init Interface Description
| Item | Description |
|---|---|
| Function Name | init |
| Function Prototype | def init(self, robot_ip: str = "127.0.0.1") |
| Function Overview | Initializes the communication runtime environment for the motion-control algorithm program. Call this function before calling any other interface. |
| Parameters | robot_ip: the robot IP address. For simulation, it is usually set to "127.0.0.1"; for a physical robot, set it to "10.192.1.2". |
| Return Value | Returns True on successful initialization and False on failure. |
| Remarks | None |
Code Example:
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
if __name__ == '__main__':
# Create a Robot instance of type Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a command-line argument is provided as the robot IP
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot communication runtime environment using the IP address
if not robot.init(robot_ip):
sys.exit()
2.2.5 getMotorNumber Interface Description
| Item | Description |
|---|---|
| Function Name | getMotorNumber |
| Function Prototype | def getMotorNumber(self, timeout: float = -1.0) |
| Function Overview | Gets the number of motors in the robot. |
| Parameters | timeout: timeout in seconds. The default value -1.0 means to wait indefinitely. |
| Return Value | Returns an unsigned integer representing the total number of motors in the robot. |
| Remarks | The dual-arm configuration normally has 14 motors. |
Code Example:
import sys
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
if __name__ == '__main__':
# Create a Robot instance of type Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Get the number of motors in the robot
motor_number = robot.getMotorNumber()
2.2.6 subscribeImuData Interface Description
| Item | Description |
|---|---|
| Function Name | subscribeImuData |
| Function Prototype | def subscribeImuData(self, callback: Callable[[datatypes.ImuData], Any]) |
| Function Overview | Subscribes to robot IMU data and calls the specified callback function when new IMU data is received. |
| Parameters | callback: callback function used to process new IMU data. |
| Return Value | Returns True on success and continuously outputs IMU data; returns False on failure. |
Remarks - datatypes.ImuData data structure prototype is as follows:
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 robot 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 Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Create a RobotReceiver instance to handle callbacks
receiver = RobotReceiver()
# Create a partial function for the callback
imuDataCallback = partial(receiver.imuDataCallback)
# Subscribe to robot IMU data
robot.subscribeImuData(imuDataCallback)
# Sleep for 1 second to prevent the program from exiting
import time
while True:
time.sleep(1)
2.2.7 subscribeRobotState Interface Description
| Item | Description |
|---|---|
| Function Name | subscribeRobotState |
| Function Prototype | def subscribeRobotState(self, callback: Callable[[datatypes.RobotState], Any]) |
| Function Overview | Subscribes to and receives robot state updates. |
| Parameters | callback: callback function that is called when a robot state update is received. The callback parameter points to datatypes.RobotState object. |
| Return Value | Returns True on success and False on failure. |
| Remarks | None |
Code Example:
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes
class RobotReceiver:
# callback function used to receive 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 Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Create a RobotReceiver instance to handle callbacks
receiver = RobotReceiver()
# Create a partial function for the callback
robotStateCallback = partial(receiver.robotStateCallback)
# Subscribe to robot state
robot.subscribeRobotState(robotStateCallback)
# Sleep for 1 second to prevent the program from exiting
import time
while True:
time.sleep(1)
2.2.8 publishRobotCmd Interface Description
| Item | Description |
|---|---|
| Function Name | publishRobotCmd |
| Function Prototype | def publishRobotCmd(self, cmd: datatypes.RobotCmd) |
| Function Overview | Publishes a command to control robot motion. |
| Parameters | cmd: the required robot command datatypes.RobotCmd object. |
| Return Value | Returns True on success and False on failure. |
| Remarks | None |
Code Example:
import sys
import time
import limxsdk.robot.Rate as Rate
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes
if __name__ == '__main__':
# Create a Robot instance
robot = Robot(RobotType.Tron2)
# For simulation, use "127.0.0.1"; for a physical robot, use "10.192.1.2"
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Get motor-count information
motor_number = robot.getMotorNumber()
# Main loop to continuously publish robot commands
rate = Rate(300) # 300Hz
cmd_msg = datatypes.RobotCmd()
while True:
# Set default values for timestamp, control mode, joint position, velocity, torque, Kp, and Kd
# motor_names corresponds to the joint names you want to control
# Note: the following is only a format example. Fill in specific parameters during actual use.
cmd_msg.stamp = time.time_ns()
cmd_msg.mode = [0.0 for _ in range(16)] # No change is required in actual use
cmd_msg.q = [0.0 for _ in range(16)] # Fill in the planned angles; control frequency is 300 Hz
cmd_msg.dq = [0.0 for _ in range(16)] # No change is required in actual use
cmd_msg.tau = [0.0 for _ in range(16)] # No change is required in actual use
cmd_msg.Kp = [420,420,300,300,200,200,200,420,420,300,300,200,200,200]
cmd_msg.Kd = [12,12,15,15,10,10,10,12,12,15,15,10,10,10,3,3]
cmd_msg.motor_names = ["" for _ in range(16)] # No change is required in actual use
robot.publishRobotCmd(cmd_msg) # Publish robot command
rate.sleep() # Control loop frequency
2.2.9 subscribeSensorJoy Interface Description
| Item | Description |
|---|---|
| Function Name | subscribeSensorJoy |
| Function Prototype | def subscribeSensorJoy(self, callback: Callable[[datatypes.SensorJoy], Any]) |
| Function Overview | On a physical robot, this method subscribes to data from the robot's remote controller. When new data is received, the specified callback is invoked with a datatypes.SensorJoy object containing the remote-controller data. |
| Parameters | callback: callback used to receive remote-controller data. Its parameter type is datatypes.SensorJoy. |
| Return Value | Returns True on success and False on failure. |
| Remarks | None |
Code Example:
import sys
import time
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes
class RobotReceiver:
# callback function used to receive remote-controller data
def sensorJoyCallback(self, sensor_joy: datatypes.SensorJoy):
print("\n------\nsensor_joy:" + \
"\n stamp: " + str(sensor_joy.stamp) + \
"\n axes: " + str(sensor_joy.axes) + \
"\n buttons: " + str(sensor_joy.buttons))
if __name__ == '__main__':
# Create a Robot instance of type Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Create a RobotReceiver instance to handle callbacks
receiver = RobotReceiver()
# Create a partial function for the callback
sensorJoyCallback = partial(receiver.sensorJoyCallback)
# Subscribe to robot remote-control data
robot.subscribeSensorJoy(sensorJoyCallback)
# Sleep for 1 second to prevent the program from exiting
import time
while True:
time.sleep(1)
2.2.10 subscribeDiagnosticValue Interface Description
| Item | Description |
|---|---|
| Function Name | subscribeDiagnosticValue |
| Function Prototype | def subscribeDiagnosticValue(self, callback: Callable[[datatypes.DiagnosticValue], Any]) |
| Function Overview | On a physical robot, this method subscribes to diagnostic values and status information. When diagnostic data is published, the specified callback is invoked with a datatypes.DiagnosticValue object containing the diagnostic data. This allows applications to monitor robot health in real time and respond promptly to possible issues. |
| Parameters | callback: callback function used to receive robot diagnostic values, whose parameter type is datatypes.DiagnosticValue. datatypes.DiagnosticValue structure contains robot diagnostic-value information, including timestamp, level, name, code, and message fields. |
| Return Value | Returns True on success and False on failure. |
| Remarks | None |
Code Example:
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes
class RobotReceiver:
# callback function used to receive diagnostic values
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 Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Create a RobotReceiver instance to handle callbacks
receiver = RobotReceiver()
# Create a partial function for the callback
diagnosticValueCallback = partial(receiver.diagnosticValueCallback)
# Subscribe to robot diagnostic information
robot.subscribeDiagnosticValue(diagnosticValueCallback)
# Sleep for 1 second to prevent the program from exiting
import time
while True:
time.sleep(1)
Return Value: |
/home/damon/PyCharmMiscProject/.venv/bin/python /home/damon/PyCharmMiscProject/testsdk.py
INFO: RobotIPAddress - "10.192.1.2"
------
diagnostic_value:
stamp: 980020701021
name: battery
level: 0
code: 100
message: 100
------
diagnostic_value:
stamp: 980016991896
name: battery_charge
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 980016965062
name: battery_cost
level: 0
code: 0
message: 100
------
diagnostic_value:
stamp: 980016998021
name: battery_voltage
level: 0
code: 0
message: 56
------
diagnostic_value:
stamp: 590572955807
name: controller
level: 0
code: 1
message: manipulation_damping controller is ready
------
diagnostic_value:
stamp: 980017064521
name: current_12v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 980017054604
name: current_15v
level: 0
code: 0
message: 2
------
diagnostic_value:
stamp: 980017043229
name: current_24v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 13302781672
name: ecm_version
level: 0
code: 0
message: 1.0.25
------
diagnostic_value:
stamp: 24524362677
name: ethercat
level: 0
code: 0
message: ethercat ok!
------
diagnostic_value:
stamp: 14114416512
name: imu
level: 0
code: 0
message: OK
------
diagnostic_value:
stamp: 14059836923
name: lan_index
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 24534264177
name: left end-effector
level: 0
code: 3
message: limx 2F gripper
------
diagnostic_value:
stamp: 980017021646
name: motor1_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 980017015812
name: motor1_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 980017032437
name: motor2_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 980017027187
name: motor2_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 980017010854
name: motor_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 23464742635
name: motor_version
level: 0
code: 0
message: 1: 1.2.23; 2: 1.2.23; 3: 1.2.23; 4: 1.2.23; 5: 1.2.23; 6: 1.2.23; 7: 1.2.23; 8: 1.2.23; 9: 1.2.23; 10: 1.2.23; 11: 1.2.23; 12: 1.2.23; 13: 1.2.23; 14: 1.2.23; 15: 1.2.18; 16: 1.2.18;
------
diagnostic_value:
stamp: 980017005604
name: motor_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 24543136677
name: right end-effector
level: 0
code: 3
message: limx 2F gripper
------
diagnostic_value:
stamp: 13993435499
name: rotate_status
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 13983715707
name: version
level: 0
code: 0
message: robot-tron2-r-1.2.20.20251225182226
------
diagnostic_value:
stamp: 980017059562
name: voltage_12v
level: 0
code: 0
message: 12
------
diagnostic_value:
stamp: 980017049646
name: voltage_15v
level: 0
code: 0
message: 16
------
diagnostic_value:
stamp: 980017037687
name: voltage_24v
level: 0
code: 0
message: 25
------
diagnostic_value:
stamp: 14044807630
name: vr_follow_head
level: 0
code: 0
message: disable vr follow head
------
diagnostic_value:
stamp: 50767494403
name: working_mode
level: 0
code: 0
message: developer_mode
------
diagnostic_value:
stamp: 1010017039577
name: battery_cost
level: 0
code: 0
message: 100
------
diagnostic_value:
stamp: 1010017068743
name: battery_charge
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 1010017076910
name: battery_voltage
level: 0
code: 0
message: 56
------
diagnostic_value:
stamp: 1010017083035
name: motor_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1010017088577
name: motor_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1010017093243
name: motor1_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1010017099077
name: motor1_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1010017104327
name: motor2_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1010017108993
name: motor2_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1010017114243
name: voltage_24v
level: 0
code: 0
message: 25
------
diagnostic_value:
stamp: 1010017119785
name: current_24v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1010017125618
name: voltage_15v
level: 0
code: 0
message: 16
------
diagnostic_value:
stamp: 1010017130285
name: current_15v
level: 0
code: 0
message: 2
------
diagnostic_value:
stamp: 1010017134952
name: voltage_12v
level: 0
code: 0
message: 12
------
diagnostic_value:
stamp: 1010017139618
name: current_12v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1010020746952
name: battery
level: 0
code: 100
message: 100
------
diagnostic_value:
stamp: 1040017682549
name: battery_cost
level: 0
code: 0
message: 100
------
diagnostic_value:
stamp: 1040017712591
name: battery_charge
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 1040017719008
name: battery_voltage
level: 0
code: 0
message: 56
------
diagnostic_value:
stamp: 1040017725424
name: motor_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1040017730966
name: motor_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1040017736216
name: motor1_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1040017741758
name: motor1_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1040017747299
name: motor2_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1040017752549
name: motor2_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1040017758674
name: voltage_24v
level: 0
code: 0
message: 25
------
diagnostic_value:
stamp: 1040017763924
name: current_24v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1040017769174
name: voltage_15v
level: 0
code: 0
message: 16
------
diagnostic_value:
stamp: 1040017774133
name: current_15v
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 1040017779091
name: voltage_12v
level: 0
code: 0
message: 12
------
diagnostic_value:
stamp: 1040017783758
name: current_12v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1040021298341
name: battery
level: 0
code: 100
message: 100
------
diagnostic_value:
stamp: 1070017263855
name: battery_cost
level: 0
code: 0
message: 100
------
diagnostic_value:
stamp: 1070017288647
name: battery_charge
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 1070017295064
name: battery_voltage
level: 0
code: 0
message: 56
------
diagnostic_value:
stamp: 1070017301480
name: motor_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1070017307022
name: motor_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1070017311980
name: motor1_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1070017317522
name: motor1_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1070017323939
name: motor2_voltage
level: 0
code: 0
message: 55
------
diagnostic_value:
stamp: 1070017328605
name: motor2_current
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1070017333855
name: voltage_24v
level: 0
code: 0
message: 25
------
diagnostic_value:
stamp: 1070017339105
name: current_24v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1070017344064
name: voltage_15v
level: 0
code: 0
message: 16
------
diagnostic_value:
stamp: 1070017348730
name: current_15v
level: 0
code: 0
message: 1
------
diagnostic_value:
stamp: 1070017353980
name: voltage_12v
level: 0
code: 0
message: 12
------
diagnostic_value:
stamp: 1070017358939
name: current_12v
level: 0
code: 0
message: 0
------
diagnostic_value:
stamp: 1070020944689
name: battery
level: 0
code: 100
message: 100
Process finished with exit code 130 (interrupted by signal 2:SIGINT)
2.2.11 setRobotLightEffect Interface Description
| Item | Description |
|---|---|
| Function Name | setRobotLightEffect |
| Function Prototype | def setRobotLightEffect(self, effect: datatypes.LightEffect) |
| Function Overview | On a physical robot, this method sets the robot's lighting effect. |
| Parameters | effect: enumeration value representing the required robot light effect. See datatypes.LightEffect. |
| Return Value | Returns True on success and False on failure. |
Remarks - Tron2::LightEffect enumeration definition:
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, // red flashing (slow)
LOW_FLASH_GREEN, // green flashing (slow)
LOW_FLASH_BLUE, // blue flashing (slow)
LOW_FLASH_CYAN, // cyan flashing (slow)
LOW_FLASH_PURPLE, // purple flashing (slow)
LOW_FLASH_YELLOW, // yellow flashing (slow)
LOW_FLASH_WHITE, // white flashing (slow)
FAST_FLASH_RED, // red flashing (fast)
FAST_FLASH_GREEN, // green flashing (fast)
FAST_FLASH_BLUE, // blue flashing (fast)
FAST_FLASH_CYAN, // cyan flashing (fast)
FAST_FLASH_PURPLE, // purple flashing (fast)
FAST_FLASH_YELLOW, // yellow flashing (fast)
FAST_FLASH_WHITE // white flashing (fast)
};
Code Example:
import sys
from functools import partial
import limxsdk.robot.Robot as Robot
import limxsdk.robot.RobotType as RobotType
import limxsdk.datatypes as datatypes
class RobotReceiver:
if __name__ == '__main__':
# Create a Robot instance of type Tron2
robot = Robot(RobotType.Tron2)
robot_ip = "10.192.1.2"
# Check whether a robot IP command-line argument is provided
if len(sys.argv) > 1:
robot_ip = sys.argv[1]
# Initialize the robot with robot_ip
if not robot.init(robot_ip):
sys.exit()
# Set the robot light effect to static red light
robot.setRobotLightEffect(datatypes.LightEffect.STATIC_RED)