pib-sdk
A Python SDK for pib humanoid robot: kinematics, motor control, speech, live telemetry, and everything a user can create in Cerebra (pib's web UI) — poses, Blockly programs, RGB-button bindings, the voice assistant, and more.
Positions are in millimetres, joint angles in degrees, and orientations as roll–pitch–yaw (see Conventions).
Documentation
This README is the entry point. Continue with:
| Document | Use it for |
|---|---|
| API reference | Signatures, parameters, return values, exceptions, units, and backend caveats for all 161 public API elements |
| Tutorials | Guided tasks from first connection through IK, poses, depth vision, IMU, drawing, programs, buttons, display, and relay |
| Runnable examples | An index of scripts under examples/ and whether each needs a live robot |
| Architecture | Call-flow diagrams for rosbridge, REST, kinematics, programs, poses, telemetry, and IMU |
Architecture at a glance
pib-sdk is one of two independent clients of the same robot-side services — the other is Cerebra. Neither wraps the other: both call the same rosbridge services/topics and the same pib-backend REST routes, so nothing this SDK does (including the voice assistant and Blockly programs) is a reimplementation of logic that already lives on the robot.
flowchart LR
SDK["pib-sdk\n(your script)"] -- "WebSocket JSON" --> RB{{"rosbridge : 9090"}}
SDK -- "HTTP JSON" --> BE["pib-backend : 5000"]
CEREBRA["Cerebra\n(web UI)"] -- "WebSocket JSON" --> RB
CEREBRA -- "HTTP JSON" --> BE
RB --- Nodes["ROS2 nodes\nmotor · voice assistant ·\nprogram executor · camera ..."]
BE --- DB[("database")]
Full control-flow diagrams — the voice assistant call sequence, the Blockly program proxy, the kinematics/Pinocchio layering, telemetry pull-vs-push — live in docs/ARCHITECTURE.md.
Concepts
Two equal clients
Cerebra and pib-sdk are peers. A change made through either client reaches the same robot-side services and persisted resources. The SDK does not automate the Cerebra UI or duplicate its assistant, pose, or program logic.
Connection and lifecycle model
Use one host name for a robot, normally pib.local. Real-time APIs connect to
rosbridge (9090); persisted Cerebra data uses pib-backend HTTP (5000).
Each live client (Write, Speak, Telemetry, Camera, and so on) owns its
own rosbridge WebSocket and should be closed or used as a context manager.
Robot is a convenient lifecycle/configuration bundle. It constructs
Write, Speak, and Telemetry once each and exposes one stateless
BackendClient; it does not multiplex those clients over one WebSocket.
Create only the specialized client you need when connection count matters.
Motor selectors and names
Write accepts literal firmware motor names such as "elbow_right" and
selector tokens such as right_arm, left_hand, head, and All. Group
tokens expand locally, in a stable order, without querying the backend.
Write.move(group, angle) applies one angle to all selected motors;
Write.move(group, *angles) consumes one angle per expanded motor. Hand
action tokens (open_right_hand, etc.) are complete actions and take no
angle. See Joint ↔ motor names.
Units and conventions
Public movement and kinematics APIs use degrees and millimetres. The SDK
converts motor commands to pib-backend's internal hundredths-of-a-degree
representation at the service boundary. The exception is the low-level
Write.send_timed_trajectory, whose positions are deliberately already in
those internal units. Depth pixels are unsigned 16-bit millimetres, where
zero means invalid.
Errors and command results
Argument and model problems raise TypeError, ValueError, KeyError, or
FileNotFoundError. Connection setup raises ConnectionError or
RuntimeError, depending on the client; waiting APIs may raise
TimeoutError. REST failures raise BackendError. Several live command
methods instead return False on backend rejection (and Write also returns
False after service-call failures), so check booleans when command delivery
matters. Camera depth reads use None/0.0 for unavailable data. Exact
behavior is listed in the API reference.
Requirements
- Python 3.9+ (3.11+ works out of the box on current Raspberry Pi OS).
- Pinocchio (
pin)>= 3.1— the kinematics backend, installed automatically from PyPI wheels. - numpy
>= 1.22. - roslibpy
>= 1.6— rosbridge client (only needed for things that talk to hardware; pure kinematics needs no connection). - A running rosbridge server on the robot (WebSocket, default port
9090) for motor control, speech, telemetry, and live program/voice- assistant control. - pib-backend's REST API (default port
5000, same host, no authentication) for anything sourced from Cerebra: poses, programs, button bindings, camera/motor settings, personalities, chats.
There are no system packages to install and no version-pinned scientific
dependencies. Pinocchio ships prebuilt wheels (including aarch64 for the
Raspberry Pi), so a plain pip install works — no apt, no conda, no
building from source.
Installation
From PyPI:
pip install pib-sdk
Quick start
Kinematics
from pib_sdk import fk, ik
# Inverse kinematics: target position in millimetres -> joint angles in degrees
q_deg = ik("right", xyz=[-400, 100, 900])
print("IK angles:", q_deg)
# Forward kinematics: joint angles in degrees -> end-effector pose
pose = fk("right", [0, 45, 0, 0, 90, 0])
print("palm position (mm):", pose.translation)
print("palm orientation (3x3):\n", pose.rotation)
fk returns a pinocchio.SE3;
read pose.translation (mm) and pose.rotation, or convert:
from pib_sdk import pose_to_xyz_rpy
xyz_mm, rpy_deg = pose_to_xyz_rpy(pose)
Need raw Pinocchio? You already have it —
fk/ikconsume and return realpinocchio.SE3objects, not a pib-sdk wrapper type. For the underlying model,pib_sdk.robot_model.get_arm_model("right").modelis the actualpin.Model(and.create_data()a freshpin.Data) thatiksolves against — useful for calling Pinocchio functions directly (custom Jacobians, etc.) against that same model.pinocchioitself is a required dependency (pin>=3.1), soimport pinocchio as pinalways works standalone; pib-sdk just never re-exports the module aspib_sdk.pin. Details: docs/ARCHITECTURE.md.
Driving the arm with an IK solution
The IK joint order matches the right_arm / left_arm motor groups exactly, so
the result feeds straight into Write.move:
from pib_sdk import Write, ik, right_arm
q_deg = ik("right", xyz=[-400, 100, 900])
with Write(host="pib.local") as w: # rosbridge on the robot (constructor default: localhost)
w.move(right_arm, *q_deg) # one angle per joint, in order
Motor control
from pib_sdk import Write, All, default, right_arm, left_hand, zero_position, open_right_hand
w = Write(host="localhost") # connects to rosbridge
w.set(All, default) # apply the default MotorSettings to all motors
w.set(left_hand, velocity=6000) # override settings for one group
w.move(right_arm, -30.0) # uniform: one angle applied to the whole group
w.move(right_arm, 10, -20, 5, 30, -10, 0) # vector: one angle per joint
w.move(All, zero_position) # everything to 0°
w.move(open_right_hand) # hand shortcut
Speech
from pib_sdk import Speak
with Speak(host="localhost") as sp:
sp.say("hello world") # default voice: Emma (Female, English)
sp.say("guten Tag", voice="Hannah") # preset -> Female / German
Head camera pose (kinematics)
from pib_sdk import camera_pose
pose = camera_pose(pan_deg=30, tilt_deg=-10) # camera pose in the base frame (mm)
This is forward kinematics for the head/camera frame — not to be confused
with pib_sdk.features.camera, which fetches an actual JPEG snapshot; see
docs/REFERENCE.md.
Everything else: one configured bundle
Robot bundles motor control, speech, telemetry, and pib-backend's REST API
behind a single host and context manager. The three live clients retain their
own rosbridge connections:
from pib_sdk import right_arm
from pib_sdk.robot import Robot
with Robot(host="pib.local") as robot:
robot.write.move(right_arm, -30.0)
robot.speak.say("moving my arm")
print(robot.telemetry.get_position_deg("shoulder_vertical_right"))
print(robot.backend.list_poses())
| Want to... | Use |
|---|---|
| Read a motor's last commanded position / live current draw | pib_sdk.telemetry.Telemetry |
| Talk to pib-backend's REST API directly (poses, programs, motors, camera, voice assistant...) | pib_sdk.backend.BackendClient |
| Turn an image into an arm-drawn trajectory | pib_sdk.features.drawing |
| Drive to / save poses created in Cerebra | pib_sdk.features.poses |
| Run or stop a Blockly program saved in Cerebra | pib_sdk.features.programs |
| See/change which program an RGB button triggers | pib_sdk.features.buttons |
| Grab a camera snapshot or millimetre depth data | pib_sdk.features.camera |
| Drive the voice assistant: state, chats, personalities | pib_sdk.features.assistant |
| Push an image to pib's screen | pib_sdk.features.display |
| Read/set the solid-state relay | pib_sdk.features.relay |
| Read the latest IMU acceleration and gyro sample | pib_sdk.features.imu |
features.assistantandfeatures.programscall the exact same rosbridge services and REST routes Cerebra itself uses for chat and "run program" — see Two equal clients.
Details, caveats, and full examples for each: docs/REFERENCE.md. For guided workflows, continue with docs/TUTORIALS.md.
Joint ↔ motor names
The URDF and the motor firmware use slightly different names for a few joints.
The SDK keeps both: kinematics is expressed in URDF joint order, and each chain
also carries the matching motor name so IK output lines up with
Write.move. You normally never need this table — it's here for reference.
| Chain | URDF joint | pib motor | Limits (°) |
|---|---|---|---|
| right_arm | shoulder_vertical_right |
shoulder_vertical_right |
−90 … 90 |
| right_arm | shoulder_horizontal_right |
shoulder_horizontal_right |
−90 … 90 |
| right_arm | upper_arm_right |
upper_arm_right_rotation |
−90 … 90 |
| right_arm | elbow_right |
elbow_right |
−45 … 90 |
| right_arm | forearm_right |
lower_arm_right_rotation |
−90 … 90 |
| right_arm | wrist_right |
wrist_right |
−30 … 30 |
| head | head_horizontal |
turn_head_motor |
−90 … 90 |
| head | head_vertical |
tilt_forward_motor |
−45 … 70 |
The left arm mirrors the right. Inspect any chain yourself:
python -m pib_sdk.robot_model
Hardware sign check. The URDF's zero pose has both arms pointing straight out to the sides, matching pib's motor zero. Joint directions in the URDF follow the URDF's axis conventions; if a real motor turns opposite to the model on your unit, flip it with the motor's
invertsetting (w.set("<motor>", invert=True)) rather than negating angles in your code. Verify one joint at a time on first bring-up.
Conventions
- Positions: millimetres.
- Joint angles: degrees. Order is base → tip, as printed by
python -m pib_sdk.robot_model. - Orientation: roll–pitch–yaw in degrees, applied as
R = Rz(yaw) · Ry(pitch) · Rx(roll)(ZYX — the same convention the previous Robotics Toolbox implementation used). - Poses:
pinocchio.SE3, expressed in pib's base frame.
Inverse kinematics options
ik(side, xyz=..., ...) (and ArmKinematics.inverse) accept:
| Option | Default | Meaning |
|---|---|---|
rpy_deg |
None |
Target orientation. None → position-only IK. |
initial_guess_deg |
zeros | Starting joint configuration (degrees). |
tolerance |
1e-4 |
Convergence threshold on the pose error (mm-scaled). |
max_iterations |
200 |
Solver iterations per attempt. |
mask |
auto | 6-element [x, y, z, rx, ry, rz] selector over the pose error. |
restarts |
50 |
Random in-limit restarts if the first solve misses. |
respect_limits |
True |
Keep the solution within mechanical joint limits. |
With respect_limits=True (the default), a returned solution is within the
loaded URDF's joint limits. Write.move separately accepts only [-90, 90]°
and can still return False if the backend rejects a command; verify custom
URDF limits against the real robot. IK raises ValueError if it cannot reach
the target.
Using a different or updated URDF
The package ships pib's V3 URDF at pib_sdk/data/pib_model.urdf. To use another
file — a newer revision, or a variant of your robot — point the loader at it:
from pib_sdk import set_urdf_path
set_urdf_path("/path/to/robot.urdf")
or set the PIB_URDF_PATH environment variable before importing. The joint and
frame names in pib_sdk.robot_model (RIGHT_ARM, LEFT_ARM, HEAD) must
match those in your URDF; mismatches raise a clear error listing the names the
URDF actually provides.
Meshes are not required. Kinematics uses only the URDF's joint tree, so the visual/collision
.stlmeshes referenced by the URDF are not needed and are not bundled. Pinocchio loads the kinematic model without them.
Development
pip install -e ".[dev]"
pytest # run the test suite
ruff check src tests
ruff format src tests
python tools/check_doc_coverage.py
python tools/check_doc_links.py
python tools/check_examples.py
python -m build # build the sdist and wheel
The tests include an independent forward-kinematics implementation (URDF parsed
with xml.etree, transforms multiplied with plain numpy) that cross-checks the
Pinocchio models to machine precision — a guard against URDF-loading or
unit-scaling regressions. Everything that talks to rosbridge or pib-backend is
tested offline against fakes (see tests/conftest.py) — no real robot or
server needed to run the suite.
License
MIT — see LICENSE.
Release files for pib-sdk 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pib_sdk-0.5.2.tar.gz | 116.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pib_sdk-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 194.0 kB
Release files / pib_sdk-0.5.2.tar.gz
| Download URL | pib_sdk-0.5.2.tar.gz |
|---|---|
| Size | 116.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2273e59ccc5d23dbc7fb6ea019e2e8702a95b0323cbdc1cb5f429b406dfd2030
|
|
BLAKE2b-256 checksum How to use checksums |
edf9956d9eea935c644433f8aa78fd8ab264b3723142da4b3a9406430c4228d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / pib_sdk-0.5.2-py3-none-any.whl
| Download URL | pib_sdk-0.5.2-py3-none-any.whl |
|---|---|
| Size | 77.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
138e3aeee54bad405341acf054c3aa604f2c548af585a2f2611c96fb300b614c
|
|
BLAKE2b-256 checksum How to use checksums |
2eb5073912337412841d6ab607cfab201d80916f4ddeb91bac25fb55119efd58
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|