Chapter 4 - xgolib Robot Control

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:

SectionWhat it coversDifficulty
4.1 Basic Motionmove_x / turn / attitude / translation etc. + per-model parameter ranges★☆☆
4.2 Advanced ControlWheel-legged XGO-RIDER / RA ra_* / rider_* interfaces★★★
4.3 Model CompatibilityXGO() auto-detect, model alias table, per-model branching★★☆
4.4 Debugging & VerificationLow-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 10 first, 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)

Parameterxgominixgolite
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 Z75~120mm60~110mm
Attitude Roll / Pitch / Yaw±20 / ±22 / ±16±20 / ±10 / ±12
Mark-time height mark_time10~3510~25

4.1.9 Common Errors

SymptomCauseFix
PermissionError: [Errno 13] opening serial portThe 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 onConfirm the robot is on; to force a port use XGO(port="/dev/ttyAMA5")
Connects but the dog doesn't moveruntime not set, speed too low / wrong directionUse 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 NameFull NameModel
"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 to xgomini behavior). Note the difference: xgomini3w in the launcher/devicetable.h model 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:

TipHow
Start slowStart from a small value like 10 (the scale in the 4.1.8 table), confirm the direction, then increase
Test suspendedHold 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 stepBetween steps do print(dog.read_battery(), dog.read_roll()) to confirm state before/after motion
Timeout fallbackAdd time.sleep() and dog.stop() around critical motion so an instruction can't hang
Test per modelRun 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.