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.
Before You Run
Section titled “Before You Run”| 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.
Minimal Example (Dry Run)
Section titled “Minimal Example (Dry Run)”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.
Sending to Real Hardware Too
Section titled “Sending to Real Hardware Too”The return value of step() (a dict of target ticks for the 18 leg motors plus
neck_yaw, neck_pitch1, and neck_pitch2, 21 motors total — see robot.look()
in Core Methods for how the two
neck-pitch joints share a look target) 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.
Choreography
Section titled “Choreography”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.
Keyframe Playback
Section titled “Keyframe Playback”To make a gesture of your own without writing an engine method, list the poses the robot should pass through, each with the time to reach it, and let the SDK ease between them. Angles are degrees from each servo’s neutral; a joint a keyframe leaves out holds where it was.
-
Write the track and check it in a dry run. With no driver attached nothing moves, and an invalid track (a joint out of range, a move too fast) raises
ValueErroron the call:from palmimo_sdk import Keyframe, Palmimoraise_front_right = [Keyframe(1.0, {"leg_6_pitch1": -30.0, "neck_yaw": 10.0}), # lift the leg, glance asideKeyframe(1.5, {"leg_6_yaw": 15.0}), # swing it sideways; the rest holdKeyframe(2.5, {"leg_6_pitch1": 0.0, "leg_6_yaw": 0.0}), # put it back]robot = Palmimo()frames = list(robot.iter_keyframes(raise_front_right, arm_legs=[6]))print(len(frames), frames[60]["leg_6_pitch1"]) # 2.5 s + the 1 s glide home, at 60 fps -
Lift the robot so every foot is in the air, then play it on the hardware:
from palmimo_sdk import DynamixelDriver, Palmimowith Palmimo(driver=DynamixelDriver()) as robot:robot.play_keyframes(raise_front_right, arm_legs=[6])Pass the legs the track lifts as arms in
arm_legs, so the overload guard watches them with the arm threshold. -
Set it down and play it on the floor only after it looks right in the air. The range checks stop servo-limit and speed mistakes, not a leg swinging into the body or another leg.
The rules and the arguments are in Keyframe Playback in the API reference.
Recording Poses by Hand
Section titled “Recording Poses by Hand”Instead of typing angles, move the robot into each pose and capture it.
-
Lift the robot so every foot is in the air. The legs you record go limp and carry no weight.
-
Start the recorder with the legs to move (here the front-right leg, 6):
Terminal window uv run python scripts/record_keyframes.py --legs 6 --step 1.5 --out wave.json -
Pose the leg by hand and press Enter for each pose (
uundoes,qfinishes). -
Answer
yto play it back while still lifted; if it is refused, edit the file (a longer--stepgives every move more time). -
Play the file from your own code. Playback starts from wherever the robot is when you call it — neutral right after connecting — whereas the recorder’s preview starts from the last pose you captured, so the first move can differ:
from palmimo_sdk import DynamixelDriver, Palmimo, load_keyframeswith Palmimo(driver=DynamixelDriver()) as robot:robot.play_keyframes(load_keyframes("wave.json"), arm_legs=[6])
The recorder’s options are in scripts/README.md.
Troubleshooting
Section titled “Troubleshooting”- Serial port not found: Check the actual device name with
ls /dev/tty.usbmodem*(macOS) orls /dev/ttyACM*(Linux), then specify the port explicitly when constructingDynamixelDriver - 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
Related Documents
Section titled “Related Documents”- Motions and gait parameters — what you can call and what you can tune
- How Palmimo moves — why the gait is built this way
- Motion development guide — adding a new motion

