Robot Interface
- aiofranka.robot.set_macos_control_thread_qos()[source]
Give the calling thread the scheduling that libfranka needs on macOS.
On macOS, libfranka busy-waits for robot states, which only keeps up with the 1 kHz control loop on a performance core. libfranka sets the QoS class USER_INTERACTIVE for the thread that creates the Robot; call this at the start of the thread that runs the control loop. Does nothing on other platforms or when busy-waiting is disabled with LIBFRANKA_MACOS_BUSY_WAIT=0.
- Returns:
True if the QoS class was set.
- Return type:
- aiofranka.robot.require_pylibfranka()[source]
Import pylibfranka, explaining how to install it on macOS if it is missing.
On macOS, aiofranka only depends on pylibfranka-macos, an unofficial build of pylibfranka, for Apple Silicon and macOS 15 or newer.
- Returns:
The pylibfranka module.
- Return type:
module
- Raises:
ModuleNotFoundError – If pylibfranka is not installed.
- aiofranka.robot.merge_payload(model, link_inertial, mass, com, inertia, site_id=None)[source]
Merge a payload on the flange into the last link of a MuJoCo FR3 model.
Sets fr3_link7’s mass, center of mass and inertia to those of the link and the payload together (parallel axis theorem), as RobotInterface.sync_payload() does for the model aiofranka’s controllers use. Call mj_setConst() afterwards.
- Parameters:
model (mujoco.MjModel) – Model to change
link_inertial (tuple) – fr3_link7’s own mass, ipos, iquat and inertia, as in the unmodified model
mass (float) – Payload mass [kg]
com (array-like) – Payload center of mass in the flange frame [m] (3,)
inertia (array-like) – Payload inertia about its center of mass in the flange frame [kg m^2] (3, 3)
site_id (int | None) – The flange site (default: attachment_site)
- aiofranka.robot.link_inertial(model)[source]
fr3_link7’s mass, ipos, iquat and inertia, for merge_payload().
- class aiofranka.robot.RobotInterface(ip=None, read_tool=True)[source]
Bases:
objectHigh-level interface for Franka FR3 robot control.
This class provides a unified interface for both real robot control (via pylibfranka) and simulation (via MuJoCo). It handles low-level communication, state synchronization, and kinematics/dynamics computation.
- model
MuJoCo model for kinematics/dynamics
- Type:
mujoco.MjModel
- data
MuJoCo data structure with current state
- Type:
mujoco.MjData
- robot
Real robot interface (if real=True)
- Type:
pylibfranka.Robot
- torque_controller
Active torque control interface
- viewer
MuJoCo viewer window (if real=False)
- Type:
mujoco.viewer
Examples
- Real robot:
>>> robot = RobotInterface("172.16.0.2") >>> robot.start() >>> state = robot.state >>> robot.step(np.zeros(7)) # Send zero torques >>> robot.stop()
- Simulation:
>>> robot = RobotInterface(None) >>> state = robot.state >>> robot.step(np.zeros(7)) # Updates MuJoCo simulation
- Caveats:
Must call start() before step() on real robot
Collision behavior is set to high thresholds by default
State is synced from robot on every access (thread-safe)
MuJoCo model must match real robot configuration
- __init__(ip=None, read_tool=True)[source]
Initialize robot interface.
- Parameters:
ip (str | None) – Robot IP address (e.g., “172.16.0.2”) for real robot, or None for simulation mode.
read_tool (bool) – Read the end-effector profile active in Desk (robot.tool), which FrankaController.activate() checks configurations against, with the Desk credentials saved by aiofranka; it waits up to 3 s for Desk and never takes the control token
- Raises:
ModuleNotFoundError – If pylibfranka is not installed (real robot mode)
ConnectionError – If cannot connect to robot at given IP
Note
In real mode, collision thresholds are set to [100.0] * 7 for joints and [100.0] * 6 for Cartesian space. Adjust via robot.robot.set_collision_behavior() for more conservative behavior.
- start()[source]
Start torque control mode on the real robot.
This must be called before sending torque commands. Does nothing in simulation.
- Raises:
RuntimeError – If robot is not ready or already in control mode
- Caveat:
After calling start(), you must send torque commands at ~1kHz to maintain control. Use FrankaController for automatic control loop management.
- stop()[source]
Stop torque control mode on the real robot.
This gracefully terminates the control session. Does nothing in simulation.
- Caveat:
Robot will hold position briefly then release brakes. Ensure robot is in a safe configuration before stopping.
- set_load(mass, com=(0.0, 0.0, 0.0), inertia=(0.0, 0.0, 0.0))[source]
Set the load attached to the flange, e.g. a tool without a gripper.
The load is set on the robot, whose internal controller compensates its gravity, and merged into the last link of the MuJoCo model with sync_payload(), so the mass matrix used by the OSC and the simulated dynamics include it. The flange frame is the attachment_site frame. A mass of 0 removes the load.
- Parameters:
mass (float) – Load mass [kg]
com (array-like) – Center of mass in the flange frame [m] (3,)
inertia (array-like) – Inertia about the center of mass in the flange frame [kg m^2], as a (3, 3) tensor or its diagonal (3,)
- Raises:
RuntimeError – If called while torque control is running
- Caveats:
Call before start(); the robot rejects the load during control
libfranka adds the load to the active end-effector profile in Desk
The load lasts until the next connection, which resets it. To keep a tool, save it as a profile with aiofranka.save_tool() and activate it with aiofranka.load_tool().
The robot rejects a mass without inertia
- sync_payload()[source]
Merge the payload that the robot compensates into the MuJoCo model.
On the real robot, the payload is the end effector configured in Desk plus the load set with set_load(), from the last robot state read. In simulation, it is the load. It replaces the payload merged before, so the mass matrix used by the OSC matches what the robot compensates.
RobotInterface calls this when it connects and in set_load(). Call it after changing the end effector in Desk while connected, after sync_mj().
- property state
Get current robot state with kinematics and dynamics.
- Returns:
- Dictionary containing:
qpos (np.ndarray): Joint positions [rad] (7,)
qvel (np.ndarray): Joint velocities [rad/s] (7,)
- ee (np.ndarray): End-effector pose as 4x4 homogeneous transform
[[R, p], [0, 1]] where R is rotation, p is position
jac (np.ndarray): End-effector Jacobian (6, 7) - [linear; angular]
mm (np.ndarray): Joint-space mass matrix (7, 7)
last_torque (np.ndarray): Last commanded torques [Nm] (7,)
- Return type:
Note
State is synchronized from real robot on every access. MuJoCo model is updated with latest robot state before computing kinematics/dynamics.
Example
>>> state = robot.state >>> print(f"Joint 1 position: {state['qpos'][0]:.3f} rad") >>> print(f"EE position: {state['ee'][:3, 3]}") >>> print(f"EE orientation: {state['ee'][:3, :3]}")
- property state_minimal
Get minimal robot state (fast, no kinematics/dynamics computation).
Use this for impedance/PID control where Jacobian and mass matrix are not needed. ~10x faster than full state property.
- Returns:
- Dictionary containing only:
qpos (np.ndarray): Joint positions [rad] (7,)
qvel (np.ndarray): Joint velocities [rad/s] (7,)
last_torque (np.ndarray): Last commanded torques [Nm] (7,)
- Return type:
- step(torque)[source]
Send torque command to robot or step simulation.
- Parameters:
torque (np.ndarray) – Joint torques [Nm] (7,)
- Raises:
RuntimeError – If real robot not started or communication error
Note
Real robot: Sends torque command via pylibfranka at current timestep
Simulation: Updates MuJoCo with torques and advances one timestep
- Caveats:
Must be called at ~1kHz for real robot to maintain control
Large torque changes may trigger safety limits
Torques should respect robot limits:
|tau_i| < 87 Nmfor joints 1-4,|tau_i| < 12 Nmfor joints 5-7
Example
>>> # Send gravity compensation torques >>> torque = robot.state['mm'] @ np.array([0, 0, 0, 0, 0, 0, -9.81]) >>> robot.step(torque)