Stop Behavior
For certain use cases, it might be necessary to configure the exact behavior of stopping a robot: how the robot decelerates when a stop is triggered, and how long it may take.
A stop can be triggered by different sources, for example a safety stop or a user stop. The stop behavior can be configured for each source independently. The available settings are the stop type and, for time-based stop types, the duration until the robot stops.
Stop Sources
The StopSource enum names the sources whose reaction can be configured:
Variant |
Description |
|---|---|
|
Stop triggered by an error condition of the robot control. |
|
Stop triggered by the application: |
|
Safety stop (Safe Stop 1). |
Stop Types
The StopType enum names the deceleration strategies:
Variant |
Description |
|---|---|
|
Each axis stops as fast as possible, not synchronized. |
|
Each axis stops within the configured stop duration. |
|
The robot decelerates along its current path within the configured stop duration. |
Not every combination of stop source and stop type is supported: the user stop is always a path stop, so
set_stop_type() rejects any other stop type for it with an InvalidArgument error. The stop duration is
stored per source and only has an effect for the time-based stop types. It must lie between 0.1 s and 10 s
(STOP_DURATION_RANGE); this range is validated at run time.
Configuring the Stop Behavior
The stop type of a source is set with set_stop_type(), its duration with set_stop_duration():
robot.set_stop_type(StopSource::SS1, StopType::AxisStopRapid)?;
robot.set_stop_type(StopSource::ErrorStop, StopType::AxisStopTimeBased)?;
robot.set_stop_duration(StopSource::ErrorStop, Duration::from_millis(500))?;
robot.set_stop_duration(StopSource::UserStop, Duration::from_secs(1))?;
The configuration is read back with stop_type() and stop_duration():
let stop_type = robot.stop_type(StopSource::SS1)?;
if stop_type != StopType::PathStopTimeBased {
return Err(ExampleError::StopTypeNotSet {
expected: StopType::PathStopTimeBased,
actual: stop_type,
});
}
let stop_duration = robot.stop_duration(StopSource::SS1)?;
if stop_duration != desired_stop_duration {
return Err(ExampleError::StopDurationNotSet {
expected: desired_stop_duration,
actual: stop_duration,
});
}
Stopping with the Configured Behavior
A stop then follows the configuration of its source. Here, stop_motion() during a motion is a user stop, so the robot
decelerates on its path within the configured second:
robot.move_ptp(poses::home()).start()?.join()?;
robot.move_ptp(poses::vertical()).start()?;
sleep(Duration::from_millis(100));
robot.stop_motion()?;
Full Example
Stop behavior example
//! Stop behavior examples.
//!
//! Demonstrates how to configure the stop behavior of a robot:
//! setting stop types and durations for different stop sources.
mod common;
use std::thread::sleep;
use std::time::Duration;
use common::poses;
use thiserror::Error;
use tracing::info;
use voraus_robot_arm::prelude::*;
/// The errors of this example.
#[derive(Debug, Error)]
enum ExampleError {
/// A call to the robot failed.
#[error(transparent)]
Robot(#[from] RobotArmError),
/// The robot control reports another stop type than the one set.
#[error("the stop type is {actual:?}, expected {expected:?}")]
StopTypeNotSet {
/// The stop type that the example set.
expected: StopType,
/// The stop type that the robot control reports.
actual: StopType,
},
/// The robot control reports another stop duration than the one set.
#[error("the stop duration is {actual:?}, expected {expected:?}")]
StopDurationNotSet {
/// The stop duration that the example set.
expected: Duration,
/// The stop duration that the robot control reports.
actual: Duration,
},
}
/// Example showing how to configure and use stop behavior.
///
/// Every stop source has a stop type and a stop duration; the duration is
/// only in effect for the time-based stop types. The user stop is always a
/// path stop, so only its duration is worth configuring.
fn run_stop_behavior_example(
robot: &(impl SetStopBehavior + Stop + MovePtp),
) -> Result<(), ExampleError> {
info!("--- Stop Behavior ---");
// Configure the stop types and durations per stop source.
robot.set_stop_type(StopSource::SS1, StopType::AxisStopRapid)?;
robot.set_stop_type(StopSource::ErrorStop, StopType::AxisStopTimeBased)?;
robot.set_stop_duration(StopSource::ErrorStop, Duration::from_millis(500))?;
robot.set_stop_duration(StopSource::UserStop, Duration::from_secs(1))?;
// Move to home then start a motion and stop mid-way.
robot.move_ptp(poses::home()).start()?.join()?;
robot.move_ptp(poses::vertical()).start()?;
sleep(Duration::from_millis(100));
robot.stop_motion()?;
// Use the same path stop with the same duration for all sources.
let desired_stop_duration = Duration::from_secs(3);
for source in StopSource::ALL {
robot.set_stop_type(source, StopType::PathStopTimeBased)?;
robot.set_stop_duration(source, desired_stop_duration)?;
}
// Verify configuration.
let stop_type = robot.stop_type(StopSource::SS1)?;
if stop_type != StopType::PathStopTimeBased {
return Err(ExampleError::StopTypeNotSet {
expected: StopType::PathStopTimeBased,
actual: stop_type,
});
}
let stop_duration = robot.stop_duration(StopSource::SS1)?;
if stop_duration != desired_stop_duration {
return Err(ExampleError::StopDurationNotSet {
expected: desired_stop_duration,
actual: stop_duration,
});
}
// Unstop and move to zero pose.
robot.unstop_motion()?;
robot.move_ptp(JointPose::default()).start()?.join()?;
info!("Stop behavior configuration complete.");
Ok(())
}
fn main() -> Result<(), ExampleError> {
common::init_logging();
let robot = common::create_connected_robot()?;
run_stop_behavior_example(&robot)?;
info!("Done.");
Ok(())
}
Trait Definitions
GetStopBehavior
pub trait GetStopBehavior {
/// Read the configured stop type of a stop source.
///
/// # Arguments
///
/// * `source` - The stop source whose configuration is read.
///
/// # Returns
///
/// The configured stop type of the source.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidValue`](crate::error::RobotArmError::InvalidValue)
/// if the control reports an unknown stop type, or the error the read
/// failed with.
fn stop_type(&self, source: StopSource) -> Result<StopType>;
/// Read the configured stop duration of a stop source.
///
/// # Arguments
///
/// * `source` - The stop source whose duration is read.
///
/// # Returns
///
/// The stop duration configured for the source.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidValue`](crate::error::RobotArmError::InvalidValue)
/// if the control reports a negative or non-finite duration, or the
/// error the read failed with.
fn stop_duration(&self, source: StopSource) -> Result<Duration>;
}
SetStopBehavior
pub trait SetStopBehavior: GetStopBehavior {
/// Configure the stop type of a stop source.
///
/// # Arguments
///
/// * `source` - The stop source to configure.
/// * `stop_type` - The desired stop type.
///
/// # Returns
///
/// `Ok(())` once the control confirms the stop type.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidArgument`](crate::error::RobotArmError::InvalidArgument)
/// if the stop type is not available for the source,
/// [`RobotArmError::RobotInErrorState`](crate::error::RobotArmError::RobotInErrorState)
/// with the control's error if the control refuses the write,
/// [`RobotArmError::NotConfirmed`](crate::error::RobotArmError::NotConfirmed)
/// if the control does not confirm the configuration within the command
/// timeout, or the error the write failed with.
fn set_stop_type(&self, source: StopSource, stop_type: StopType) -> Result<()> {
self.send_stop_type(source, stop_type)?.confirm()
}
/// Send the stop type of a stop source.
///
/// The call returns before the control confirms the stop type. Call
/// [`confirm`](CommandHandle::confirm) on the returned handle to wait
/// for it.
///
/// # Arguments
///
/// * `source` - The stop source to configure.
/// * `stop_type` - The desired stop type.
///
/// # Returns
///
/// The handle that confirms the stop type. [`CommandHandle::confirm`]
/// returns
/// [`RobotArmError::RobotInErrorState`](crate::error::RobotArmError::RobotInErrorState)
/// with the control's error if the control refuses the write, or
/// [`RobotArmError::NotConfirmed`](crate::error::RobotArmError::NotConfirmed)
/// if the control does not confirm the stop type within the command
/// timeout.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidArgument`](crate::error::RobotArmError::InvalidArgument)
/// if the stop type is not available for the source, or the error the
/// write failed with.
fn send_stop_type(&self, source: StopSource, stop_type: StopType) -> Result<CommandHandle<'_>>;
/// Configure the stop duration of a stop source, used by the time-based
/// stop types.
///
/// # Arguments
///
/// * `source` - The stop source to configure.
/// * `duration` - The stop duration, in [`STOP_DURATION_RANGE`].
///
/// # Returns
///
/// `Ok(())` once the control confirms the stop duration.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidArgument`](crate::error::RobotArmError::InvalidArgument)
/// if the duration is out of range,
/// [`RobotArmError::RobotInErrorState`](crate::error::RobotArmError::RobotInErrorState)
/// with the control's error if the control refuses the write,
/// [`RobotArmError::NotConfirmed`](crate::error::RobotArmError::NotConfirmed)
/// if the control does not confirm the configuration within the command
/// timeout, or the error the write failed with.
fn set_stop_duration(&self, source: StopSource, duration: Duration) -> Result<()> {
self.send_stop_duration(source, duration)?.confirm()
}
/// Send the stop duration of a stop source.
///
/// The call returns before the control confirms the stop duration. Call
/// [`confirm`](CommandHandle::confirm) on the returned handle to wait
/// for it.
///
/// # Arguments
///
/// * `source` - The stop source to configure.
/// * `duration` - The stop duration, in [`STOP_DURATION_RANGE`].
///
/// # Returns
///
/// The handle that confirms the stop duration. [`CommandHandle::confirm`]
/// returns
/// [`RobotArmError::RobotInErrorState`](crate::error::RobotArmError::RobotInErrorState)
/// with the control's error if the control refuses the write, or
/// [`RobotArmError::NotConfirmed`](crate::error::RobotArmError::NotConfirmed)
/// if the control does not confirm the stop duration within the command
/// timeout.
///
/// # Errors
///
/// Returns [`RobotArmError::InvalidArgument`](crate::error::RobotArmError::InvalidArgument)
/// if the duration is out of range, or the error the write failed with.
fn send_stop_duration(
&self,
source: StopSource,
duration: Duration,
) -> Result<CommandHandle<'_>>;
}