uv pip install rfx-sdk
import rfx
# Every robot, same interface
robot = rfx.RealRobot("so101.yaml")
obs = robot.observe()
robot.act(action)
# Every model, self-describing
loaded = rfx.load_policy("hf://rfx-community/go2-walk-v1")
rfx.run(robot, loaded, rate_hz=50)
Why rfx
ROS was built for message passing between components. We're in a different era -- the workflow is collect demos, train a policy, deploy, iterate. rfx is built from scratch for that loop, with all the infrastructure you'd expect from a robotics framework underneath.
- Rust core for real-time control, Python SDK for fast research
- Three-method robot interface --
observe(),act(),reset()-- same API for sim and real - Self-describing models -- save once, load anywhere, deploy with zero config
- HuggingFace Hub native -- push and pull policies like you push datasets
- Zenoh transport -- pub/sub topics, nodes, launch graphs -- all the ROS primitives, none of the pain
- ROS 2 interop -- coexist with existing ROS 2 stacks via zenoh-plugin-ros2dds bridge
- Batteries included -- simulation (Genesis, MJX), teleoperation, LeRobot export, hardware drivers
Install
uv pip install rfx-sdk
With simulation and robot extras:
uv pip install rfx-sdk rfx-sdk-sim rfx-sdk-go2 rfx-sdk-lerobot
From source:
git clone https://github.com/quantbagel/rfx.git && cd rfx
bash scripts/setup-from-source.sh
The interface
Every robot in rfx -- simulated or real -- implements the same three methods:
robot = rfx.SimRobot.from_config("go2.yaml", backend="genesis")
# robot = rfx.RealRobot("so101.yaml", port="/dev/ttyACM0")
obs = robot.observe() # {"state": Tensor(1, 64), "images": ...}
robot.act(action) # Tensor(1, 64)
robot.reset()
Run a policy against any robot with one call:
rfx.run(robot, policy, rate_hz=200, duration=30.0)
Rate-controlled loop with jitter tracking, error handling, and clean shutdown built in.
Train
from rfx.nn import MLP
from rfx.utils.transforms import ObservationNormalizer
policy = MLP(obs_dim=48, act_dim=12, hidden=[256, 256])
normalizer = ObservationNormalizer(state_dim=48)
# ... your training loop ...
tinygrad-native policies. MLP, ActorCritic, or subclass Policy for your own architecture.
Save
Every saved model is a self-describing directory. Weights, architecture, robot config, normalizer -- everything needed to reconstruct and deploy.
policy.save("runs/go2-walk-v1",
robot_config=config,
normalizer=normalizer,
training_info={"total_steps": 50000, "best_reward": 245.3})
runs/go2-walk-v1/
rfx_config.json # architecture + robot + training metadata
model.safetensors # weights
normalizer.json # observation normalizer state
Push to HuggingFace Hub:
rfx.push_policy("runs/go2-walk-v1", "rfx-community/go2-walk-v1")
Load and deploy
Load from disk or Hub. No need to know the architecture, hyperparameters, or training setup.
loaded = rfx.load_policy("runs/go2-walk-v1")
loaded = rfx.load_policy("hf://rfx-community/go2-walk-v1")
loaded.policy_type # "MLP"
loaded.robot_config # RobotConfig(name="Go2", ...)
loaded.training_info # {"total_steps": 50000, "best_reward": 245.3}
# Deploy -- torch/tinygrad conversion handled automatically
robot = rfx.RealRobot(loaded.robot_config)
rfx.run(robot, loaded, rate_hz=loaded.robot_config.control_freq_hz)
Inspect metadata without loading weights:
rfx.inspect_policy("runs/go2-walk-v1")
Supported hardware
| Robot | Type | Interface | Status |
|---|---|---|---|
| SO-101 | 6-DOF arm | USB serial (Rust driver) | Ready |
| Unitree Go2 | Quadruped | Ethernet (Zenoh transport) | Ready |
Custom robots: implement observe() / act() / reset() or write a YAML config with URDF.
Simulation
# Genesis (GPU-accelerated)
robot = rfx.SimRobot.from_config("so101.yaml", backend="genesis", viewer=True)
# MJX (JAX-accelerated MuJoCo)
robot = rfx.SimRobot.from_config("go2.yaml", backend="mjx", num_envs=4096)
# Mock (zero dependencies, for testing)
robot = rfx.MockRobot(state_dim=12, action_dim=6)
Teleoperation
Bimanual SO-101 recording:
from rfx.teleop import run, so101
arm = so101()
run(arm, logging=True, rate_hz=200, duration_s=30.0)
Communication and runtime
Under the hood, rfx has a full robotics communication stack built on Zenoh -- topics, nodes, launch files, graph introspection. It's there when you need it, invisible when you don't.
from rfx.teleop import create_transport
# Pub/sub messaging -- Zenoh transport (Rust-backed, compiled in by default)
transport = create_transport()
transport.publish("robot/state", state_bytes)
sub = transport.subscribe("robot/cmd")
envelope = sub.recv(timeout_s=1.0)
Nodes follow a simple lifecycle contract:
from rfx.runtime.node import Node
class ControlNode(Node):
publish_topics = ("robot/cmd",)
subscribe_topics = ("robot/state",)
def setup(self): ... # init
def tick(self): ... # one loop iteration
def shutdown(self): ... # cleanup
Launch multiple nodes from a YAML graph:
name: go2-deploy
nodes:
- package: go2_ctrl
node: policy_node
rate_hz: 200
- package: go2_ctrl
node: logger_node
rate_hz: 10
rfx launch go2_deploy.yaml
rfx graph # inspect the active node graph
rfx topic-list # see all live topics
ROS 2 coexistence: rfx topics are visible to ROS 2 tools via the zenoh-plugin-ros2dds bridge. Migrate incrementally -- run rfx nodes alongside existing ROS 2 nodes, no code changes required on either side.
Enterprise scaling: The same code that runs on one robot runs on a fleet. No architecture changes, no extra infra. Get in touch -- we'll handle scaling so you don't have to.
Production Zenoh setup
Zenoh is compiled into the native extension by default -- no extra packages to install. Single-machine setups work out of the box with automatic peer discovery.
For multi-machine deployments, pass endpoints explicitly:
from rfx.node import auto_transport
transport = auto_transport(
connect=["tcp/192.168.1.100:7447"], # remote Zenoh router
shared_memory=True, # zero-copy on same machine
)
Or configure via environment:
export RFX_ZENOH_CONNECT="tcp/192.168.1.100:7447" # comma-separated endpoints
export RFX_ZENOH_LISTEN="tcp/0.0.0.0:7447" # listen for incoming peers
export RFX_ZENOH_SHARED_MEMORY=0 # disable shared memory if needed
Shared-memory transport is enabled by default for same-machine zero-copy between processes.
rfxJIT
Built-in kernel compiler that lowers and executes across cpu, cuda, and metal. IR-based autodiff, optimization passes (constant folding, dead-op elimination, fusion), and a tinyJIT-style cache+replay runtime.
from rfx.jit import value_and_grad, available_backends
# JAX-style functional transforms
loss_and_grads = value_and_grad(loss_fn)
# Check what's available on this machine
available_backends() # {"cpu": True, "cuda": True, "metal": False}
Enable globally via environment:
export RFX_JIT=1 # enable rfxJIT execution paths
export RFX_JIT_BACKEND=auto # auto | cpu | cuda | metal
When enabled, policy inference automatically routes through rfxJIT when possible, with transparent fallback to tinygrad's TinyJit.
Custom policies
Register your own architectures for automatic save/load:
from rfx.nn import Policy, register_policy
@register_policy
class MyPolicy(Policy):
def __init__(self, obs_dim, act_dim):
self.obs_dim, self.act_dim = obs_dim, act_dim
# ... build layers ...
def config_dict(self):
return {"obs_dim": self.obs_dim, "act_dim": self.act_dim}
def forward(self, obs):
# ... your forward pass ...
Docs
Community
License
MIT
Metadata
Release files for rfx-sdk 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rfx_sdk-0.2.0.tar.gz | 253.5 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rfx_sdk-0.2.0-cp313-cp313-win_amd64.whl | CPython 3.13 | CPython 3.13 | Windows x86-64 | Details |
| rfx_sdk-0.2.0-cp313-cp313-macosx_11_0_arm64.whl | CPython 3.13 | CPython 3.13 | macOS 11.0+ ARM64 | Details |
Total release size: 7.5 MB
Release files / rfx_sdk-0.2.0.tar.gz
| Download URL | rfx_sdk-0.2.0.tar.gz |
|---|---|
| Size | 253.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f75b01a79611015cd2b6f81065ea4b02d614966f5a8a75a3c120cde94d011d76
|
|
BLAKE2b-256 checksum How to use checksums |
b9fac8b39385489319378b23789bcc2c862f08c5f1f594cc05c89f1f768f5ee8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rfx_sdk-0.2.0-cp313-cp313-win_amd64.whl
| Download URL | rfx_sdk-0.2.0-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 3.7 MB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
f9085c22699a79a9b72247de9e7ebce7ea1ab00666065b947a1875d81fbc7b7b
|
|
BLAKE2b-256 checksum How to use checksums |
8cf2fed3cc158d6043ca1f85763215a64690bc83ba6e12cc94288211967143a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / rfx_sdk-0.2.0-cp313-cp313-macosx_11_0_arm64.whl
| Download URL | rfx_sdk-0.2.0-cp313-cp313-macosx_11_0_arm64.whl |
|---|---|
| Size | 3.6 MB |
| Tags | CPython 3.13 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
bbd79f532d20ecf79c6d007bc2e9839bea179aea67903c299c9428bdbad4e589
|
|
BLAKE2b-256 checksum How to use checksums |
df882739d8c77dca9d1f0b639a4814f979c775c307026df78bb8aab1eaeade7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|