Skip to content

Controlling Motions from Python

To build walking into your own script, use the Palmimo class in robot.py. Every method it exposes is listed in Core Methods; this page shows the shapes those calls take.

Step Reference
Confirm all motors respond diagnose_servos.py scan
Reset to neutral stance Palmimo.stop() eases to neutral
Torque off (move by hand) Exit the app / cut power (see Safety)

Lift the unit so the feet are in the air the first time you run a new motion.

from palmimo_sdk import Palmimo
robot = Palmimo(gait_speed=0.012, step_length=30.0, step_height=30.0)
robot.forward()
for _ in range(120): # ~2 seconds @ 60 Hz
positions = robot.step() # dict[str, int]: motor name -> tick (0-4095)
print(positions)
robot.stop()
for _ in range(60): # return to neutral
robot.step()

⚠️ Ticks run 0-4095, but keep motion inside the safe range 200-3900 — the raw ends are mechanical limits. See Safety Design.

The return value of step() (a dict of target ticks for the 18 leg motors plus neck_yaw and neck_pitch1, 20 motors total — the neck’s neck_pitch2, servo ID 20, physically exists but MotionEngine holds no state for it and omits it from the output) is what DynamixelDriver.write_positions() takes, so passing it there drives real hardware. For the concrete bus-initialization sequence, see DynamixelDriver and the Peripherals & Connection section of the API reference.

Palmimo.play() in robot.py plays back a sequence of (motion name, seconds) steps. The special motion look_around sweeps the neck in a sine wave. Note that with the (name, seconds) tuple form, motion stays at the default Motion.IDLE, so the legs stay still during that segment. To sweep the neck while walking, pass RoutineStep(motion=..., neck_sweep=True) instead of a tuple.

routine = [
("forward", 2.0),
("rotate_left", 1.0),
("dance", 3.0),
("look_around", 2.0),
("idle", 0.5),
]
robot = Palmimo()
for positions in robot.play(routine, fps=60):
# send positions to real hardware / a simulator
...

For real-time playback (paced internally with time.sleep), use play_realtime.

  • Serial port not found: Check the actual device name with ls /dev/tty.usbmodem* (macOS) or ls /dev/ttyACM* (Linux), then specify the port explicitly when constructing DynamixelDriver
  • Motors don’t move / rattle: Check power supply capacity and cable connections, and confirm individual IDs respond with diagnose_servos.py scan
  • Motion is erratic: Always lift the unit so the feet are in the air, run against real hardware to check behavior, then set it down