Quick Start

This guide will get you controlling a Franka robot in minutes.

Basic Example

Here’s a minimal example that demonstrates the core functionality:

import asyncio
import numpy as np
from aiofranka import RobotInterface, FrankaController

async def main():
    # Connect to robot (use IP for real robot, None for simulation)
    robot = RobotInterface("172.16.0.2")
    controller = FrankaController(robot)

    # Start the 1kHz control loop
    await controller.start()

    try:
        # Test connection quality
        await controller.test_connection()

        # Move to home position with smooth trajectory
        await controller.move([0, 0, 0.0, -1.57079, 0, 1.57079, -0.7853])

        # Switch to impedance control
        controller.switch("impedance")
        controller.kp = np.ones(7) * 80.0
        controller.kd = np.ones(7) * 4.0
        controller.set_freq(50)  # 50Hz update rate

        # Execute sinusoidal motion
        for cnt in range(100):
            delta = np.sin(cnt / 50.0 * np.pi) * 0.1
            init = controller.initial_qpos
            await controller.set("q_desired", delta + init)

    finally:
        # Always stop gracefully
        await controller.stop()

if __name__ == "__main__":
    asyncio.run(main())

Understanding the Code

Let’s break down what each part does:

  1. Create Robot Interface

    robot = RobotInterface("172.16.0.2")  # Real robot
    # robot = RobotInterface(None)        # Simulation
    

    The IP address connects to your real robot. Use None for simulation mode.

  2. Create Controller

    controller = FrankaController(robot)
    

    The controller manages the 1kHz control loop and provides high-level commands.

  3. Start Control Loop

    await controller.start()
    

    This starts the background control loop at 1kHz. Must be awaited!

  4. Test Connection

    await controller.test_connection()
    

    Runs for 5 seconds and prints timing statistics. Useful for diagnosing issues.

  5. Move with Trajectory

    await controller.move([0, 0, 0, -1.57, 0, 1.57, -0.785])
    

    Generates and executes a smooth, jerk-limited trajectory to the target position.

  6. Switch Controller Mode

    controller.switch("impedance")
    

    Switches between impedance, OSC, and torque control modes at runtime.

  7. Set Gains

    controller.kp = np.ones(7) * 80.0  # Stiffness
    controller.kd = np.ones(7) * 4.0   # Damping
    

    Configure controller gains for desired behavior.

  8. Set Update Frequency

    controller.set_freq(50)  # 50 Hz
    

    Enforces timing for subsequent set() calls.

  9. Send Commands

    await controller.set("q_desired", target)
    

    Rate-limited setter that automatically maintains the specified frequency.

  10. Stop Controller

    await controller.stop()
    

    Gracefully stops the control loop and robot. Always use in a finally block!

Simulation Mode

Test your code without hardware:

import asyncio
from aiofranka import RobotInterface, FrankaController

async def test_in_simulation():
    # None = simulation mode
    robot = RobotInterface(None)
    controller = FrankaController(robot)

    await controller.start()

    # Same code works in simulation!
    await controller.move()

    await controller.stop()

asyncio.run(test_in_simulation())

The MuJoCo viewer will open automatically, showing the robot motion.

Reading Robot State

Access current robot state at any time:

state = controller.state

print(f"Joint positions: {state['qpos']}")      # [rad]
print(f"Joint velocities: {state['qvel']}")     # [rad/s]
print(f"End-effector pose:\n{state['ee']}")     # 4x4 transform
print(f"Jacobian:\n{state['jac']}")             # (6, 7)
print(f"Mass matrix:\n{state['mm']}")           # (7, 7)
print(f"Last torques: {state['last_torque']}")  # [Nm]

Controller Modes

aiofranka supports three control modes:

Impedance Control (Joint Space)

controller.switch("impedance")
controller.kp = np.ones(7) * 80.0
controller.kd = np.ones(7) * 4.0
controller.set_freq(50)

for i in range(100):
    target = compute_target(i)
    await controller.set("q_desired", target)

Best for: Joint-space trajectories, compliant behavior

Operational Space Control (Task Space)

controller.switch("osc")
controller.ee_kp = np.array([300, 300, 300, 1000, 1000, 1000])
controller.ee_kd = np.ones(6) * 10.0
controller.set_freq(50)

desired_ee = np.eye(4)
desired_ee[:3, 3] = [0.5, 0.0, 0.4]  # Position [x, y, z]
await controller.set("ee_desired", desired_ee)

Best for: Cartesian motions, end-effector tracking