Motion and Control
Motion spaces
MotionSpace<R> maps a typed space to its Target:
| Space | Target |
|---|---|
JointSpace<N> |
[f64; N], with R: Joints<N> |
EndSpace |
Pose, with R: EndPoint |
FlangeSpace, TcpSpace |
Pose |
Relative<S>, Inertial<S> |
S::Target |
Drivers implement MoveTo<S> and MoveTraj<S>. Applications use the blanket Motion facade and choose the space with turbofish syntax.
move_to blocks until completion or failure. move_to_async returns an inert Future and defaults to an unsupported error; a driver must provide a real asynchronous backend to support it. It is a different contract from an async control callback below.
MoveTraj<S> requires move_traj, move_path and move_waypoints. Drivers can funnel planning into dense trajectories, or explicitly return UnprocessableInstructionError for unsupported path/waypoint operations. Inspect simulator-specific queue semantics separately.
Control spaces
ControlSpace<R> fixes Obs and Command. Equal payload types do not make two channels equivalent.
| Channel | Obs | Command |
|---|---|---|
TorqueControl<N> |
JointState<N> |
[f64; N] |
JointPositionControl<N> |
JointState<N> |
[f64; N] |
JointVelocityControl<N> |
JointState<N> |
[f64; N] |
ArmTorqueControl<N> |
ArmState<N> |
[f64; N] |
CartesianPoseControl<N> |
ArmState<N> |
Pose |
CartesianVelocityControl<N> |
ArmState<N> |
[f64; 6] |
BaseVelocityControl |
BaseState |
[f64; 6] |
A callback can produce a command or end the session
Drivers implement ControlWith<S>::control_with_flow. These are the relevant signatures, with the surrounding trait bounds omitted:
pub type ControlStep<Command> = std::ops::ControlFlow<(), (Command, bool)>;
fn control_with_flow<F>(&mut self, closure: F) -> RobotResult<()>
where
F: FnMut(S::Obs, Duration) -> ControlStep<S::Command>;
| Callback result | Meaning |
|---|---|
ControlFlow::Continue((command, false)) |
Send this computed command and continue. |
ControlFlow::Continue((command, true)) |
Send this final computed command, then finish normally. |
ControlFlow::Break(()) |
No algorithm command was produced for this cycle; enter the device's session termination protocol. |
Break(()) is not an emergency stop and does not mean that no protocol packet can be sent. The device determines its required termination handshake or continuity packet. Do not fabricate a zero command to satisfy the callback return type. After either ending path the driver must not invoke the callback again.
The existing control_with interface remains available. Its callback returns (command, done); the default wrapper maps it to Continue((command, done)). Existing controller helpers can continue to use that interface. The callback is scoped to the session, without a blanket Send + 'static requirement, and must not be stored after the call returns.
hold_command(obs) is a continuity fallback based on the current channel's observation. It is not a universal safe command or a substitute for vendor safety mechanisms.
Async callback, blocking session
control_with_flow_async accepts async FnMut(...) -> ControlStep<Command>. control_with_async accepts async FnMut(...) -> (Command, bool). Both methods return RobotResult<()>, not a Future; the whole session still blocks. The default adapter runs each callback Future to completion with futures::executor::block_on.
An async callback therefore does not establish a Tokio reactor or guarantee that timers, sockets, or sibling futures on the calling thread will progress. Check the driver's execution model before using runtime-dependent operations. A truly asynchronous device session is a separate design task.
For the optional graph adapter, see Roplat integration.