Chapter 4: xgolib Robot Control
To make the robot move, you call the xgolib motion library. This chapter covers the most-used basic motions, then wheel-legged (Rider/RA) advanced control, with per-model parameter ranges and model-compatibility tips so your motion code runs correctly on every model.
In this chapter:
| Section | What it covers | Difficulty |
|---|---|---|
| 4.1 Basic Motion | move_x / turn / attitude / translation etc. + per-model parameter ranges | ★☆☆ |
| 4.2 Advanced Control | Wheel-legged XGO-RIDER / RA ra_* / rider_* interfaces | ★★★ |
| 4.3 Model Compatibility | XGO() auto-detect, model alias table, per-model branching | ★★☆ |
| 4.4 Debugging & Verification | Low-speed try, suspend test, state printing, per-model branch testing, etc. | ★☆☆ |
🧭 Reading path: Quadruped models (Mini/Lite/Mini3W) see 4.1; wheel-legged (Rider/RA) see 4.2; to run one codebase on multiple models, definitely read 4.3.
Prerequisites:
- Done Chapter 1, you can SSH into the robot;
- The robot is powered on and idle (motion commands directly drive the hardware);
- You know your model (Mini / Lite / Rider / RA).
4.1 Basic Motion (Quadruped Models)
Why xgolib: The robot's servos, IMU and battery all connect through a single UART, so you can't drive each servo directly. xgolib wraps that serial protocol into an object — creating an
XGO()instance is like “holding the whole robot's controls”; after that, one line of code makes it move forward, turn or perform an action, saving you from handling serial framing, checksums and per-model differences yourself.
xgolib is LuwuDynamics' open-source robot-dog motion library, published as the xgolib PyPI package (official repo LuwuDynamics/xgo_doglib); it focuses on the XGOMini / XGOLite / XGORider / XGOMini3W models, and the new version has dropped xgoedu support — install with pip3 install xgolib. The version bundled in Luwu-OS (v1.1.6) is at /opt/luwu-os/libs/xgolib/, and the unified XGO() auto-selects the model (__all__ = ['XGO', 'XGO_DOG', 'XGO_RIDER']).
4.1.1 Minimal Run-It Script (see the robot move first)
Save this as test_move.py and run python3 test_move.py:
from xgolib import XGO
import time
dog = XGO() # auto-detect the model and open the serial port
print("Connected model:", dog.read_firmware())
dog.forward(18) # speed 18, start moving forward
time.sleep(2) # keep moving forward for 2 seconds
dog.stop() # stop
print("Done: the robot should move forward ~2 s then stop")
✅ Expected result: The terminal prints
Connected model: M...(firmware) first, then the robot moves forward ~2 s and stops, finally printing the done message. If it errors out or the dog doesn't move, see 4.1.9 Common Errors.⚠️ Before the first run make sure the robot is charged, idle and clear of obstacles; try a small value like
10first, confirm the direction is correct, then increase it.
4.1.2 Connecting & Basic Motion
XGO() is the single entry point — it opens the serial port, probes the model and connects:
from xgolib import XGO
dog = XGO() # auto-detect the model (scans /dev/ttyAMA5, /dev/ttyAMA0)
# or set it manually: XGO("xgomini") / XGO("xgolite") ...
Motion methods take a speed command: pass the speed value, and with runtime>0 they run for that many seconds then stop; pass 0 to stop.
dog.move_x(10, runtime=2) # forward speed, stops after ~2 s (xgomini: -25~25)
dog.move_y(5, runtime=2) # lateral speed (-18~18)
dog.turn(10, runtime=1) # in-place turn speed (-100~100)
dog.move_x(0) # manual stop
4.1.3 Directional Semantics (Forward / Back / Left / Right)
Don't bother with axes — use the semantic methods, which are equivalent to move_x / move_y:
dog.move('x', 10) # generic direction: 'x' / 'y'
dog.forward(10) # forward (same as move_x)
dog.back(10) # backward
dog.left(10) # strafe left
dog.right(10) # strafe right
4.1.4 Attitude & Translation (Quadruped Body)
- Attitude
attitude: control the body's pitch / roll / yaw angle (xgomini: Roll ±20° / Pitch ±22° / Yaw ±16°) - Translation
translation: position & height of the body on the X / Y / Z axes (xgomini: X ±35 / Y ±19.5 / Z 75~120mm)
dog.attitude('p', 15) # pitch, single axis
dog.attitude(['y', 'p'], [5, 10]) # yaw + pitch
dog.translation('x', 10) # forward/backward (-35~35)
dog.translation('y', 5) # left/right (-19.5~19.5)
dog.translation('z', 90) # body height (75~120)
4.1.5 Gaits, Actions & Performance
dog.gait_type('trot') # gaits: trot / walk / high_walk / slow_trot
dog.action(13, wait=True) # play a preset action (ID 1~255, wait=True blocks until done)
dog.perform(1) # start cyclic performance (auto-rotates actions); perform(0) stops
dog.pace(0) # mark time in place (0 stop / 1 start)
dog.mark_time(10) # mark-time height (xgomini: 10~35)
💡 Action IDs depend on the firmware action table and differ by model/version, so try a small ID first to confirm.
4.1.6 Arm (models with an arm, e.g. XGO-MINI)
dog.arm(120, 150) # cartesian control (x, z)
dog.arm_polar(200, 130) # polar control (angle theta, distance r)
dog.arm_mode(1) # arm mode switch
dog.arm_speed(50) # arm speed (0~100)
dog.claw(245) # gripper (0=open, 255=closed)
4.1.7 Reading State
batt = dog.read_battery() # battery 0~100
fw = dog.read_firmware() # firmware version string, e.g. 'M107'
roll = dog.read_roll() # current roll angle
Other general commands:
dog.stop()stops all motion,dog.imu(1)toggles self-balance (1=on, 0=off),dog.reset()resets to the initial pose.
4.1.8 Parameter Range Table (model-dependent, from changePara() in xgolib_dog.py)
| Parameter | xgomini | xgolite |
|---|---|---|
Forward speed move_x | ±25 | ±25 |
Lateral speed move_y | ±18 | ±18 |
Turn speed turn | ±100 | ±100 |
| Translation X / Y | ±35 / ±19.5 | ±25 / ±18 |
| Body height Z | 75~120mm | 60~110mm |
| Attitude Roll / Pitch / Yaw | ±20 / ±22 / ±16 | ±20 / ±10 / ±12 |
Mark-time height mark_time | 10~35 | 10~25 |
4.1.9 Common Errors
| Symptom | Cause | Fix |
|---|---|---|
PermissionError: [Errno 13] opening serial port | The port is held by another process (e.g. the launcher is running) | Close the process holding the port, run as sudo, or quit the launcher first |
Could not open port ... | Probe scanned the wrong port, or the robot isn't powered on | Confirm the robot is on; to force a port use XGO(port="/dev/ttyAMA5") |
| Connects but the dog doesn't move | runtime not set, speed too low / wrong direction | Use the 4.1.1 run-it script to confirm basic motion, then tune the speed |
Warning on XGO("xgomini3w") | xgolib 1.1.6 doesn't support XGO-MINI3W (see 4.3) | Use XGO("mini3w") or auto-detect instead |
4.2 Advanced Control (XGO-RIDER / RA wheel-foot)
RA and Rider refer to the same wheel-foot robot. Both ra_* and rider_* are methods on the XGO_RIDER class (ra_* are aliases targeting RA). Calling XGO("ra") or XGO("rider") returns an XGO_RIDER instance:
from xgolib import XGO
dog = XGO("ra") # returns an XGO_RIDER instance (internally version="xgorider")
# --- RA interface (ranges -87~+87 etc., see method docstrings) ---
dog.ra_mode(0) # mode switch: 0=wheel-foot mode, 1=car mode
dog.ra_move_x(87) # move forward/backward, speed -87~+87
dog.ra_turn(87) # rotate, speed -87~+87
dog.ra_height(50) # chassis height, range 0~100
dog.ra_roll(10) # roll angle, -87~+87
dog.ra_pitch(10) # pitch angle, -87~+87
dog.ra_action(1) # preset actions: 1=sway L/R, 2=raise/lower, 3=forward/back,
# 4=snake, 5=lift+rotate, 6=swing, 255=reset
dog.ra_perform(1) # start/stop performance mode (action rotation)
dog.ra_imu(1) # self-balance on/off
dog.ra_led(0, [255, 0, 0]) # LED strip color (index, RGB list)
dog.ra_reset() # reset
# --- Rider native interface (rider_*) ---
dog.rider_move_x(1.0) # forward speed (VX ±1.5)
dog.rider_turn(90) # turn speed (VYAW ±360)
dog.rider_height(90) # chassis height (Z 60~120mm)
dog.rider_roll(10) # roll angle (Roll ±17°)
dog.rider_action(1) # preset action (1~255)
dog.rider_perform(1) # performance mode on/off
dog.rider_balance_roll(1) # self-balance on/off (IMU)
dog.rider_reset_odom() # reset odometry
dog.rider_led(0, [0, 255, 0]) # LED strip
dog.rider_read_battery() # battery level
dog.rider_read_roll() # read roll angle
dog.rider_read_pitch() # read pitch angle
dog.rider_read_yaw() # read yaw angle
XGO_RIDER also inherits all generic motion methods (move_x/move_y/turn/translation/attitude/action, etc.), but parameter ranges follow the xgorider table (VX ±1.5 / VY ±1.0 / VYAW ±360 / Z 60~120mm / Roll ±17°).
4.3 Model Compatibility
XGO(version="auto", port=None, baud=115200, verbose=False):
version: when omitted, the serial ports are scanned automatically (SCAN_PORTS = ["/dev/ttyAMA5", "/dev/ttyAMA0"]) and the firmware is probed- Supported model aliases (short / full name):
| Short Name | Full Name | Model |
|---|---|---|
"mini" | "xgomini" | XGO-MINI |
"lite" | "xgolite" | XGO-LITE |
"mini3w" | "xgomini2sw" | XGO-mini2SW |
"rider" | "xgorider" | XGO-RIDER (two-wheel) |
"ra" | "xgora" | same as above; returns XGO_RIDER |
⚠️
XGO("xgomini3w")is not a valid alias (xgolib 1.1.6 does not support XGO-MINI3W; an unknown name prints a warning and falls back toxgominibehavior). Note the difference:xgomini3win thelauncher/devicetable.hmodel registry is only used for demo filtering.
from xgolib import XGO, XGO_RIDER
dog = XGO() # auto-detect the model
# You can also specify: XGO("xgomini") / XGO("xgolite") / XGO("mini3w") / XGO("rider") / XGO("ra")
# Branch by model type
if isinstance(dog, XGO_RIDER):
dog.rider_roll(10) # wheel-foot: use rider_*/ra_* interfaces
else:
dog.attitude('r', 10) # four-legged: use attitude/translation etc.
# Firmware version check
fw = dog.read_firmware() # returns an ASCII firmware string, e.g. 'M107' / 'RA100' / 'MW300'
if fw.upper().startswith('RA'):
pass # RA-specific logic
For model detection at the system level, call configs/detect_device.py (the launcher uses it too):
python3 /opt/luwu-os/configs/detect_device.py
# prints one model string: xgomini / xgolite / xgomini2sw / xgorider / unknown
# priority: live serial probe > configs/device.ini manual override > unknown
4.4 Debugging & Verification Tips
The worst thing when writing motion code is a “one-run full-body” — the dog may run into someone or trip itself. I recommend:
| Tip | How |
|---|---|
| Start slow | Start from a small value like 10 (the scale in the 4.1.8 table), confirm the direction, then increase |
| Test suspended | Hold the dog over a table edge or on a stand; first check whether the servos target the right angle, then test on the ground |
| Print state step by step | Between steps do print(dog.read_battery(), dog.read_roll()) to confirm state before/after motion |
| Timeout fallback | Add time.sleep() and dog.stop() around critical motion so an instruction can't hang |
| Test per model | Run the same code on a Mini and a Lite to feel the parameter-range differences (4.1.8) |
⚠️ For any motion test, first confirm enough battery, clear surroundings and the dog is idle; if anything looks wrong, press
dog.stop()or power off immediately.
