Skip to content
Robot-Exp-PlatformPublic

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

RsBullet

RsBullet brings Bullet physics to Rust: load robot models, create bodies, step a simulation, and inspect or control their state. Use it for simulation and algorithm development, or when you want a simulated robot to expose the same roplat_behavior interfaces used by the driver ecosystem.

It builds the native Bullet engine; a Python interpreter or an installed pybullet package is not required. This repository continues the RuBullet project. The original documentation is preserved in README_ORIGIN.md.

For this checkout's finite stepping, native Panda example and optional headless build, start with source build and deployment. The registry example below is retained for the older published API; it does not install the changes on this branch.

Choose the right layer

Crate Current source Registry example below Responsibility
rsbullet 0.7.0 0.4.0 RsBullet, typed robot builders, queued control, and an optional roplat simulation rhythm. Re-exports the core API.
rsbullet-core 0.7.0 0.4.0 PhysicsClient: explicit Bullet commands, body/joint IDs, geometry, model loading, and state queries.
rsbullet_sys 0.3.3 0.3.2 Builds the bundled Bullet C++ source and exposes its raw C FFI.

Most applications should depend on rsbullet. The layers keep native build details below the command API, while the higher layer manages robot handles and control callbacks. You can still access sim.client when you need a Bullet operation that has no higher-level wrapper.

RsBullet owns the client and the simulation clock. A robot's enqueue or control_with call submits work; it does not run the physics engine. Each sim.step() drains queued work, runs active callbacks, then advances Bullet once. Callback completion removes that callback. This makes ownership and stepping explicit, but the application must keep stepping while queued work is active. A simulation step is not a guarantee of a real-time deadline or of matching a physical robot's behavior.

A graph written for a real arm expects the driver's blocking ControlWith session instead. Wrap the world and one robot in RsBulletDevice: each control cycle observes the robot, calls the controller, applies the command and steps the world once, so the same ControlRhythm or AsyncControlRhythm runs on a real driver and in simulation. Sessions keep to the model's declared limits, joint speed included, and a joint MoveTo returns once every joint is within ARRIVAL_WINDOW of its target. The model type implements DeviceModel, whose CONTROL_PERIOD (default 1/240 s) the device reports as Robot::CONTROL_PERIOD. RsBullet::clock() exposes the simulated time as a roplat_msgs clock for nodes that stamp or wait on it.

Prerequisites

Use Rust nightly: roplat_behavior, a normal dependency, uses unstable Rust features. The native build also needs CMake and a C++ compiler:

  • Windows: the MSVC C++ build tools, Windows SDK, and CMake on PATH.
  • Linux: C/C++ tools, CMake, OpenGL/GLU, and X11 development libraries. On Ubuntu, a starting set is build-essential cmake libgl1-mesa-dev libglu1-mesa-dev libx11-dev libxi-dev.
  • macOS: Xcode Command Line Tools and CMake; the build links the Cocoa and OpenGL frameworks.

On the current source branch, the default gui feature preserves the graphics-enabled build. For a CPU-only server, use default-features = false (or --no-default-features for this workspace). That builds Bullet DIRECT, URDF loading, IK, contacts and TinyRenderer without the OpenGL/X11 libraries. roplat_trajectory also needs Fontconfig development files on Linux through its plotting dependency. GUI modes require the gui feature and an available graphical session. This feature split is not available in the registry 0.4.0 example below; see source build and deployment.

First simulation: a falling sphere, with no model files

Create a binary project and replace its files as below:

cargo new rsbullet-demo
cd rsbullet-demo

Cargo.toml:

[package]
name = "rsbullet-demo"
version = "0.1.0"
edition = "2024"

[dependencies]
rsbullet = "0.4.0"
robot_behavior = "0.6.1"
nalgebra = "0.34"
anyhow = "1"

src/main.rs:

use std::time::Duration;

use nalgebra::Isometry3;
use robot_behavior::PhysicsEngine;
use rsbullet::{
    CollisionGeometry, CollisionId, CollisionShapeOptions, Mode,
    MultiBodyBase, MultiBodyCreateOptions, RsBullet,
};

fn main() -> anyhow::Result<()> {
    let mut sim = RsBullet::new(Mode::Direct)?;
    sim.set_step_time(Duration::from_secs_f64(1.0 / 240.0))?
        .set_gravity([0.0, 0.0, -9.81])?;

    let sphere = sim.client.create_collision_shape(
        &CollisionGeometry::Sphere { radius: 0.1 },
        Some(CollisionShapeOptions::default()),
    )?;
    let body = sim.client.create_multi_body(&MultiBodyCreateOptions {
        base: MultiBodyBase {
            mass: 1.0,
            pose: Isometry3::translation(0.0, 0.0, 2.0),
            collision_shape: CollisionId(sphere),
            ..Default::default()
        },
        ..Default::default()
    })?;

    // Advance 0.5 seconds of simulated time. No wall-clock sleep is needed.
    for _ in 0..120 {
        sim.step()?;
    }
    let pose = sim.client.get_base_position_and_orientation(body)?;
    println!("Sphere height after 120 steps: {:.3}", pose.translation.z);
    sim.shutdown();
    Ok(())
}

Run it without exporting bundled example assets:

# Linux/macOS
BULLET_SKIP_ASSET_EXPORT=1 cargo +nightly run
# Windows PowerShell
$env:BULLET_SKIP_ASSET_EXPORT = "1"
cargo +nightly run

The program prints a height below the initial 2.0 and exits. It creates its collision geometry in code, so it needs no URDF, meshes, floor, Python, or GUI. The example uses meters, kilograms, seconds, and downward gravity along negative Z. Keep your model scale, masses, gravity, and timestep consistent; these values are passed to Bullet rather than automatically converted.

Loading your own robot

Supply a URDF and every mesh it references. Implement robot_behavior::RobotDescription with URDF = Some("your_robot.urdf"), add the model's directory with sim.add_search_path(...), then call sim.robot_builder::<YourRobot>("robot").base_fixed(true).load()?. Import the AddSearchPath, AddRobot, and EntityBuilder traits for these methods.

The builder returns RsBulletRobot<YourRobot>. Its body_id, joint_indices, and joint_names connect the typed handle to Bullet. The joint list selects revolute and prismatic joints; extra behavior traits on your robot description are needed for the corresponding typed motion APIs. For lower-level model loading or per-joint commands, use PhysicsClient directly. Poses use nalgebra::Isometry3<f64>; distinguish world/link poses from a joint's relative coordinate when interpreting state.

The crates.io package includes the native engine source, not the full collection of demonstration models. Some repository examples refer to local asset paths and must be adapted. To obtain Bullet's example models explicitly:

git clone --depth 1 https://github.com/bulletphysics/bullet3.git

Point set_additional_search_path at that checkout's examples/pybullet/gym/pybullet_data directory and keep its meshes beside the URDFs. For a reproducible experiment, pin the model repository revision too. BULLET_SKIP_ASSET_EXPORT=1 disables build-time asset copying; without it, the build may populate the user's Bullet data directory when that directory does not already exist.

Simulated sensors

sim.add_force_torque_sensor(&robot, joint_index, frame) enables Bullet's sensor on one joint of a loaded robot and returns an RsBulletForceTorque, which implements roplat_behavior::ForceTorqueSensor like a real sensor driver. After every sim.step() it samples the wrench the parent exerts on the joint's child link, at the joint origin and in the child link's frame, stamped with simulation time (clock::SIMULATION, also available as sim.simulated_stamp()), the given frame, and a sequence number. read_wrench returns the latest sample (an error before the first step or after a reset); bias_wrench takes the latest sample as zero. Bullet itself reports this wrench at the child's centre of mass; the sensor converts it without changing the engine-wide joint feedback mode, so other force_torque readings from get_joint_states are unaffected.

sim.add_imu(&robot, link_index, frame) (-1 for the base) returns an RsBulletImu implementing roplat_behavior::ImuSensor, sampled the same way at the link's URDF frame: orientation in the world, angular velocity in the link frame, and linear acceleration as an accelerometer's specific force Rᵀ(a − g) with the engine's current gravity (a link at rest reads +9.81 m/s² up, a falling one about zero). Acceleration is differenced between steps, so the first sample leaves it out of the Imu mask.

sim.camera(spec) renders a CameraSpec with Bullet's software renderer behind roplat_behavior::Camera; sim.render_rgbd(&spec) renders one frame. Intrinsics give the focal lengths, principal point and distortion (Distortion::PlumbBob or RationalPolynomial), and the image is the one spec.info() describes. Depth is the optical-axis z in metres, infinite where it is missing, never a finite number beyond far; DepthNoise adds seeded, reproducible Gaussian noise and dropped pixels. Native simulation gives the details.

device.with_gripper(["panda_finger_joint1", "panda_finger_joint2"]) drives two prismatic finger joints of an RsBulletDevice's body as a roplat_behavior::Gripper, coupled by a gear constraint so they stay mirrored like a real hand's. move_gripper steps the world until the fingers rest: a zero-force move goes to the width and fails if something stops it short, while a grasp closes with the given force and reports in read_gripper().grasped whether the fingers stopped within 0.01 m of the width. Native simulation gives the full semantics.

Optional integration and source builds

Enable the roplat feature to expose SimRhythm; ordinary simulator use does not need roplat. On the current Git preparation branch (rsbullet / rsbullet-core 0.7.0, roplat 0.6.0-rc.1; not yet published), the default feature is gui. SimRhythm is a compatibility alias for roplat_behavior::roplat::SimulationRhythm<RsBullet>; the shared scheduler owns its engine and declares Resources = (). Other simulators can use the same scheduler without depending on Bullet. Use --no-default-features --features roplat for a headless roplat application. These source contracts are not part of the registry 0.4.0 example above. Tokio is needed by the optional scheduler and development examples; ordinary RsBullet use does not add it. The enet and clsocket features enable their matching native transports. Backend flags such as dart, physx, mujoco, and grpc do not by themselves install or validate those external backends; in particular, mujoco is not a maintained standalone MuJoCo integration.

For a source checkout, initialize the native source before building:

git clone https://github.com/Robot-Exp-Platform/rsbullet.git
cd rsbullet
git submodule update --init --recursive rsbullet-sys/bullet3

The current source uses sibling roplat and roplat_behavior workspaces via explicit Cargo patches. These new package versions are not published; follow source build and deployment and retain the exact checkout revisions and lockfile. The registry example above selects the older released API.

For this branch's finite simulation and headless support, follow source build and deployment, including the native submodule and exact dependency revisions. A GitHub source ZIP does not include the Bullet submodule. The headless Dockerfile contains a build environment, not the robot models or private dependency credentials.

Next steps

License

The Rust crates are distributed under the MIT license. The vendored Bullet source retains its own upstream license; see Bullet's license. Imported robot models may have separate licenses.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages