Pause and Continue
Pausing the robot’s movement can be useful to react to external events during program execution. When a pause command is sent, the robot decelerates along its current path until it comes to a complete stop. While paused, new instructions can still be registered, but they will only be executed once the pause signal is revoked.
Pausing the Robot’s Movement
To pause the robot, call pause_motion() from the PauseContinue trait. It blocks until the robot has come to a
stop, bounded by the robot’s command timeout, and returns
RobotArmError::DidNotStop if the robot fails to settle in time. It can be called either at standstill or while the
robot is moving, as long as the robot is enabled and not stopping. In any other state, for example disabled or in
error, the robot control rejects the pause and pause_motion() returns RobotArmError::CannotPause without pausing
anything.
pause_motion() is thread safe and may be called from another thread than the one issuing instructions, but not from
another client:
robot.move_ptp(poses::home()).start()?.join()?;
info!("Home reached.");
// Start moving to zero (non-blocking).
robot.move_ptp(JointPose::default()).start()?;
// Do some work while the robot is moving.
do_some_work();
// Pause the robot: it decelerates and stops on its planned path.
robot.pause_motion()?;
info!("Robot paused.");
Note that the motion to the zero pose is started without join(), which allows a pause during the movement.
do_some_work() is a placeholder for whatever the application does in the meantime:
/// Simulate some work being done while the robot is in motion or paused.
fn do_some_work() {
sleep(Duration::from_millis(100));
}
After pause_motion(), the execution of new motion instructions, in this case a motion to the vertical pose, is
blocked. They are still added to the instruction queue:
let vertical = robot.move_ptp(poses::vertical()).start()?;
info!("Queued move to VERTICAL while paused.");
// Do more work while paused.
do_some_work();
// Continue: the robot resumes from where it stopped.
robot.continue_motion()?;
info!("Robot resumed.");
// Wait for the queued instruction to complete.
vertical.join()?;
info!("VERTICAL reached after continue.");
Only after continue_motion() is called does the robot continue the execution of all queued motion instructions:
it first finishes the motion to the zero pose and then moves to the vertical pose.
A pause has no effect while the robot is stopped; see Stop for the difference. A pause ends a continuous jog instead of holding it; see Jogging.
Disabling the robot or a robot error discards the pause on the robot control. The library still holds new
instructions until continue_motion() is called, so a robot enabled again does not start moving on its own.
Reading the Pause State
Note
Reading the pause state requires voraus.core 2.29.0 or later. On older cores, pause_state() returns
RobotArmError::InvalidValue with ControlValueError::PauseStateUnsupported.
A robot reports its pause state through pause_state() from the GetPauseState
trait. It returns a PauseState:
State |
Meaning |
|---|---|
|
No pause is instructed. |
|
A pause is instructed and the robot arm still decelerates. |
|
A pause is instructed and the robot arm stands still. |
The call does not block; it reads the pause and standstill flags of the robot control. continue_motion(), a stop,
a disable or a robot error return the state to NotPaused. After pause_motion() has returned, the robot has
settled:
// Read the pause state: the robot has settled, so it is paused.
let pause_state = robot.pause_state()?;
info!("The pause state in standstill is {pause_state:?}.");
If you run the code, the log output should contain the following line:
The pause state in standstill is Paused.
Pause vs. Stop
Pause |
Stop |
|
|---|---|---|
Pending instructions |
Kept and resumed |
Aborted |
New instructions |
Queued |
Rejected |
Resume command |
|
|
Use pause to halt motion temporarily and resume later. Use stop to abort everything that is pending.
Full Example
Pause and continue example
//! Pause and continue motion example.
//!
//! Demonstrates how to pause the robot during motion, queue new
//! instructions while paused, and then continue execution.
mod common;
use std::thread::sleep;
use std::time::Duration;
use common::poses;
use tracing::info;
use voraus_robot_arm::prelude::*;
/// Simulate some work being done while the robot is in motion or paused.
fn do_some_work() {
sleep(Duration::from_millis(100));
}
/// Example showing how to pause and continue the robot's motion.
///
/// This function accepts any robot that implements `MovePtp`,
/// `PauseContinue` and `GetPauseState`.
fn run_pause_continue(robot: &(impl MovePtp + PauseContinue + GetPauseState)) -> Result<()> {
info!("--- Pause and continue ---");
// Move to home and wait for completion.
robot.move_ptp(poses::home()).start()?.join()?;
info!("Home reached.");
// Start moving to zero (non-blocking).
robot.move_ptp(JointPose::default()).start()?;
// Do some work while the robot is moving.
do_some_work();
// Pause the robot: it decelerates and stops on its planned path.
robot.pause_motion()?;
info!("Robot paused.");
// Read the pause state: the robot has settled, so it is paused.
let pause_state = robot.pause_state()?;
info!("The pause state in standstill is {pause_state:?}.");
// While paused, we can queue additional instructions.
let vertical = robot.move_ptp(poses::vertical()).start()?;
info!("Queued move to VERTICAL while paused.");
// Do more work while paused.
do_some_work();
// Continue: the robot resumes from where it stopped.
robot.continue_motion()?;
info!("Robot resumed.");
// Wait for the queued instruction to complete.
vertical.join()?;
info!("VERTICAL reached after continue.");
Ok(())
}
fn main() -> voraus_robot_arm::error::Result<()> {
common::init_logging();
let robot = common::create_connected_robot()?;
run_pause_continue(&robot)?;
info!("Done.");
Ok(())
}
Trait Definitions
PauseContinue
pub trait PauseContinue {
/// Pause robot motion.
///
/// Blocks until the robot has settled, bounded by the command timeout.
/// The robot accepts a pause in motion and at standstill, as long as it
/// is enabled and not stopping.
///
/// This method is thread safe: it may be called from another thread
/// than the one issuing instructions, but not from another client, since
/// the pause state is held per client.
///
/// # Returns
///
/// `Ok(())` on success.
///
/// # Errors
///
/// Returns [`RobotArmError::CannotPause`](crate::error::RobotArmError::CannotPause)
/// if the robot control rejects the pause in the robot's current state,
/// [`RobotArmError::DidNotStop`](crate::error::RobotArmError::DidNotStop)
/// if the robot does not settle in time, or the error the pause command
/// failed with. A rejected pause leaves no pause in force; any other
/// failed pause command leaves the pause state as it was. After
/// `DidNotStop` the pause is in force, and queued motions wait for
/// [`continue_motion`](Self::continue_motion).
fn pause_motion(&self) -> Result<()> {
self.send_pause_motion()?.confirm()
}
/// Send the pause and return before the robot has settled.
///
/// The pause takes effect as in [`pause_motion`](Self::pause_motion).
/// Call [`confirm`](CommandHandle::confirm) on the returned handle to
/// wait until the robot has settled.
///
/// # Returns
///
/// The handle that confirms the pause.
/// [`CommandHandle::confirm`] returns
/// [`RobotArmError::DidNotStop`](crate::error::RobotArmError::DidNotStop)
/// if the robot does not settle in time; the pause is then in force.
///
/// # Errors
///
/// Returns [`RobotArmError::CannotPause`](crate::error::RobotArmError::CannotPause)
/// if the robot control rejects the pause in the robot's current state,
/// or the error the pause command failed with.
fn send_pause_motion(&self) -> Result<CommandHandle<'_>>;
/// Continue motion after a pause.
///
/// Lifts the pause on the robot control only if it still holds one: a
/// disable or a robot error discards the control's pause, while the
/// pause of this library holds new instructions until this call.
/// Without a pause, the call has no effect.
///
/// # Returns
///
/// `Ok(())` on success.
///
/// # Errors
///
/// Returns [`RobotArmError`](crate::error::RobotArmError) if reading
/// the pause state or the continue command fails.
fn continue_motion(&self) -> Result<()> {
self.send_continue_motion()?.confirm()
}
/// Send the continue.
///
/// The control lifts the pause when it accepts the call, so the
/// returned handle is already confirmed.
///
/// # Returns
///
/// The handle of the continue, already confirmed.
///
/// # Errors
///
/// As for [`continue_motion`](Self::continue_motion).
fn send_continue_motion(&self) -> Result<CommandHandle<'_>>;
}
GetPauseState
pub trait GetPauseState {
/// Get the pause state of the robot.
///
/// Does not block: the state is read from the robot control's pause and
/// standstill flags without waiting for the robot to settle.
///
/// # Returns
///
/// [`Paused`](PauseState::Paused) if a pause is instructed and the
/// robot arm stands still, [`Pausing`](PauseState::Pausing) if a
/// pause is instructed and the arm still decelerates,
/// [`NotPaused`](PauseState::NotPaused) otherwise.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidValue`](crate::error::RobotArmError::InvalidValue)
/// with [`ControlValueError::PauseStateUnsupported`](crate::error::ControlValueError::PauseStateUnsupported)
/// if the robot control is older than voraus.core 2.29, or another
/// [`RobotArmError`](crate::error::RobotArmError) if the robot is not
/// connected or a read fails.
fn pause_state(&self) -> Result<PauseState>;
}