Moving a Robot in Joint Space

A point-to-point (PTP) motion is a movement where the robot travels to a target configuration by following the shortest path in joint space. The motion is therefore the quickest possible, but the resulting end effector path in Cartesian space can be unpredictable.

This chapter specifically explains PTP movements. For detailed information on how to use these methods synchronously or asynchronously, see the asynchronous instructions section and the instructions chapter.

Performing a PTP Movement

A PTP movement is started with move_ptp() from the MovePtp trait. It requires a target of type JointPose, CartesianPose or CartesianTarget and returns a motion builder. Optional parameters are chained onto the builder, and start() queues the motion. The speed() modifier defines the general speed for the motion relative to the possible maximum joint speed. It accepts a Factor, which ranges from 0.01 to 1.0, or a Percent, which ranges from 1.0 to 100.0; start() rejects a lower value. Without speed(), the robot moves at full speed, a Factor of 1.0.

A CartesianPose stores its translation as Meters and its rotation as Radians, so the unit travels with every component. Its constructor takes the unit types of the crate, so a bare number does not compile: CartesianPose::new(Centimeters(55.0), Meters(0.0), Millimeters(300.0), Degrees(180.0), Radians(0.0), Radians(0.0)) is the same pose as CartesianPose::new(Meters(0.55), Meters(0.0), Meters(0.3), Radians(PI), Radians(0.0), Radians(0.0)). A JointPose takes one unit value per joint as well, Radians or Degrees for a rotary axis and Meters, Millimeters or Centimeters for a linear one.

Note

A CartesianPose is interpreted in the robot coordinate system.

A CartesianPose itself can be ambiguous with regard to the exact joint configuration of the robot. It is at the discretion of the underlying robot control to choose a convenient joint configuration to reach the given pose. This means that the configuration reached depends on the starting position. A CartesianTarget removes both ambiguities; see Perform a PTP Movement Using a CartesianTarget.

Perform a PTP Movement Using a JointPose

To perform a PTP movement to a target defined in joint space, pass a JointPose as the target:

// Move at 25% speed.
robot
    .move_ptp(poses::home())
    .speed(Factor(0.25))
    .start()?
    .join()?;
info!("Home reached (Factor 0.25).");

// Move at 100% speed.
robot
    .move_ptp(poses::vertical())
    .speed(Percent(100.0))
    .start()?
    .join()?;
info!("Vertical reached (Percent 100).");

Perform a PTP Movement Using a CartesianPose

To perform a PTP movement to a target defined in Cartesian space, pass a CartesianPose as the target:

let target = CartesianPose::new(
    Meters(0.1382),
    Meters(0.550),
    Meters(0.296),
    Radians(PI),
    Radians(0.0),
    Radians(0.0),
);
robot
    .move_ptp(target)
    .move_ptp(target + z(Meters(0.1)))
    .start()?
    .join_timeout(Duration::from_secs(10))?;

robot
    .move_ptp(target)
    .speed(Percent(100.0))
    .start()?
    .join()?;
info!("Target (Cartesian) reached.");

Since a Cartesian pose is ambiguous for a six-axis robot, the resulting joint configuration at the target can differ depending on the starting pose of the robot.

Perform a PTP Movement with Blending

PTP movements can use blending by chaining .blending() with a Factor or Percent. The higher the blending value, the more and earlier the blended path deviates from the original path that would reach the target pose. In most cases this allows the robot to maintain a higher path velocity and complete the overall path faster.

The exact behavior and interpretation of the blending parameter as well as implicit limitations are highly dependent on the specific robot used.

Blending only works if the instruction that follows is known to the robot control in advance. Therefore, blended motions are written as one block: .blending() is the transition into the next step, and all steps are sent together when start() is called:

fn ptp_with_blending(robot: &impl MovePtp) -> Result<()> {
    info!("--- PTP with blending ---");

    robot.move_ptp(poses::home()).start()?.join()?;

    robot
        .move_ptp(poses::blending_1())
        .blending(Percent(50.0))
        .move_ptp(poses::blending_2())
        .blending(Factor(0.5))
        .move_ptp(poses::blending_3())
        .start()?
        .join()?;
    info!("Blending sequence complete.");
    Ok(())
}

Note how the block ends on a step without blending, and how waiting happens only once, for the whole block. The type of the builder enforces it: after .blending() only a PTP or linear motion may follow, so a block that ends on a blended step does not compile.

Amending Blending Parameters

A blending parameter can be amended after the motion instruction was sent with amend_blending() from the AmendBlending trait. It applies to the most recently sent instruction:

fn amend_blending_example(robot: &(impl MovePtp + AmendBlending)) -> Result<()> {
    info!("--- Amending blending parameters ---");

    robot.move_ptp(poses::home()).start()?.join()?;

    robot.move_ptp(poses::blending_1()).start()?;
    robot.amend_blending(Percent(50.0))?;
    robot.move_ptp(poses::blending_2()).start()?;
    robot.amend_blending(Factor(0.5))?;
    robot.move_ptp(poses::blending_3()).start()?.join()?;
    info!("Amended blending sequence complete.");
    Ok(())
}

In this example the robot blends the first two waypoints into their successors just like in the previous example, but the blending parameters are supplied after sending the motion instruction. This is the escape hatch for cases where the next target is only known while the robot is already moving. Inside a block it is never needed.

Amending fails with RobotArmError::NotAllowed when there is nothing to amend (NotAllowedError::NoMotionToAmend): without a preceding motion, or once the previous motion has completed, since there is nothing left to blend out of; a wait, a stop, a robot error or a reconnect end the previous motion as well. The robot control may also drop a blend request that arrives just before the motion completes. Amending a motion that already carries blending fails with NotAllowedError::BlendingAlreadySpecified, and an out-of-range value with RobotArmError::UnitLimit.

A typical use is a target that depends on a signal which arrives while the robot is already moving. The robot moves to a safe position; if the signal says that it may enter a restricted area beyond, the running motion is amended with blending and the motion into the area is sent, so the robot blends smoothly into it. Without the signal it just stops at the safe position:

fn blend_into_a_late_target(
    robot: &(impl MovePtp + AmendBlending),
    may_enter: &AtomicBool,
) -> Result<()> {
    info!("--- Blending into a late target ---");

    robot.move_ptp(poses::home()).start()?.join()?;

    let safe_position = robot.move_ptp(poses::blending_1()).start()?;
    if !may_enter.load(Ordering::Acquire) {
        safe_position.join()?;
        info!("The area is blocked; stopped at the safe position.");
        return Ok(());
    }
    match robot.amend_blending(Percent(50.0)) {
        Ok(()) => info!("The area is free; blending into it."),
        // The signal came after the robot had reached the safe position:
        // there is nothing left to blend out of, so it enters from there.
        Err(RobotArmError::NotAllowed(NotAllowedError::NoMotionToAmend)) => {
            info!("The area is free; entering it from the safe position.");
        }
        Err(err) => return Err(err),
    }
    robot.move_ptp(poses::blending_2()).start()?.join()?;
    Ok(())
}

Perform a Relative PTP Movement

For moving the robot relative to its current pose, use move_ptp_relative(). Other than that, it accepts the same modifiers as move_ptp().

In the example below, the robot moves 10 cm in the positive z-direction from its current pose. The z() helper builds a CartesianPose in which every component is zero except z:

robot
    .move_ptp_relative(z(Meters(0.1)))
    .speed(Percent(100.0))
    .start()?
    .join()?;
info!("Moved +10cm in Z (relative).");

Perform a PTP Movement Using a CartesianTarget

A CartesianTarget wraps a CartesianPose and pins what a plain pose leaves open: the coordinate system the pose is expressed in (with_cs()), the frame that arrives at it (with_arriving_cs()), and the joint configuration it is reached in (with_configuration_vector()). CartesianTarget::new(pose) starts from the defaults of a plain pose: the robot coordinate system, the tool arriving, and no configuration vector. move_ptp() and move_ptp_relative() take it like any other target. See Coordinate Systems for an example with a user coordinate system. Without a configuration vector, the robot control chooses the joint solution; Configuration Vector explains the vector and moves to one target in two postures.

Full Example

Trait Definitions

The trait carries the entry points as default methods, which return a MotionBlock builder; its required method is what a robot type implements.