Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

First program

Three steps on one page: connect and read a state, stream states, make the first motion. At the end the arm has moved 5 cm up under your program and come to rest there.

Prerequisites: Install and The realtime machine, or the simulator with RealtimeConfig::Ignore in place of Enforce below.

1. Connect and read the state

extern crate franka;
use franka::{RealtimeConfig, Robot};

fn main() -> franka::FrankaResult<()> {
    let robot = Robot::new("172.16.0.2", RealtimeConfig::Enforce)?;
    println!("FCI version {:?}, server version {}", robot.fci_version(), robot.server_version());

    let state = robot.read_once()?;
    println!("q      = {:?}", state.q);
    println!("O_T_EE = {:?}", state.O_T_EE);
    println!("mode   = {}", state.robot_mode);
    Ok(())
}

Robot::new opens the command channel, negotiates the protocol version (an FR3 speaks FCI v10, a Franka Emika Robot FCI v5; the same binary drives both) and, with Enforce, checks the realtime prerequisites. read_once waits for one state of the 1 kHz stream: q are the seven joint angles in radians, O_T_EE the end-effector pose as a column-major 4x4 matrix in the base frame, and robot_mode prints Idle on a robot that is ready to move.

2. Stream states

read takes a callback and keeps going until it returns false:

extern crate franka;
use franka::{RealtimeConfig, Robot};
fn main() -> franka::FrankaResult<()> {
let robot = Robot::new("172.16.0.2", RealtimeConfig::Enforce)?;
let mut count = 0;
robot.read(|state| {
    println!("{:?}", state.q);
    count += 1;
    count < 100
})?;
Ok(()) }

One state per millisecond, on the calling thread. examples/echo_robot_state.rs is this program, a port of libfranka’s example of the same name. The arm has not moved yet.

3. The first motion

Have the user stop button at hand and make sure there is free space above the end effector. The robot must be in FCI mode with the brakes open.

extern crate franka;
use std::sync::Arc;
use franka::{RealtimeConfig, Robot, TargetControlOptions};

fn main() -> franka::FrankaResult<()> {
let robot = Arc::new(Robot::new("172.16.0.2", RealtimeConfig::Enforce)?);
let control = robot.start_cartesian_target_control(TargetControlOptions::default())?;
let start = control.target();            // the start position, base frame, metres
control.set_position([start[0], start[1], start[2] + 0.05])?;   // 5 cm up
std::thread::sleep(std::time::Duration::from_secs(2));
control.stop()?;                         // settle, finish the motion, join the thread
Ok(()) }

What to expect on the first run: the arm rises 5 cm along the base z axis in about 0.9 s and stays there; stop() returns about a quarter of a second later, and the program exits.

The target was a step; the motion is not. start_cartesian_target_control spawns the 1 kHz loop on a realtime thread of its own and returns a handle. set_position writes the target into a slot that the loop reads every cycle, and an online trajectory generator in the loop re-plans a jerk-limited profile from the current command to the target on every cycle, under a budget of 0.3 m/s, 0.5 m/s² and 20 m/s³ by default (a norm; each axis gets 1/√3 of it). The loop tracks the resulting pose stream itself: every cycle it solves for the joint configuration of the pose and sends the torques of an impedance law that pulls the arm there, 750 N/m in translation by default, so the arm gives way when pushed and returns when let go. TargetControlOptions::default().with_backend(Backend::RobotController) has the robot’s own Cartesian impedance controller track the stream instead. stop() waits until the command has landed on the target, holds it for 250 cycles and then finishes the motion, because the robot refuses to finish on a moving command. set_position can be called from any thread at any rate, which is the point: a planner at 10 Hz, a script, a socket. Command from a low-rate program has the options and the guards; Three ways to control the arm places this next to the 1 kHz callback API and ActiveControl.

If the robot refuses the motion, stop() returns FrankaError::Control with the robot’s error text and the reflex reason, and robot.automatic_error_recovery() clears it; see State and errors.

The README’s quick example (examples/readme_joint_move.rs) is the 1 kHz-side equivalent of a first motion: a joint-space cosine on joint 4 written as a loop of your own, read_once then write_once, on start_joint_position_control (see Drive the loop yourself). CI runs it against the simulator.

Where next