Skip to main content

Franka Simulation Server

A high-fidelity simulation server that communicates with the Franka robot's network protocol, enabling seamless switching between simulation and hardware. Runs on MuJoCo by default; Genesis is available as an optional backend.

Overview

The Franka Simulation Server provides a drop-in replacement for the real Franka robot, implementing the complete libfranka network protocol. This allows developers to:

  • Test and debug robot controllers in simulation before deployment
  • Develop applications that work identically on both simulation and hardware
  • Validate error handling and safety features
  • Experiment with different control strategies risk-free

Compatibility

franka_sim implements the latest libfranka wire protocol — robot server version 10. In this protocol the robot model is built client-side: during connection the client fetches the robot URDF via the GetRobotModel command and builds its own Pinocchio model. The server therefore just serves a valid URDF (a hand-less FR3 arm by default) instead of a precompiled model library.

You need a libfranka version that speaks server protocol 10:

libfranka Version Robot System Version Robot/Gripper Server
>= 0.18.0 >= 5.9.0 10 / 3 ✅ supported
>= 0.15.0 >= 5.7.2 9 / 3 — not supported

See the Franka software compatibility matrix for the full list. Older libfranka releases (server version 9 and below) use a different wire format — double-based RobotState and server-side LoadModelLibrary instead of GetRobotModel — and are not compatible with this server.

Related Projects

Preview Video

  • Native libfranka control

Libfranka Native Control

  • With Python

With Python

Architecture

Architecture

In this repository, we only provide the simulation server backend, with a choice of physics engines behind it (MuJoCo by default, Genesis optionally).

The libfranka python bindings will become available in a separate repository.

The system consists of several key components:

  1. libfranka Interface Layer

    • Implements the standard Franka robot network protocol
    • Handles TCP command interface and UDP state updates
    • Targets the latest libfranka wire protocol (robot server version 10)
  2. Physics Simulation Backend

    • Physics-based robot simulation using MuJoCo (default) or Genesis (--physics genesis)
    • Real-time joint state computation and dynamics
  3. State Management

    • Complete robot state tracking and synchronization
    • Accurate error reporting and status updates
    • Real-time state transmission (1kHz update rate)
  4. Control Modes

    • Joint Position Control
    • Joint Velocity Control
    • Joint Torque Control
    • Supports seamless switching between modes

Key Features

  • Protocol Compatibility: Full implementation of the Franka robot network protocol
  • Real-time Simulation: High-frequency state updates and control (1kHz)
  • Multiple Control Modes: Supports position, velocity, and torque control
  • Error Handling: Replicates real robot error states and recovery

Getting Started

Prerequisites

  • Python 3.9+
  • numpy==1.26.4
  • mujoco>=3.2,<3.3 (the default physics backend, installed automatically)

Genesis is an optional backend (--physics genesis) — see below.

Installation

Option 1: Install from PyPI (Recommended)

The package is available on PyPI and can be installed with pip. This pulls in MuJoCo, the default physics backend:

pip install franka-sim

To also use the Genesis backend, install the genesis extra:

pip install 'franka-sim[genesis]'

Option 2: Install from Source

# Clone the repository
git clone git@github.com:BarisYazici/libfranka-sim.git

# Install the package
cd libfranka-sim/simulation
pip install -e .

Basic Usage

After installation, you can run the server using the command-line executable:

# Start the server without visualization
run-franka-sim-server

# Start the server with visualization
run-franka-sim-server -v

Alternatively, if you installed from source, you can use:

# Start the simulation server
python -m franka_sim.run_server -v

In your application, use standard libfranka commands. The simulation will respond exactly like the real robot.

Testing in CI (no robot required)

Because the sim speaks the real wire protocol, your libfranka / franka_ros2 code can be regression-tested on every push. Three ways in — see the Testing in CI guide for details:

# GitHub Actions: start a simulated FR3, then run your tests against 127.0.0.1
- uses: BarisYazici/libfranka-sim@v1
  with:
    args: '--enforce-comm-constraints --enforce-motion-limits'
# Docker: self-contained image (models baked in, works offline)
docker run -d --network host ghcr.io/barisyazici/franka-sim
docker exec <container> franka-sim-check --timeout 30   # readiness gate
# pytest: `pip install franka-sim` ships a plugin; one server per session on a free port
def test_my_controller(franka_sim_server):
    connect_my_stack(franka_sim_server.host, franka_sim_server.port)

With --enforce-comm-constraints and --enforce-motion-limits the sim aborts motions the way the real FCI does (lost-cycle reflexes, discontinuity limits), so your error-recovery paths get exercised in CI instead of on hardware.

Gripper (Franka Hand)

The gripper server (libfranka gripper protocol, TCP port 1338) runs by default alongside the arm. Drive it with the standard franka::Gripper client (homing / move / grasp / stop).

# Arm + gripper, kinematic hand (no mesh; width tracked analytically, CI-friendly)
python -m franka_sim.run_server -v

# Arm + gripper, physics-backed Franka Hand: the hand mesh is loaded and the
# finger DOFs are simulated, so homing/move/grasp visibly move the fingers in
# the viewer (grasp succeeds on a finger-position stall against an object)
python -m franka_sim.run_server -v --gripper-physics

# Disable the gripper server entirely
python -m franka_sim.run_server -v --no-gripper
Flag Gripper backend Hand in viewer
(default) FrankaHandSim (kinematic) no
--gripper-physics FrankaHandPhysics (physics) yes, fingers move
--no-gripper none (arm only) no

Mobile duo (two arms + TMR base on one scene)

--mobile-duo serves the mobile FR3 duo: one physics scene (both arms and the TMR mobile base, physically rigid to each other) driven by three FCI bridges, one per role. libfranka clients cannot be told to use a port other than 1337, so the bridges are separated by host IP instead — each is bound to its own loopback alias with --bind ROLE=HOST, repeated once per role (left, right, base):

# Bring up the loopback aliases once per boot (Linux; 127.0.0.0/8 is all loopback)
sudo ip addr add 127.0.0.11/8 dev lo 2>/dev/null
sudo ip addr add 127.0.0.12/8 dev lo 2>/dev/null
sudo ip addr add 127.0.0.13/8 dev lo 2>/dev/null  # only needed with --spine

python -m franka_sim.run_server --mobile-duo \
  --scene-urdf /path/to/mobile_fr3_duo.urdf \
  --mesh-root  /path/to/franka_description \
  --bind left=127.0.0.11 \
  --bind right=127.0.0.12 \
  --bind base=127.0.0.10
Flag Meaning
--mobile-duo Serve the mobile duo instead of the classic single-arm server
--scene-urdf PATH The combined mobile_fr3_duo URDF loaded into the one physics scene (required with --mobile-duo; see below for generating it)
--mesh-root PATH Package root used to resolve package:// mesh URIs in the URDF — a franka_description checkout; defaults to the URDF's own directory
--bind ROLE=HOST Bind one bridge to a host address; repeat for left, right and base (all three are required)
--physics {genesis,mujoco} Physics backend for the scene (default mujoco)

By convention this repo uses three loopback aliases on 127.0.0.0/8, one per role, plus a fourth for the spine device:

Role Address
base 127.0.0.10
left arm 127.0.0.11
right arm 127.0.0.12
spine 127.0.0.13

Every bridge still listens on the standard libfranka command port (1337); override it for all three at once with --port.

Physics backend. MuJoCo is the default (--physics mujoco, or just omit the flag) and is installed as a core dependency. --physics genesis runs the same scene on Genesis instead, and needs the genesis extra: pip install 'franka-sim[genesis]'. Genesis' per-call kernel-launch overhead caps the scene at ~0.4x real time at its 2.5 ms step; MuJoCo holds 1.00x real time at a 1 ms step — the rate the FCI bridges actually serve — using about a third of one core. The protocol surface, the joint and link names, the initial pose and the reported state are identical between the two, so a client cannot tell them apart. Contacts are disabled on the MuJoCo path (the chassis' URDF collision meshes interpenetrate as authored, and nothing in this scene depends on contact: the base pose is integrated kinematically and both arms are servo-driven).

Generating --scene-urdf. scripts/generate_mobile_duo_urdf.sh <franka_description_dir> <output.urdf> runs the upstream xacro with the options this server expects (hand:=false, explicit robot_types, no ROS 2 control/Gazebo) from a sourced ROS 2 Jazzy environment. It asserts the franka_description checkout is pinned to the exact sha the mesh paths and joint names were generated against, and refuses to run otherwise — a different checkout can silently rename meshes or joints out from under the sim.

The fake spine device. The duo's prismatic lift (franka_spine_vertical_joint) is driven by a separate REST device on real hardware, not by libfranka. --spine runs a fake version of that device in-process (franka_sim.mobile.spine_stub) and shares its motion model with the scene, so a REST move visibly raises the lift (and everything mounted on it — the head and both arms) in the viewer:

python -m franka_sim.run_server --mobile-duo \
  --scene-urdf /path/to/mobile_fr3_duo.urdf --mesh-root /path/to/franka_description \
  --bind left=127.0.0.11 --bind right=127.0.0.12 --bind base=127.0.0.10 \
  --spine
Flag Meaning
--spine Also run the spine stub in-process (requires --mobile-duo)
--spine-host Address the spine stub binds (default: 127.0.0.13)
--spine-port Port the spine stub binds; SpineApiClient hardcodes 443 with no port, so leave this at the default unless you know why you're changing it (default: 443)
--spine-cert / --spine-key TLS certificate/key for the stub; a throwaway self-signed pair is generated automatically when omitted

The spine stub also runs standalone via the run-franka-spine-stub console script (installed with the package), useful for exercising just the REST device without a physics scene:

run-franka-spine-stub --host 127.0.0.13 --port 443

Cartesian-velocity protocol mode. The TMR base has no joint interface, so it is driven by libfranka's kCartesianVelocity motion generator: the client's commanded body-frame twist (O_dP_EE_c) is routed straight to the base's swerve inverse kinematics instead of to a joint position/velocity/ torque path.

Environment variables.

Variable Meaning
FRANKA_SIM_SPINE_PORT Test-suite only. SpineApiClient hardcodes port 443, which needs root to bind; set this to point the mobile-duo end-to-end tests at an unprivileged port instead (e.g. via an iptables REDIRECT 443 -> 8443)

Troubleshooting

If you're running the Genesis backend (--physics genesis) and encounter issues related to missing asset files, make sure you have the genesis extra installed with the correct version of genesis-world:

pip install 'franka-sim[genesis]'

The simulator automatically uses the assets provided by the Genesis package, so no additional asset files are needed. Note that genesis-world==0.2.1 also needs libigl<2.6 (newer libigl changes igl.signed_distance's return arity); install that pin alongside the extra if Genesis import fails with an unpacking error.

Configuration

Switching Between Simulation and Hardware

To switch between simulation and hardware:

  1. Update the robot IP address in your application:

    • Use localhost or 127.0.0.1 for simulation
    • Use the real robot's IP for hardware
  2. No other changes needed - your application code remains identical

Development Status

The simulation server currently implements all major features of the Franka robot:

  • Complete network protocol implementation
  • All joint interfaces
  • Real-time state updates
  • Visualization support
  • Genesis connection
  • libfranka python bindings
  • v10 wire protocol (Connect, float-based RobotState, GetRobotModel/URDF)
  • Robot model via URDF (client-side Pinocchio through GetRobotModel)
  • Gripper simulation / Franka Hand (kinematic + Genesis physics, --gripper-physics)
  • Automatic error recovery (so franka_hardware / franka_ros2 can activate)
  • Advanced collision detection (in progress)
  • Cartesian interfaces (planned)

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details.

Acknowledgments

  • Franka Robotics GmbH for the original libfranka implementation
  • The Genesis Simulator team for the physics engine

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

franka_sim-1.1.2.tar.gz (13.2 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

franka_sim-1.1.2-cp311-cp311-manylinux2014_x86_64.whl (558.2 kB view details)

Uploaded CPython 3.11

franka_sim-1.1.2-cp311-cp311-macosx_11_0_universal2.whl (558.2 kB view details)

Uploaded CPython 3.11macOS 11.0+ universal2 (ARM64, x86-64)

franka_sim-1.1.2-cp310-cp310-manylinux2014_x86_64.whl (558.2 kB view details)

Uploaded CPython 3.10

franka_sim-1.1.2-cp310-cp310-macosx_11_0_universal2.whl (558.2 kB view details)

Uploaded CPython 3.10macOS 11.0+ universal2 (ARM64, x86-64)

franka_sim-1.1.2-cp39-cp39-manylinux2014_x86_64.whl (558.2 kB view details)

Uploaded CPython 3.9

File details

Details for the file franka_sim-1.1.2.tar.gz.

File metadata

  • Download URL: franka_sim-1.1.2.tar.gz
  • Upload date:
  • Size: 13.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for franka_sim-1.1.2.tar.gz
Algorithm Hash digest
SHA256 2204e670cab7696c671226fe07ddab9caf030a45a9af1094c1ec792078ef7baf
MD5 99def98e9ced666d3436621e04a050e6
BLAKE2b-256 adbf8410a192a10a7a71a531c2856e1e9c8b1c4c6ea17c81726c8aa62cf25c9b

See more details on using hashes here.

File details

Details for the file franka_sim-1.1.2-cp311-cp311-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for franka_sim-1.1.2-cp311-cp311-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f27c35150cc14ca2720aae9329be7c026e42fc6a3f08e1a003b83b98e90e7cd4
MD5 e525635e765ffded526acd5a2a48c696
BLAKE2b-256 7e733862c9a4ae2cd2c2d1be0cc8f71a83c91968ff70b9009ef70e20b27b19ba

See more details on using hashes here.

File details

Details for the file franka_sim-1.1.2-cp311-cp311-macosx_11_0_universal2.whl.

File metadata

File hashes

Hashes for franka_sim-1.1.2-cp311-cp311-macosx_11_0_universal2.whl
Algorithm Hash digest
SHA256 5175d755dcd3fb3c4bef5c8e70e68d6530e3384a8afd62da70f1c011c3d3aebd
MD5 2ff6b3a6575a7ee7f1b3387c59cc6234
BLAKE2b-256 df8ad22ea6c958f9eaa5dfcb66fba5a9d4a6d60aa2b89549302ba9289bae7cf2

See more details on using hashes here.

File details

Details for the file franka_sim-1.1.2-cp310-cp310-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for franka_sim-1.1.2-cp310-cp310-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f93e2e51da5f3e74e7d440ab1b80f2bab3ec2b49ed764c3f46db8f7175db0c92
MD5 99dfff692f5ec01aab635d468580f1bb
BLAKE2b-256 9de3f12a0404605c24dc3ed28757ff6043c1d960d0713fc7282f13a6abacfeff

See more details on using hashes here.

File details

Details for the file franka_sim-1.1.2-cp310-cp310-macosx_11_0_universal2.whl.

File metadata

File hashes

Hashes for franka_sim-1.1.2-cp310-cp310-macosx_11_0_universal2.whl
Algorithm Hash digest
SHA256 b0659a170bb4bcf8cc9297a82cacc5c9f037bb2144848846726b017a44153b4d
MD5 8cfdb940af0b91e5ef1d20e00129218d
BLAKE2b-256 37e8367d8cb4e68800e5fa582f2b431e92ea6f07beb8ccbe12bf1bc9ee90de49

See more details on using hashes here.

File details

Details for the file franka_sim-1.1.2-cp39-cp39-manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for franka_sim-1.1.2-cp39-cp39-manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 257fbc8fe313138e24d9eb6b7d5b5c583f750eb4fa555ef14c242a496fb4581e
MD5 3eaef5b2bbf48366e397c12298c040b1
BLAKE2b-256 543b498ca8250726a7e00baf07c0e042d3f6874d2d646dd8f95902dba6b8c2d0

See more details on using hashes here.

Release history Release notifications | RSS feed

1.1.6

6 files

1.1.5

6 files

1.1.4

6 files

1.1.3

6 files

This release

1.1.2 This release

6 files

1.1.1

6 files

1.1.0

6 files

1.0.0

6 files

0.4.0

6 files

0.3.0

6 files

0.2.0

6 files

0.1.13

6 files

0.1.12

6 files

0.1.11

6 files

0.1.10

6 files

0.1.9

6 files

0.1.8

6 files

0.1.7

6 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page