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:
Create Robot Interface
robot = RobotInterface("172.16.0.2") # Real robot # robot = RobotInterface(None) # Simulation
The IP address connects to your real robot. Use
Nonefor simulation mode.Create Controller
controller = FrankaController(robot)
The controller manages the 1kHz control loop and provides high-level commands.
Start Control Loop
await controller.start()
This starts the background control loop at 1kHz. Must be awaited!
Test Connection
await controller.test_connection()
Runs for 5 seconds and prints timing statistics. Useful for diagnosing issues.
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.
Switch Controller Mode
controller.switch("impedance")
Switches between impedance, OSC, and torque control modes at runtime.
Set Gains
controller.kp = np.ones(7) * 80.0 # Stiffness controller.kd = np.ones(7) * 4.0 # Damping
Configure controller gains for desired behavior.
Set Update Frequency
controller.set_freq(50) # 50 Hz
Enforces timing for subsequent
set()calls.Send Commands
await controller.set("q_desired", target)
Rate-limited setter that automatically maintains the specified frequency.
Stop Controller
await controller.stop()
Gracefully stops the control loop and robot. Always use in a
finallyblock!
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