API Reference¶
The interfaces below are defined in waldoctl, the abstraction layer that all backends implement. When you write from parol6 import RobotClient in a script, that RobotClient is a concrete subclass of waldoctl.RobotClient — it inherits the same methods documented here, plus any backend-specific extras. The same applies to Robot, tool specs, and status types.
In short: this reference covers everything available to your scripts regardless of which backend you're using.
Robot¶
waldoctl.Robot
¶
Bases: ABC
Unified robot interface — the single entry point for any backend.
Combines identity, joint configuration, tool definitions, kinematics, lifecycle management, and client factories into one ABC.
Required methods are marked with @abstractmethod. Optional
capabilities have concrete defaults that backends override as needed.
name: str
abstractmethod
property
¶
Human-readable robot name, e.g. "PAROL6".
joints: JointsSpec
abstractmethod
property
¶
Joint configuration: count, names, limits, home position.
tools: ToolsSpec
property
¶
Backend-native tools composed with plugins registered via
waldoctl.tools. Cached per instance (entry points are static).
native_tools: ToolsSpec
abstractmethod
property
¶
The backend's own tools; composed with plugin tools by :attr:tools.
Backends implement this; consumers read :attr:tools (which adds plugin
tools).
cartesian_limits: CartesianKinodynamicLimits
abstractmethod
property
¶
Jog-mode Cartesian velocity and acceleration limits.
position_unit: Literal['mm', 'm']
abstractmethod
property
¶
How this robot's users think about distance (display hint).
has_force_torque: bool
property
¶
Whether force / torque readout is available.
has_freedrive: bool
property
¶
Whether a freedrive / teach mode is available.
digital_outputs: int
abstractmethod
property
¶
Number of digital output pins.
digital_inputs: int
abstractmethod
property
¶
Number of digital input pins.
urdf_path: str
abstractmethod
property
¶
Path to the URDF file for 3-D rendering.
mesh_dir: str
abstractmethod
property
¶
Directory containing STL / mesh files referenced by the URDF.
joint_index_mapping: tuple[int, ...]
abstractmethod
property
¶
Maps URDF joint indices to control joint indices.
motion_profiles: tuple[str, ...]
property
¶
Available motion profile names.
At least one profile is required. The default is ("linear",)
which backends should override with their actual profiles.
cartesian_frames: tuple[str, ...]
property
¶
Available Cartesian reference frames for jogging.
Default includes both WRF and TRF which are required.
backend_package: str
abstractmethod
property
¶
Python package used by user scripts and subprocess workers.
sync_client_class: type
abstractmethod
property
¶
The synchronous client class (e.g. RobotClient).
Used for editor autocomplete discovery and stepping wrapper. Convention: backends export this class at their package level.
async_client_class: type
abstractmethod
property
¶
The asynchronous client class (e.g. AsyncRobotClient).
Used for editor command discovery (introspecting available methods). Convention: backends export this class at their package level.
has_collision_checking: bool
property
¶
Whether collision checking (self + workspace shapes) is available.
has_physics_simulation: bool
property
¶
Whether this backend's dry run can simulate as well as plan.
A planning dry run answers where the controller would tell the arm to go. A simulating one drives the same commands through the backend's control loop against a physics plant and reports what the arm did — servo lag, gravity sag, and objects that move because something pushed them.
False means the host shows the planned trajectory and nothing
else, which is how it behaved before any backend could do this.
A backend that returns True must also expose simulate on its
dry-run client (see SimulatedDryRunResult).
fk(q_rad: NDArray[np.float64], out: NDArray[np.float64]) -> NDArray[np.float64]
abstractmethod
¶
Forward kinematics.
q_rad: joint angles in radians (num_joints,).
out: pre-allocated (6,) buffer to write the result into.
Returns out filled with [x, y, z, rx, ry, rz] in meters + radians.
ik(pose: NDArray[np.float64], q_seed_rad: NDArray[np.float64]) -> IKResult
abstractmethod
¶
Inverse kinematics.
pose: [x, y, z, rx, ry, rz] — meters + radians.
q_seed_rad: current joint angles in radians (seed).
Returns an IKResult with q in radians.
set_active_tool(tool_key: str, tcp_offset_m: tuple[float, float, float] | None = None, variant_key: str | None = None) -> None
abstractmethod
¶
Apply tool transform to the local FK/IK model.
When set, fk() returns TCP position instead of flange position.
tcp_offset_m: optional (x, y, z) user offset in meters, composed on top of the tool's registered transform. variant_key: optional variant whose TCP overrides the tool default.
check_limits(q_rad: NDArray[np.float64]) -> bool
abstractmethod
¶
Return True if all joints are within limits.
fk_batch(joint_path_rad: NDArray[np.float64]) -> NDArray[np.float64]
abstractmethod
¶
Batch FK: (N, num_joints) radians -> (N, 6) poses (m + rad).
ik_batch(poses: NDArray[np.float64], q_start_rad: NDArray[np.float64]) -> list[IKResult]
abstractmethod
¶
Batch IK: (N, 6) poses -> list of IKResult (radians).
in_collision(q_rad: NDArray[np.float64]) -> bool
¶
Whether q_rad (radians) collides — with itself, the attached
tool, or a workspace keep-out shape.
colliding_pairs(q_rad: NDArray[np.float64]) -> list[tuple[str, str]]
¶
Colliding (name, name) geometry/link pairs at q_rad.
check_trajectory(q_path_rad: NDArray[np.float64]) -> int
¶
First colliding row index in (N, num_joints) path, or -1 if clear.
min_distance(q_rad: NDArray[np.float64]) -> float
¶
Min clearance at q_rad (signed; negative = penetration).
apply_shapes(shapes: list[Shape]) -> None
¶
Apply shapes to this process's in-process collision world, for preview and editing-pose queries.
The installation layer is not settable here: it is the robot's boot-time environment, and a backend whose in-process world is its runtime's own applies it from the config the runtime boots from. A host holding an installation proposal — not in that config yet, so enforced nowhere else — passes it here with the program layer.
This does not touch the backend — RobotClient.set_shapes does
that. A backend whose in-process world is its runtime's own engine
answers exactly as the runtime will; one that mirrors it separately
answers as well as the mirror. No-op without collision checking.
start(**kwargs: Any) -> None
abstractmethod
¶
Start the backend process / connection (blocking).
What "start" means is backend-specific: spawn a subprocess, connect to a remote server, launch a ROS node, etc.
stop() -> None
abstractmethod
¶
Stop the backend process and release resources.
is_available(**kwargs: Any) -> bool
abstractmethod
¶
Check if the backend is reachable / ready.
create_async_client(**kwargs: Any) -> RobotClient
abstractmethod
¶
Create an async client connected to this backend.
create_sync_client(**kwargs: Any) -> object
abstractmethod
¶
Create a synchronous client. Returns backend-specific type.
create_dry_run_client(**kwargs: Any) -> DryRunClient | None
¶
Create an offline simulation client, or None if unsupported.
RobotClient¶
waldoctl.RobotClient
¶
Bases: ABC
Generic async robot control interface.
Backends inherit from this ABC and implement the required abstract
methods. Optional methods have concrete defaults that raise
NotImplementedError.
Command palette integration: Methods that should appear in the editor's
command palette must include Category: and Example: sections in
their docstrings. The editor parses these at startup to build the palette.
Category: <name>— groups the command in the palette UI.Example:— the first indented line becomes the insertion snippet.
Command return codes: Command methods declared -> int follow one
convention, which backends MUST honor:
- Queued motion commands (Category: Motion) return the command's queue
index (
>= 0) once the backend acknowledges it;< 0when the command could not be confirmed or was rejected. - Every other command returns
1when the backend confirmed it applied the command,0when unconfirmed (unreachable, or no reply in time — the command may or may not have been applied), and< 0on rejection. A backend may raise instead of returning a negative code on active rejection; callers must treat both as failure.
A backend that cannot confirm application must never report success.
Success is >= 0 for queued motion, > 0 for everything else.
tool: ToolSpec
property
¶
The active bound tool.
Raises RuntimeError if no tool has been set.
close() -> None
abstractmethod
async
¶
Release resources and disconnect.
ping() -> PingResult | None
abstractmethod
async
¶
Check connectivity. Returns None if unreachable.
Category: Query
Example
rbt.ping()
wait_ready(timeout: float = 5.0, interval: float = 0.05) -> bool
abstractmethod
async
¶
Block until the robot backend is reachable or timeout expires.
stream_status() -> AsyncIterator[StatusBuffer]
abstractmethod
¶
Async iterator of real-time status snapshots (yields copies, safe to store).
stream_status_shared() -> AsyncIterator[StatusBuffer]
abstractmethod
¶
Async iterator of real-time status snapshots (shared buffer, zero-copy).
move_j(angles: list[float] | None = None, *, pose: list[float] | None = None, duration: float = 0.0, speed: float = 0.0, accel: float = 1.0, r: float = 0.0, rel: bool = False, wait: bool = False, timeout: float = 10.0, **wait_kwargs: Any) -> int
abstractmethod
async
¶
Joint-space move. angles: joint angles in degrees.
If pose is given, performs joint-interpolated move to Cartesian target. Returns the command index (>= 0) on success, -1 on failure.
Category: Motion
Example
rbt.move_j(
move_l(pose: list[float], *, frame: Frame = 'WRF', duration: float = 0.0, speed: float = 0.0, accel: float = 1.0, r: float = 0, rel: bool = False, wait: bool = False, **wait_kwargs: Any) -> int
abstractmethod
async
¶
Linear Cartesian move to [x, y, z, rx, ry, rz].
Returns the command index (>= 0) on success, -1 on failure.
Category: Motion
Example
rbt.move_l(
home(wait: bool = False, calibrate: bool = False, **wait_kwargs: Any) -> int
abstractmethod
async
¶
Move to the robot's home position.
An uncalibrated robot (first home after power-on) always runs the
backend's calibration sequence — searching for its end stops to
establish joint references — and ends at the home position. Once
calibrated, home() is a planned move to the home position;
calibrate=True re-runs the calibration sequence instead.
Returns the command index (>= 0) on success, -1 on failure.
Category: Motion
Example
rbt.home()
move_c(via: list[float], end: list[float], *, frame: Frame = 'WRF', duration: float | None = None, speed: float | None = None, accel: float = 1.0, r: float = 0, wait: bool = False, **wait_kwargs: Any) -> int
async
¶
Circular arc move through via to end.
Category: Motion
Example
rbt.move_c(
move_s(waypoints: list[list[float]], *, frame: Frame = 'WRF', duration: float | None = None, speed: float | None = None, accel: float = 1.0, wait: bool = False, **wait_kwargs: Any) -> int
async
¶
Cubic spline move through waypoints.
Category: Motion
Example
rbt.move_s(
move_p(waypoints: list[list[float]], *, frame: Frame = 'WRF', duration: float | None = None, speed: float | None = None, accel: float = 1.0, wait: bool = False, **wait_kwargs: Any) -> int
async
¶
Process move with auto-blending through waypoints.
Category: Motion
Example
rbt.move_p(
servo_j(angles: list[float], *, pose: list[float] | None = None, speed: float = 1.0, accel: float = 1.0) -> int
abstractmethod
async
¶
Streaming joint position target (fire-and-forget).
angles: joint angles in degrees (ignored if pose is set). If pose is given, dispatches to Cartesian target via IK.
Category: Streaming
Example
rbt.servo_j(
servo_l(pose: list[float], *, speed: float = 1.0, accel: float = 1.0) -> int
abstractmethod
async
¶
Streaming linear Cartesian position target (fire-and-forget).
pose: [x, y, z, rx, ry, rz] in mm and degrees.
Category: Streaming
Example
rbt.servo_l(
jog_j(joint: int, speed: float = 0.0, duration: float = 0.1, *, joints: list[int] | None = None, speeds: list[float] | None = None, accel: float = 1.0) -> int
abstractmethod
async
¶
Joint velocity jog. Single-joint or multi-joint.
Single joint: jog_j(0, 0.5, 1.0)
Multi joint: jog_j(joints=[0, 1], speeds=[0.5, -0.3], duration=1.0)
Category: Jog
Example
rbt.jog_j(
jog_l(frame: Frame, axis: Axis | None = None, speed: float = 0.0, duration: float = 0.1, *, axes: list[Axis] | None = None, speeds_list: list[float] | None = None, accel: float = 1.0) -> int
abstractmethod
async
¶
Cartesian velocity jog. Single-axis or multi-axis.
Single axis: jog_l("WRF", "X", 0.5, 1.0)
Multi axis: jog_l("WRF", axes=["X", "Y"], speeds_list=[0.5, -0.3])
Category: Jog
Example
rbt.jog_l("WRF", "X", speed=0.5, duration=1.0)
wait_motion(timeout: float = 10.0, **kwargs: Any) -> bool
abstractmethod
async
¶
Block until the robot has stopped moving or timeout expires.
Category: Synchronization
Example
rbt.wait_motion()
wait_command(command_index: int, timeout: float = 10.0) -> bool
abstractmethod
async
¶
Block until a specific command index has completed.
Category: Synchronization
Example
rbt.wait_command(
wait_status(predicate: Callable[[StatusBuffer], bool], timeout: float = 5.0) -> bool
async
¶
Block until predicate returns True for a status snapshot.
wait_checkpoint(label: str, timeout: float = 30.0) -> bool
async
¶
Block until a checkpoint with label is reached.
stop() -> int
abstractmethod
async
¶
Stop all motion — cancel the active move and clear the queue.
The controller stays enabled and holding position; the next motion command is accepted immediately.
Category: Control
Example
rbt.stop()
estop() -> int
abstractmethod
async
¶
Protective stop: stop all motion and latch the controller
disabled until reset().
Category: Control
Example
rbt.estop()
reset() -> int
abstractmethod
async
¶
Clear a latched protective stop, re-enabling motion.
Category: Control
Example
rbt.reset()
loop_stats() -> LoopStatsResult | None
async
¶
Control-loop runtime metrics; None when unreachable.
Category: Query
Example
stats = rbt.loop_stats()
set_status_rate(hz: float) -> int
async
¶
Set the rate the controller broadcasts status at.
Raising it costs bandwidth and consumer CPU continuously, so it is a knob for a debugging or tuning session rather than a permanent setting. A rate the controller cannot divide its control loop into is rejected rather than snapped to a neighbour — a silently different rate than the one asked for makes any capture taken at it wrong in a way nothing reports.
Category: Configuration
Example
rbt.set_status_rate(125)
status_rate() -> StatusRate | None
async
¶
Current broadcast rate and the control rate it divides; None
when unreachable.
Category: Query
Example
rate = rbt.status_rate()
simulator(enabled: bool) -> int
async
¶
Enable or disable simulator mode.
Category: Control
Example
rbt.simulator(True)
is_simulator() -> bool
async
¶
Query whether simulator mode is active.
Category: Query
Example
active = rbt.is_simulator()
teleport(angles_deg: list[float], tool_positions: list[float] | None = None) -> int
async
¶
Instantly set joint angles and optional tool positions (simulator only).
Category: Control
Example
rbt.teleport([0, -90, 0, 0, 0, 0]) rbt.teleport([0, -90, 0, 0, 0, 0], tool_positions=[1.0])
freedrive(enabled: bool) -> int
async
¶
Release the arm for hand guiding, or take it back under control.
How a backend delivers this is its own business — a gravity feedforward with no position term, a brake release, an impedance mode. Callers state the intent; the backend picks the mechanism, and refuses with its own reason when the arm is in no state to be pushed around (unreferenced joints, drives down, mid-move).
Category: Control
Example
rbt.freedrive(True)
is_freedrive() -> bool
async
¶
Whether the arm is back-driveable right now.
The question is about the arm, not the request: a backend that
accepted freedrive(True) but cannot honour it yet answers
False. Never report an arm safe to grab on the strength of a
command having been sent.
Category: Query
Example
if rbt.is_freedrive(): ...
set_shapes(shapes: list[Shape]) -> int
async
¶
Replace the program-layer keep-out / marker shapes (the collision world).
Collision-enabled shapes are added to the backend's collision checkers so motion is blocked against them; an empty list clears all program-layer shapes. Installation-layer shapes (declared in the backend's robot config) are unaffected — programs inherit them and cannot remove them.
A shape carrying physics (see waldoctl.Physical) also enters
the simulator's contact world — static when mass is None, a free
body otherwise. A visual-only marker (collision=False) cannot
declare physics; the backend rejects the whole list. Backends
without a physics simulator reject any shape declaring it rather than
silently ignoring the declaration.
The change also invalidates committed motion: the backend re-guards the currently-streaming trajectory's remaining path and every queued trajectory before it starts, halting with a collision error rather than driving into a keep-out declared after the motion was planned.
Returns 1 only after the backend confirms the world was applied;
0 if unconfirmed, < 0 if the backend rejected the shapes (see
the class docstring's return-code convention).
Category: Configuration
Example
rbt.set_shapes([Box(name="table", x=0.6, y=0.4, z=0.02, pose=(0.3, 0, -0.01, 0, 0, 0))])
shapes() -> ShapeWorld | None
async
¶
The collision world the backend is currently enforcing, by layer.
Readback truth: displays should render this — not a locally stored
copy — re-querying whenever StatusBuffer.scene_epoch changes.
Returns None if the backend is unreachable.
Category: Query
Example
world = rbt.shapes()
angles() -> list[float] | None
abstractmethod
async
¶
Current joint angles in degrees.
Category: Query
Example
angles = rbt.angles()
pose(frame: Frame = 'WRF') -> list[float] | None
abstractmethod
async
¶
Current TCP pose as [x, y, z, rx, ry, rz] in mm and degrees.
Category: Query
Example
pose = rbt.pose()
joint_speeds() -> list[float] | None
async
¶
Current joint velocities.
Category: Query
Example
speeds = rbt.joint_speeds()
io() -> list[int] | None
async
¶
Digital I/O state.
Category: Query
Example
io = rbt.io()
status() -> object | None
async
¶
Aggregate status snapshot.
Category: Query
Example
status = rbt.status()
queue() -> list[str] | None
async
¶
Queued command list.
Category: Query
Example
queue = rbt.queue()
tools() -> ToolResult | None
async
¶
Current tool and available tools.
Category: Query
Example
tools = rbt.tools()
activity() -> ActivityResult | None
async
¶
What the robot is currently doing.
Returns state (idle/executing/error), current command name, parameters, and error description if applicable.
Category: Query
Example
act = rbt.activity()
reachable() -> object | None
async
¶
Remaining freedom of movement per joint/axis before hitting limits.
Category: Query
Example
en = rbt.reachable()
error() -> object | None
async
¶
Current error state, or None if no error.
Category: Query
Example
err = rbt.error()
profile() -> str | None
async
¶
Current motion profile name.
Category: Query
Example
profile = rbt.profile()
tcp_speed() -> float | None
async
¶
TCP linear velocity in mm/s.
Category: Query
Example
speed = rbt.tcp_speed()
connect_hardware(port_str: str) -> int
async
¶
Connect to robot hardware via serial port.
Category: Configuration
Example
rbt.connect_hardware("/dev/ttyUSB0")
select_profile(profile: str) -> int
async
¶
Set the motion profile (e.g. "TOPPRA").
Category: Configuration
Example
rbt.select_profile("TOPPRA")
select_tool(tool_name: str, variant_key: str = '') -> int
async
¶
Set the active end-effector tool on the controller.
Category: Configuration
Example
rbt.select_tool("PNEUMATIC")
set_tcp_offset(x: float = 0, y: float = 0, z: float = 0) -> int
async
¶
Set TCP offset in mm, composed on top of the current tool transform.
The offset shifts the effective TCP point in the tool's local frame. Subsequent motion (especially TRF relative moves) will use the new TCP. Call with (0, 0, 0) to reset. Changing tools resets the offset.
Category: Configuration
Example
rbt.set_tcp_offset(0, 0, -190)
tcp_offset() -> list[float]
async
¶
Query current TCP offset in mm [x, y, z].
Returns exactly three values, and raises when the controller
does not answer. An implementation must not return a zero vector for
an unreachable controller: [0, 0, 0] is a legitimate offset — a
tool deliberately cleared — so a caller that receives it as a
not-answered sentinel cannot tell "the offset is zero" from "there
is no controller", and a host that adopts the readback will quietly
erase the offset the user set.
Category: Configuration
Example
offset = rbt.tcp_offset()
set_payload(mass: float, com: tuple[float, float, float] = (0.0, 0.0, 0.0), inertia: Inertia6 | None = None) -> int
async
¶
Declare what the arm is carrying at the TCP.
An inertial declaration only: the gravity feedforward and torque
planning carry it, the collision geometry does not change (use
set_shapes for that).
This is what a backend's own model cannot know. A shipped model describes the nominal arm; the mass in the gripper, the fixture bolted to the flange and the spool on the end of it are the operator's, and they move the first moments the gravity model depends on.
mass in kg, 0 clears the payload. com is the centre of mass in
end-effector-frame metres. inertia is an :data:Inertia6;
omitted means a point mass.
Invalid input — a negative mass, an inertia that is not positive
semidefinite — raises RuntimeError (a backend's own error type
derives from it) rather than returning -1.
Category: Configuration
Example
rbt.set_payload(1.2, com=(0.0, 0.0, 0.05))
estimate_payload(spread: float = 0.5, ridge: float = 0.01, declare: bool = True) -> PayloadEstimate
async
¶
Estimate what the arm is carrying, and declare it.
Mass and centre of mass, from the torque the arm holds. NOT the
inertia tensor: static poses cannot excite it, so the result is
carried as a point mass — which is what most payloads are well
enough described by. A payload whose inertia matters is declared
with set_payload from its drawing.
Call it after closing on a part whose mass is not known. The arm moves: the backend swings the wrist — where the load's lever arm is, so nothing below moves and the pick is not disturbed — through a few poses, taking seconds.
The backend clears the declared payload before measuring (the load is found in the torque an unloaded model cannot explain) and restores it on every exit that does not declare, failure included. With declare (the default) the estimate replaces it, so the gravity model carries the part from the next tick.
spread is how far each wrist joint swings either way, in radians. ridge holds back parameters the motion did not measure.
Raises RuntimeError (a backend's own error type derives from
it) when there is no room to measure, or when declare is set
and no mass was actually measured — a backend must refuse rather
than declare noise.
Category: Motion
Example
found = rbt.estimate_payload() print(f"holding {found.mass:.3f} kg")
payload() -> PayloadResult | None
async
¶
The payload the runtime is currently carrying.
Returns None if the backend is unreachable. A backend that
carries no payload reports zeros rather than None.
Category: Query
Example
print(rbt.payload())
write_io(index: int, value: int) -> int
async
¶
Set digital output by logical index (0 = first output pin).
Category: I/O
Example
rbt.write_io(0, 1) # Set first output HIGH
tool_action(tool_key: str, action: str, params: list[Any] | None = None, *, wait: bool = False, timeout: float = 10.0) -> int
async
¶
Invoke a tool-specific action by key.
tool_key: identifier of the attached tool (e.g. "ELECTRIC").
action: action name understood by the tool (e.g. "calibrate", "move").
params: optional positional parameters for the action.
Category: I/O
Example
rbt.tool_action("ELECTRIC", "calibrate")
reset_state() -> int
async
¶
Reset controller state (world shapes, tool selection, errors).
Category: Control
Example
rbt.reset_state()
checkpoint(label: str) -> int
async
¶
Insert a checkpoint marker in the command queue.
Category: Synchronization
Example
rbt.checkpoint("pick_done")
delay(seconds: float) -> int
async
¶
Insert a non-blocking delay in the command queue.
Category: Synchronization
Example
rbt.delay(1.0)
DryRunClient¶
waldoctl.DryRunClient
¶
Bases: Protocol
Offline motion client for path preview / dry-run simulation.
Concrete implementations run the real command pipeline against a
simulated controller state without hardware. Each motion method
returns a DryRunResult containing the TCP trajectory and final
joint state.
Required methods: home(), move_j(), move_l(),
angles(), pose(), flush().
Joint Configuration¶
waldoctl.JointsSpec(count: int, names: tuple[str, ...], limits: JointLimits, home: HomePosition)
dataclass
¶
Complete joint configuration for a robot.
All array properties have their first dimension equal to count.
waldoctl.JointLimits(position: PositionLimits, hard: KinodynamicLimits, jog: KinodynamicLimits)
dataclass
¶
All joint limits — position and kinodynamic.
waldoctl.PositionLimits(deg: NDArray[np.float64], rad: NDArray[np.float64])
dataclass
¶
waldoctl.KinodynamicLimits(velocity: NDArray[np.float64], acceleration: NDArray[np.float64], jerk: NDArray[np.float64] | None = None)
dataclass
¶
Per-joint velocity, acceleration, and jerk limits.
All arrays have shape (num_joints,) in SI units (rad/s family).
velocity: NDArray[np.float64]
instance-attribute
¶
(N,) — max joint velocities in rad/s.
acceleration: NDArray[np.float64]
instance-attribute
¶
(N,) — max joint accelerations in rad/s².
jerk: NDArray[np.float64] | None = None
class-attribute
instance-attribute
¶
(N,) — max joint jerks in rad/s³, or None.
waldoctl.HomePosition(deg: NDArray[np.float64], rad: NDArray[np.float64])
dataclass
¶
waldoctl.CartesianKinodynamicLimits(velocity: LinearAngularLimits, acceleration: LinearAngularLimits)
dataclass
¶
waldoctl.LinearAngularLimits(linear: float, angular: float)
dataclass
¶
Results¶
waldoctl.IKResult
¶
waldoctl.DryRunResult
¶
Bases: Protocol
Result from a dry-run motion command (path preview).
tcp_poses: NDArray[np.float64]
instance-attribute
¶
(N, 6) — TCP trajectory [x, y, z, rx, ry, rz] in meters + radians.
end_joints_rad: NDArray[np.float64]
instance-attribute
¶
(num_joints,) — final joint angles in radians.
duration: float
instance-attribute
¶
Trajectory duration in seconds.
error: object | None
instance-attribute
¶
Structured error (e.g. RobotError), or None on success.
valid: NDArray[np.bool_] | None
instance-attribute
¶
(N,) per-pose IK validity; None means all poses are valid.
joint_trajectory_rad: NDArray[np.float64] | None
instance-attribute
¶
(N, num_joints) — full joint trajectory in radians, aligned with tcp_poses rows. None if unavailable.
waldoctl.IKResultData(q: NDArray[np.float64], success: bool, violations: str | None = None)
dataclass
¶
Concrete IKResult for use in tests and adapters.
waldoctl.DryRunResultData(tcp_poses: NDArray[np.float64], end_joints_rad: NDArray[np.float64], duration: float, error: object | None = None, valid: NDArray[np.bool_] | None = None, joint_trajectory_rad: NDArray[np.float64] | None = None, object_tracks: tuple[ObjectTrack, ...] | None = None)
dataclass
¶
Concrete DryRunResult for use in tests and adapters.
Status¶
waldoctl.StatusBuffer
¶
Bases: Protocol
Status snapshot yielded by status_stream_shared().
Each field is a numpy array for zero-copy access in the hot path.
pose: np.ndarray
instance-attribute
¶
(16,) float64 — flattened 4x4 homogeneous transform.
angles: np.ndarray
instance-attribute
¶
(N,) float64 — joint angles in degrees.
speeds: np.ndarray
instance-attribute
¶
(N,) float64 — joint velocities in rad/s.
io: np.ndarray
instance-attribute
¶
(5,) int32 — [in1, in2, out1, out2, estop].
tool_status: ToolStatus
instance-attribute
¶
Universal EOAT status (key, state, positions, etc.).
joint_en: np.ndarray
instance-attribute
¶
(12,) int32 — joint enable envelope.
cart_en: dict[str, np.ndarray]
instance-attribute
¶
Frame name -> (12,) int32 Cartesian enable envelope.
action_current: str
instance-attribute
¶
Currently executing action name.
action_params: str
instance-attribute
¶
Brief serialization of current action parameters.
action_state: ActionState
instance-attribute
¶
State of the current action.
executing_index: int
instance-attribute
¶
Index of the command currently being executed (-1 if idle).
completed_index: int
instance-attribute
¶
Index of the last completed command (-1 if none).
last_checkpoint: str
instance-attribute
¶
Label of the last checkpoint reached (empty if none).
tcp_speed: float
instance-attribute
¶
TCP linear velocity in mm/s.
simulator_active: bool
instance-attribute
¶
Whether the controller is in simulator mode.
collision_active: bool
instance-attribute
¶
Whether a motion was blocked/stopped by a predicted collision.
collision_pairs: list[tuple[str, str]]
instance-attribute
¶
Colliding pairs at the predicted colliding config. Names are URDF link
names, shape:<name> (program keep-out), install:<name>
(installation keep-out), or tool:<key>:<part> (attached tool geometry)
— never backend-internal geometry identifiers.
scene_epoch: int
instance-attribute
¶
Monotonic counter bumped on every collision-world change; displays
re-query RobotClient.shapes() when it moves.
homed: bool
instance-attribute
¶
All joints homed. Until homing, reported joint positions are unreferenced and backends refuse planned motion; frontends seed dry-run previews with this so previews mirror that gate.
torques: np.ndarray
instance-attribute
¶
(N,) float64 — measured joint torques [Nm].
torques_ext: np.ndarray
instance-attribute
¶
(N,) float64 — external joint torque estimate [Nm]: measured torque minus the backend's dynamics model. A hand pushing the arm, a payload the model does not know.
enabled: bool
instance-attribute
¶
Whether the controller accepts motion.
warnings: list[tuple]
instance-attribute
¶
Self-clearing warning-class conditions as structured-error 6-tuples
(command_index, code, title, cause, effect, remedy) — stale data,
degraded loop, failed homing. Hard latches are NOT here; they surface
through the error query/standing error.
link_health: dict
instance-attribute
¶
Motor-bus link health: state (backend enum/str), restarts,
tx_errors, rx_frames. Empty when the backend has no bus.
drive_health: dict
instance-attribute
¶
Per-drive readings and faults, for watching a joint approach a limit
and for seeing which drive tripped: temperatures_c and
currents_ma (one entry per actuator, arm joints first),
bus_voltage_v (the lowest supply a drive reports, where sag under
load shows first), and faults — a sequence of active fault labels
per drive in the backend's own vocabulary. A list is empty when the
backend reports nothing of that kind at all; NaN inside a reading
means that drive has not answered yet, and an empty label sequence (a
list on one backend, a tuple on another) means a healthy drive. A
backend may report faults without analog registers or the reverse, so
test the member you need rather than assuming they arrive together.
loop_health: dict
instance-attribute
¶
Control-loop health as the loop runs: p99_period_s (the tail is
what breaks a control loop, not the mean) and overruns (ticks that
missed their deadline, cumulative). The rest of the loop metrics —
mean/min/max, scheduling policy, bus frame age — are boot constants or
detail, and stay in the loop_stats() query. Empty when the backend
does not measure its loop.
homing: dict
instance-attribute
¶
Homing progress: active, sequence_step, and per-actuator
joints — (state, phase) pairs. Empty when idle and unsupported.
freedrive: bool
property
¶
Whether the arm is back-driveable right now — hand guiding is actually in effect, not merely requested. A read-only property so backends may derive it from their own state rather than store it.
mode: IntEnum
property
¶
Controller mode. Backend-specific enum — a read-only property
so a backend's own enum subclass satisfies the Protocol (a plain
attribute would be invariant); .name is the display string
(BOOTING, IDLE, JOG, ...).
waldoctl.PingResult(hardware_connected: bool)
dataclass
¶
Result of a connectivity check.
hardware_connected: bool
instance-attribute
¶
Whether the controller has a live link to robot hardware (serial, socket, CAN, PLC, etc.).
waldoctl.ToolResult(tool: str, available: list[str])
dataclass
¶
waldoctl.ActionState
¶
Bases: IntEnum
State of the currently executing action on the controller.
Tools¶
waldoctl.ToolSpec(*, key: str, display_name: str, tool_type: str | ToolType, tcp_origin: tuple[float, float, float], tcp_rpy: tuple[float, float, float], description: str = '', meshes: tuple[MeshSpec, ...] = (), motions: tuple[PartMotion, ...] = (), variants: tuple[ToolVariant, ...] = (), activation_type: ActivationType = ActivationType.PROGRESSIVE, action_l_labels: tuple[str, str] | None = None, action_l_icons: tuple[str, str] | None = None, action_l_mode: ToggleMode = ToggleMode.TOGGLE, adjust_step: int | None = None, adjust_labels: tuple[str, str] | None = None, adjust_icons: tuple[str, str] | None = None, action_r_labels: tuple[str, str] | None = None, action_r_icons: tuple[str, str] | None = None, action_r_mode: ToggleMode = ToggleMode.TRIGGER, camera_spec: CameraSpec | None = None)
¶
Bases: ABC
Base contract every tool must satisfy.
key is unique per tool instance (e.g. "pneumatic_left").
tool_type determines which GUI panel category the tool belongs to.
Immutable spec fields are stored privately and exposed via read-only
properties. :attr:runtime_settings is the mutable, bindable layer for
user overrides (currently camera device; tools can extend it).
key: str
property
¶
Unique instance identifier.
display_name: str
property
¶
Human-readable name for UI display.
tool_type: str
property
¶
GUI category — determines which panel (if any) is shown.
Returns a str so third-party tools can introduce new categories
beyond the built-in :class:ToolType values. Comparison with
ToolType.GRIPPER etc. still works because ToolType is a
StrEnum.
tcp_origin: tuple[float, float, float]
property
¶
(x, y, z) translation from flange to TCP in meters.
tcp_rpy: tuple[float, float, float]
property
¶
(roll, pitch, yaw) orientation from flange to TCP in radians.
activation_type: ActivationType
property
¶
How the tool is activated — binary (on/off) or progressive (continuous).
description: str
property
¶
Short description of the tool.
meshes: tuple[MeshSpec, ...]
property
¶
Mesh descriptors for 3D visualization.
motions: tuple[PartMotion, ...]
property
¶
Physical motion descriptors for movable tool parts.
variants: tuple[ToolVariant, ...]
property
¶
Named mesh/motion variants (e.g. different jaw sets).
action_l_labels: tuple[str, str] | None
property
¶
(off_label, on_label) tooltip text for the left action button.
action_l_icons: tuple[str, str] | None
property
¶
(off_icon, on_icon) Material Icon names for the left action button.
action_l_mode: ToggleMode
property
¶
How the left action button behaves — stateful on/off or one-shot trigger.
adjust_step: int | None
property
¶
Step size for the +/- adjust buttons, or None if not supported.
adjust_labels: tuple[str, str] | None
property
¶
(decrease_label, increase_label) tooltip text for adjust buttons.
adjust_icons: tuple[str, str] | None
property
¶
(decrease_icon, increase_icon) Material Icon names for adjust buttons.
action_r_labels: tuple[str, str] | None
property
¶
(off_label, on_label) tooltip text for the right action button.
action_r_icons: tuple[str, str] | None
property
¶
(off_icon, on_icon) Material Icon names for the right action button.
action_r_mode: ToggleMode
property
¶
How the right action button behaves — stateful on/off or one-shot trigger.
channel_descriptors: tuple[ChannelDescriptor, ...]
property
¶
Descriptors for tool-specific process data channels.
camera_spec: CameraSpec | None
property
¶
Spec-time default camera attached to this tool, if any.
Returns None when the tool has no camera. The user can still
override via :attr:runtime_settings; consumers should resolve the
effective device via :attr:effective_camera_device.
runtime_settings: 'ToolRuntimeSettings'
property
¶
User-tweakable runtime overrides for this tool.
Bindable. The host application persists these per tool key to
:attr:nicegui.app.storage.general so user choices survive restarts.
effective_camera_device: int | str | None
property
¶
Resolved camera device after applying any runtime override.
Resolution order: runtime_settings.camera_device if set, else
camera_spec.device if a camera_spec exists, else None.
Two values mean "no camera": None (no spec or override supplies a
device) and -1 (a spec is present but set to CameraSpec.device's
no-camera sentinel). Callers must treat both as absent.
action_l(engaged: bool) -> None
async
¶
Left action button handler.
Override in subclasses to define tool-specific behavior.
action_r(engaged: bool) -> None
async
¶
Right action button handler.
Override in subclasses to define tool-specific behavior.
status() -> ToolStatus
async
¶
Query current tool status from the controller.
Returns the live tool status (state, engaged, positions, channels).
The base ToolSpec raises NotImplementedError; client-bound
tool subclasses override it against the controller.
waldoctl.ToolsSpec
¶
Bases: ABC
Collection of available tools for a robot.
Supports membership testing by ToolType (category) or str (key).
available: tuple[ToolSpec, ...]
abstractmethod
property
¶
All available tool specifications, ordered for display.
default: ToolSpec
abstractmethod
property
¶
Default tool (typically bare flange / "NONE").
__getitem__(key: str) -> ToolSpec
abstractmethod
¶
Look up a tool by its key. Raises KeyError if not found.
__contains__(item: object) -> bool
abstractmethod
¶
Test membership by ToolType (any tool of that category?)
or str (specific key exists?).
by_type(tool_type: str | ToolType) -> tuple[ToolSpec, ...]
abstractmethod
¶
Return all tools matching the given category.
Accepts a plain str so third-party tool categories work without
extending the built-in :class:ToolType enum.
waldoctl.GripperTool(**kwargs: Any)
¶
Bases: ToolSpec
Base for all grippers.
All grippers support set_position() as the universal control method.
Position is normalized: 0.0 = fully open, 1.0 = fully closed.
Action methods are abstract — backends provide concrete implementations.
gripper_type: GripperType
abstractmethod
property
¶
Gripper sub-type.
set_position(position: float, **kwargs: float | int) -> int
abstractmethod
async
¶
Set gripper position. 0.0 = fully open, 1.0 = fully closed.
Category: Tool
Example
rbt.tool.set_position(0.5)
calibrate(**kwargs: object) -> int
async
¶
Calibrate the gripper. Not all grippers support this.
Category: Tool
Example
rbt.tool.calibrate()
is_open(position: float) -> bool
¶
Infer open/closed from normalized position. True = open.
action_l(engaged: bool) -> None
async
¶
Left action: open if engaged, close if not.
open(**kwargs: float | int) -> int
abstractmethod
async
¶
Open the gripper.
Category: Tool
Example
rbt.tool.open()
close(**kwargs: float | int) -> int
abstractmethod
async
¶
Close the gripper.
Category: Tool
Example
rbt.tool.close()
waldoctl.PneumaticGripperTool(*, io_port: int, **kwargs: Any)
¶
Bases: GripperTool
Pneumatic gripper — binary open/close.
Action methods are abstract — backends provide concrete implementations.
io_port: int
property
¶
Digital I/O port number for open/close control.
waldoctl.ElectricGripperTool(*, position_range: tuple[float, float], speed_range: tuple[float, float], current_range: tuple[int, int], **kwargs: Any)
¶
Bases: GripperTool
Electric gripper — continuous position with speed and current control.
Action methods and computed properties (adjust_step,
channel_descriptors) are abstract — backends provide concrete
implementations.
position_range: tuple[float, float]
property
¶
(min, max) position range (normalized 0..1).
speed_range: tuple[float, float]
property
¶
(min, max) speed range (normalized 0..1).
current_range: tuple[int, int]
property
¶
(min, max) current range in mA.
stop(**kwargs: object) -> int
async
¶
Halt the jaws where they are, ahead of anything still queued. Not all grippers support this.
Category: Tool
Example
rbt.tool.stop()
release(**kwargs: object) -> int
async
¶
Drop the grip once the action in flight settles, freeing the jaws for manual handling. Not all grippers support this.
Category: Tool
Example
rbt.tool.release()
waldoctl.ToolType
¶
Bases: StrEnum
Tool categories the web commander has GUI support for.
StrEnum so third-party tools can pass arbitrary category strings
via waldoctl.tools entry points while ToolType.GRIPPER == "gripper"
keeps existing equality checks working.
waldoctl.GripperType
¶
Bases: Enum
Gripper sub-types — each gets different UI controls.
waldoctl.ActivationType
¶
Bases: Enum
How a tool is activated / controlled.
On/off only — no intermediate position feedback from hardware.
Tools with motion descriptors need estimated_speed fields
so the simulator can animate transitions.
PROGRESSIVE: Continuous position control with real-time position feedback.
waldoctl.ToggleMode
¶
waldoctl.ToolState
¶
Bases: IntEnum
State of an end-of-arm tool.
waldoctl.ToolStatus
¶
Universal end-of-arm tool status — the bindable surface exposed at
commander.status.tool.
Populated by the host application's status loop at the controller's
broadcast rate. Consumers combine positions[i] with
ToolSpec.motions[i] to reconstruct the physical state of each DOF
without knowing the tool type. Tool-specific process data is in
channels, described by the tool's channel_descriptors.
Decorated with @bindable_dataclass so UI elements can bind to leaf
fields directly: bind_text_from(commander.status.tool, "key"),
bind_value_from(commander.status.tool, "engaged"), etc. Field
reassignment by the status loop fires bindings synchronously.
Mutate-in-place invariant: this is a sub-object of RobotStatus —
its fields are written individually by the status loop. The instance
itself is never swapped.
key: str = 'NONE'
class-attribute
instance-attribute
¶
Attached tool key.
variant_key: str = ''
class-attribute
instance-attribute
¶
Active variant within the attached tool (empty if the tool has no variants).
state: ToolState = ToolState.OFF
class-attribute
instance-attribute
¶
Tool operational state.
engaged: bool = False
class-attribute
instance-attribute
¶
Actively doing work (welding, gripping, dispensing).
part_detected: bool = False
class-attribute
instance-attribute
¶
EOAT part/object presence confirmed.
fault_code: int = 0
class-attribute
instance-attribute
¶
0=no fault, nonzero=tool-specific error.
positions: tuple[float, ...] = ()
class-attribute
instance-attribute
¶
DOF positions 0..1, one per PartMotion.
channels: tuple[float, ...] = ()
class-attribute
instance-attribute
¶
Tool-specific process data, described by ChannelDescriptor.
position: float
property
¶
Primary DOF position (positions[0] if any, else 0.0).
Convenience accessor for the common single-DOF case (gripper open/
close, etc.). Not bindable — bind to positions and use a backward
function if reactive display of the primary value is needed.
current: float
property
¶
Primary process-channel value (channels[0] if any, else 0.0).
Convenience accessor for the common case (e.g. gripper motor current).
Not bindable — bind to channels with a backward function for
reactive display.
waldoctl.ToolVariant(key: str, display_name: str, meshes: tuple[MeshSpec, ...] = (), motions: tuple[PartMotion, ...] = (), tcp_origin: tuple[float, float, float] | None = None, tcp_rpy: tuple[float, float, float] | None = None)
dataclass
¶
Named variant that replaces a tool's meshes and motions.
Each variant is self-contained — it provides a complete set of meshes and motions, so the scene swaps them wholesale without merge logic.
key: str
instance-attribute
¶
Unique identifier within the tool (e.g. "finger", "pinch").
display_name: str
instance-attribute
¶
Human-readable name for the UI dropdown.
meshes: tuple[MeshSpec, ...] = ()
class-attribute
instance-attribute
¶
Complete mesh set for this variant.
motions: tuple[PartMotion, ...] = ()
class-attribute
instance-attribute
¶
Complete motion descriptors for this variant.
tcp_origin: tuple[float, float, float] | None = None
class-attribute
instance-attribute
¶
(x, y, z) TCP translation in meters, or None to use tool default.
tcp_rpy: tuple[float, float, float] | None = None
class-attribute
instance-attribute
¶
(roll, pitch, yaw) TCP orientation in radians, or None to use tool default.
waldoctl.MeshSpec(file: str, origin: tuple[float, float, float] = (0.0, 0.0, 0.0), rpy: tuple[float, float, float] = (0.0, 0.0, 0.0), role: MeshRole = MeshRole.BODY)
dataclass
¶
Immutable descriptor for a single STL mesh in a tool assembly.
file: str
instance-attribute
¶
Filename of the STL mesh.
origin: tuple[float, float, float] = (0.0, 0.0, 0.0)
class-attribute
instance-attribute
¶
(x, y, z) offset in meters.
rpy: tuple[float, float, float] = (0.0, 0.0, 0.0)
class-attribute
instance-attribute
¶
(roll, pitch, yaw) orientation in radians.
role: MeshRole = MeshRole.BODY
class-attribute
instance-attribute
¶
Which mesh group this belongs to.
waldoctl.MeshRole
¶
Bases: Enum
Well-defined roles for tool mesh groups.
waldoctl.LinearMotion(role: MeshRole, axis: tuple[float, float, float], travel_m: float, symmetric: bool = True, estimated_speed_m_s: float | None = None, estimated_accel_m_s2: float | None = None)
dataclass
¶
Linear motion of tool parts (gripper jaws, press-fit rams).
role: MeshRole
instance-attribute
¶
Which mesh group moves.
axis: tuple[float, float, float]
instance-attribute
¶
Unit vector along which the motion occurs.
travel_m: float
instance-attribute
¶
Max displacement per side in meters.
symmetric: bool = True
class-attribute
instance-attribute
¶
If True, paired parts (left/right) move in opposite directions.
estimated_speed_m_s: float | None = None
class-attribute
instance-attribute
¶
Estimated travel speed in m/s (for binary-activation tools without position feedback).
estimated_accel_m_s2: float | None = None
class-attribute
instance-attribute
¶
Estimated acceleration in m/s² (for binary-activation tools).
waldoctl.RotaryMotion(role: MeshRole, axis: tuple[float, float, float], travel_rad: float, symmetric: bool = True, estimated_speed_rad_s: float | None = None, estimated_accel_rad_s2: float | None = None)
dataclass
¶
Rotary motion of tool parts (spindle bits, drill chucks).
role: MeshRole
instance-attribute
¶
Which mesh group moves.
axis: tuple[float, float, float]
instance-attribute
¶
Unit vector for the rotation axis.
travel_rad: float
instance-attribute
¶
Max rotation in radians.
symmetric: bool = True
class-attribute
instance-attribute
¶
If True, paired parts rotate in opposite directions.
estimated_speed_rad_s: float | None = None
class-attribute
instance-attribute
¶
Estimated angular speed in rad/s (for binary-activation tools).
estimated_accel_rad_s2: float | None = None
class-attribute
instance-attribute
¶
Estimated angular acceleration in rad/s² (for binary-activation tools).
waldoctl.ChannelDescriptor(name: str, unit: str, min: float = 0.0, max: float = 0.0)
dataclass
¶
Describes one process data channel reported by a tool.
The controller populates ToolStatus.channels positionally — index i
in the channels tuple corresponds to channel_descriptors[i].
name: str
instance-attribute
¶
Human-readable name (e.g. "Force", "Current").
unit: str
instance-attribute
¶
SI unit symbol (e.g. "N", "mA", "bar").
min: float = 0.0
class-attribute
instance-attribute
¶
Minimum expected value (0 = auto-scale).
max: float = 0.0
class-attribute
instance-attribute
¶
Maximum expected value (0 = auto-scale).