Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

girder

A minimal ROS-free robotics framework in Rust — typed pub/sub, a deterministic sim clock, record/replay, a lock-free shared-memory ring, and TCP. No DDS, no parameter server, no launch XML. About 3 k lines of framework code.

MuJoCo pendulum publishing /joint_states on girder; bag replay is bit-exact

The GIF is a 3 s pendulum swing. A sim node owns the clock and publishes /joint_states + /clock; a rec node writes a .gdr bag. Replaying that bag onto a fresh domain reproduces every payload bit-exactly (CI gate).

Still: docs/demo_frame.png.

Numbers (measured on this machine, cargo test --release / girder-bench)

Metric Result
In-process pub/sub, 64-byte rkyv Blob64, N=20 000 p50 420 ns · p99 530 ns · 2.17 M/s
Shared-memory ring (two domains, same host) p50 710 ns · p99 1.9 µs · 1.16 M/s
TCP localhost, length-prefixed frames p50 22.7 µs · p99 38.7 µs · 46.2 k/s
iceoryx2 ipc::Service u64 (zero-copy POD, same box) p50 250 ns · 3.37 M/s
zenoh same-session 64-byte put p50 290 ns · 3.12 M/s
Record 1 500 pendulum steps + replay bit-exact payloads
Pendulum demo, 1 500 × 2 ms steps 5 ms wall (no render)

iceoryx2 wins raw IPC latency — it is a zero-copy POD path with years of work behind it, and the bench used an 8-byte u64 versus girder's 64-byte archived struct. girder is ~0.6× that in-process and ~0.35× on SHM, with a typed rkyv message layer, a sim clock, and bags in the same crate. That is the product: enough middleware for a small robot team, not a DDS replacement.

Methodology and a copy-paste table: docs/benchmarks.md. Re-run with cargo run --release -p girder-bench.

Quickstart

cargo test --release -p girder
cargo run --release -p girder-demo --bin pendulum
cargo run --release -p girder-demo --bin pendulum -- --render   # ffmpeg
cargo run --release -p girder-cli -- --help
use girder::{Domain, Qos, msg::Blob64};

let domain = Domain::builder("robot").simulated_clock().build()?;
let tx = domain.node("arm").advertise::<Blob64>("/ping", Qos::STREAMING)?;
let mut rx = domain.node("log").subscribe::<Blob64>("/ping", Qos::STREAMING)?;
tx.publish(&Blob64::with_seq(1))?;
assert_eq!(rx.try_recv()?.unwrap().msg.seq, 1);

CLI (the rosbag / ros2 topic hz replacements):

girder record -o run.gdr --topic /joint_states --ty JointState --shm --domain robot
girder replay run.gdr --rate 1.0 --shm --domain robot
girder top --shm --domain robot

Layout

crates/girder         core: Domain, Node, clock, bus, SHM, TCP, bags
crates/girder-cli     `girder record|replay|top|topics`
crates/girder-mujoco  sim bridge: JointState out, effort in, sim-owned clock
crates/girder-bevy    Bevy ECS plugin: Transform ↔ girder::msg::Transform
crates/girder-demo    pendulum GIF + bit-exact replay
crates/girder-bench   latency/throughput vs iceoryx2 and zenoh
assets/pendulum.xml   tiny MJCF used by the demo and the MuJoCo test

Design notes

  • Typed topics, tiny QoS. A topic is a name + an rkyv message type. QoS is history depth, latch, and max payload size. Overflow drops the oldest sample and counts it. There is no deadline / durability / liveliness matrix.
  • The sim owns time. Clock::simulated() starts at t = 0. tick(dt) advances it; a real-time factor of 0 runs as fast as the caller. Followers can subscribe to /clock (ClockMsg) and call Clock::follow.
  • One bus, three transports. In-process is a per-subscriber queue. Same-host uses a seqlock ring in /dev/shm (lock-free readers, a short spinlock on the write side — Mara Bos's atomics book as the reference). Cross-host is length-prefixed frames over TCP (GIRD magic). QUIC reuses that codec behind --features quic (self-signed, LAN-only).
  • Bags are boring on purpose. .gdr is append-only rkyv + timestamps + an FNV checksum per record. Replay at rate = 0 is as-fast-as-possible and is what CI uses for bit-exactness.
  • Bridges prove the seam, not the simulator. girder-mujoco steps a model and publishes JointState. girder-bevy is bevy_ecs only — no renderer — so a site twin can share poses without dragging wgpu into the bus crate.

Limitations

  • No discovery beyond a TCP bind/connect and a named SHM domain. No mDNS.
  • No request/reply, no parameters, no TF tree. Publish/subscribe and time are the v0.1 surface.
  • SHM slots are fixed-size (Qos::max_bytes, default 4 KiB). Oversize payloads are an error, not a fragmented write.
  • TCP reconnect exists as a bounded backlog on the writer; there is no session persistence across process death.
  • iceoryx2 and zenoh are benchmark targets, not pluggable backends (parked for post-v0.1).
  • The pendulum grasp of "a robot on girder" is one hinge. A UR5e + mobile base on the same bus is the next demo, not this one.

License

Apache-2.0. See LICENSE.

About

Minimal ROS-free robotics framework in Rust — typed pub/sub, sim clock, record/replay, SHM + TCP. No DDS.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages