A compact Lisp runtime with an integrated Behaviour Tree engine, bounded-time planning, and async vision-language-action orchestration.
muslisp is the executable. muesli-bt is the runtime and project.
On the shared benchmark subset under matched conditions, muesli-bt shows lower tick latency and faster BT compilation and instantiation than BehaviorTree.CPP 4.9.0.
If you are new to the repository, start with Getting Started and Examples Overview.
When control loops need to stay responsive, three things matter:
- decision logic that stays readable (
bt/defbtin Lisp) - compute-heavy reasoning that respects strict tick budgets (
plan-action/planner.plan) - asynchronous model calls that can stream and be cancelled (
vla.submit/vla.poll/vla.cancel)
muesli-bt keeps those concerns in one place with explicit runtime semantics and built-in observability.
- 2.59-2.75x lower per-node traversal cost on the shared 255-node traversal benchmarks
- 2.27x lower worst-case reactive interruption latency on the shared reactive benchmark
- approx 10x faster single-instance creation, and around 35x faster batched instantiation, on node instantiation benchmarks
See bench/README.md for the harness, scope, and comparison workflow.
Guarantees:
- Tick semantics are explicit and auditable (
tick_begin/tick_end, ordered node events, stable tick index progression). - Budget and deadline handling is enforced at decision points with observable overrun/cancellation events.
- Runtime observability uses one canonical external event stream:
mbt.evt.v1JSONL withcontract_version.
Host obligations:
- The host owns time integration, sensor/actuator I/O, and safety fallback policy.
- The host owns timeout policy and provides planner/VLA capability backends.
Authoritative contract artefacts:
- runtime contract v1
muesli-studiointegration contract: docs/contracts/muesli-studio-integration.md- canonical event schema (
mbt.evt.v1) - deterministic fixtures and fixture bundles
Validation tools:
- schema validation:
tools/validate_log.py - trace-level validation and replay comparison:
tools/validate_trace.py
Conformance is layered so reviewers can separate core runtime semantics from heavier integration checks:
L0: core-only runtime contract checks (fast, deterministic, PR-safe)L1: simulator integration conformance (PyBullet/Webots, push/PR CI gate)L2: ROS 2 conformance (rosbag-driven, push/PR CI gate on Ubuntu 22.04 + Humble)- Generic
env.*contract coverage inmuslisp_testsincludes both PyBullet and ROS2 backends.
Runbook and checklist: conformance levels. Validation overview: contracts index.
OS notes:
- macOS:
brew install cmake ninja python@3.11 - Ubuntu/Debian:
sudo apt update && sudo apt install -y cmake ninja-build g++ python3 python3-venv - Core/no-ROS builds are supported on both Linux and macOS. ROS2 stays optional and Linux-only when enabled.
- For docs +
pybullet, useuvwith Python3.11.
Build:
cmake --preset dev
cmake --build --preset dev -jOptional benchmark harness: see bench/README.md for build, run and comparison instructions.
Run the visual PyBullet racecar demo:
make demo-setup
make demo-run MODE=bt_plannerRun the PyBullet e-puck-style differential-drive flagship demo:
make demo-setup
PYTHONPATH=build/dev/python \
.venv-py311/bin/python examples/pybullet_epuck_goal/run_demo.py --headlessGuide: PyBullet: e-puck-style goal seeking
Shared flagship guides:
Verify install (single command; writes + validates canonical event log):
make verify-installAdditional runnable commands:
./build/dev/muslisp examples/bt/hello_bt.lisp
./build/dev/muslisp examples/repl_scripts/planner-bt-1d.lisp
./build/dev/muslisp examples/repl_scripts/a-star-grid.lisp
./build/dev/muslisp examples/repl_scripts/dijkstra-grid-pq.lisp
./build/dev/muslisp examples/repl_scripts/prm-2d-pq.lisp
./build/dev/muslisp examples/repl_scripts/vla-stub-bt.lisp
./build/dev/muslispOn interactive Linux and macOS terminals, muslisp now uses a small vendored line editor for current-line editing, history, and wrapped multi-line input. Persistent history is stored at ~/.muesli_bt_history, and :clear drops a pending multi-line buffer without leaving the REPL.
The current v0.5.x baseline keeps the transport surface intentionally narrow and uses one shared wheeled BT across the main backend paths:
- shared flagship BT reused across Webots, PyBullet, and ROS2
- strict host boundary: backend wrappers own observation shaping and command mapping
- supported ROS host: Ubuntu 22.04 + ROS 2 Humble
- supported ROS transport:
nav_msgs/msg/Odometryin,geometry_msgs/msg/Twistout - scripted cross-transport comparison and same-robot strict comparison checks
- canonical logging through
mbt.evt.v1
The ROS2 part of that baseline remains intentionally narrow:
- supported host: Ubuntu 22.04 + ROS 2 Humble
- attach path:
(env.attach "ros2") - transport:
nav_msgs/msg/Odometryin,geometry_msgs/msg/Twistout - package export:
muesli_bt::integration_ros2 - runner:
muslisp_ros2 - reset policy: live runs use
reset_mode="unsupported";stubremains for tests and harnesses - canonical logging path:
event_log_pathwrites directmbt.evt.v1JSONL toevents.jsonl-style artefact paths - canonical lifecycle summary: long runs emit
episode_begin,episode_end, andrun_end - ROS timing metadata:
run_start.data.capabilitiesrecordstime_source,use_sim_time, andobs_timestamp_source
Release artefact posture:
ubuntu-x86_64archive: generic non-ROS buildubuntu-22.04-ros2-humble-x86_64archive: ROS-enabled build withmuesli_bt::integration_ros2andmuslisp_ros2- the ROS-enabled archive requires a matching ROS 2 Humble runtime on the target host
Start with the ROS2 tutorial for the supported end-to-end build, run, and canonical log validation flow, then use docs/integration/ros2-backend-scope.md and docs/contracts/conformance.md for the backend boundary and conformance lanes.
For the flagship comparison story itself, use:
- cross-transport flagship comparison
- same-robot strict comparison
- PyBullet: e-puck-style goal seeking
- Isaac Sim: TurtleBot3 ROS2 demo
muesli-bt installs a CMake package for runtime consumers and tooling such as muesli-studio. Start with the runtime contract, the tooling integration profile, and the package consumption guide.
find_package(muesli_bt CONFIG REQUIRED)
add_executable(mbt_inspector ...)
target_link_libraries(mbt_inspector PRIVATE muesli_bt::runtime)muesli_btConfig.cmake also defines muesli_bt_SHARE_DIR, which points to the installed contract and schema assets under ${prefix}/share/muesli_bt.
Optional integration targets may also be exported when enabled at build time:
muesli_bt::integration_pybulletmuesli_bt::integration_webotsmuesli_bt::integration_ros2
The detailed target matrix, contract assets, and consumer guidance live in:
- docs/getting-started-consume.md
- docs/contracts/README.md
- docs/contracts/muesli-studio-integration.md
Use one Python version for docs tooling and pybullet: Python 3.11.
Fast path:
./scripts/setup-python-env.shuv run python --version
uv venv --python 3.11 .venv-py311
uv pip install --python .venv-py311/bin/python -r docs/requirements.txt
CFLAGS='-Dfdopen=fdopen' CPPFLAGS='-Dfdopen=fdopen' \
uv pip install --python .venv-py311/bin/python pybulletThen use that same environment for docs:
.venv-py311/bin/python -m mkdocs servemake demo-setup: installs pinned demo deps and buildsmuesli_bt_bridge.make demo-run MODE=bt_planner: runs the racecar demo through the bridge/runtime path.PYTHONPATH=build/dev/python .venv-py311/bin/python examples/pybullet_epuck_goal/run_demo.py --headless: runs the e-puck-style PyBullet flagship path. Guide:docs/examples/pybullet-epuck-goal.mdmake verify-install: runs a deterministic BT smoke run, writeslogs/verify-install.mbt.evt.v1.jsonl, and validates it againstmbt.evt.v1.
The benchmark harness is optional and separated from the main runtime tree under bench/.
Current first-milestone coverage:
A1single-leaf baselineA2scheduler jitter traceB1static tick overheadB2reactive interruptionB5parse, compile, load, and instantiate costB6logging overhead
Use the bench-release preset for meaningful numbers, then run the generated bench executable from build/bench-release/bench/.
For a Linux/NVIDIA humanoid example that keeps the current ROS2 thin adaptor intact, use the checked-in H1 locomotion demo:
- Lisp demo assets:
examples/isaac_h1_ros2_demo/ - container/tooling helpers:
tools/docker/isaac_lab_vla_stack/ - docs page:
docs/examples/isaac-h1-ros2-demo.md
This example keeps muesli-bt on the Odometry -> Twist ROS2 surface while Isaac Sim and NVIDIA's H1 controller handle the low-level humanoid policy loop.
For a wheeled Isaac Sim example, use a small differential-drive robot with the same ROS 2 boundary:
- input:
nav_msgs/msg/Odometry - output:
geometry_msgs/msg/Twist - runtime entrypoint:
examples/repl_scripts/ros2-flagship-goal.lisp - docs page:
docs/examples/isaac-wheeled-ros2-showcase.md - example guide: Isaac Sim: TurtleBot3 ROS2 Demo
This example gives you a simple path to run TurtleBot3 in Isaac Sim, keep the ROS 2 interface easy to understand, and record a repeatable simulator run.
VLA support in this repository is currently stub integration + contract hooks.
What is real today:
- async interface surface (
vla.submit,vla.poll,vla.cancel) - cancellation lifecycle behaviour in BT nodes (
vla-request,vla-wait,vla-cancel) - canonical runtime logging for VLA lifecycle events (
mbt.evt.v1)
What is placeholder today:
- production model transport/auth/provider integration (implemented by host backends, not by
muslispitself)
This pattern keeps ticking while a VLA job runs, uses planner output when available, and falls back safely:
(defbt hybrid
(sel
(seq
(cond goal-reached-1d state 1.0 0.05)
(succeed))
(seq
(vla-wait :name "vision" :job_key vla-job :action_key action)
(act apply-planned-1d state action state)
(succeed))
(seq
(plan-action :name "planner"
:planner "mcts"
:state_key state
:action_key action
:budget_ms 10
:work_max 300)
(act apply-planned-1d state action state)
(succeed))
(seq
(vla-request :name "vision"
:job_key vla-job
:instruction_key instruction
:state_key state
:deadline_ms 25
:dims 1
:bound_lo -1.0
:bound_hi 1.0)
(act bb-put-float action 0.0)
(running))))- small Lisp core with closures and lexical scoping
- non-moving mark/sweep GC
- mutable
vecandmapcontainers - deterministic RNG (
rng.make,rng.uniform,rng.normal,rng.int) - numeric helpers (
sqrt,log,exp,atan2,abs,clamp) - monotonic time (
time.now-ms) - JSON built-ins (
json.encode,json.decode)
-
composites:
- memoryless:
seq,sel - memoryful:
mem-seq,mem-sel - yielding/reactive:
async-seq,reactive-seq,reactive-sel
- memoryless:
-
decorators and leaves:
invert,repeat,retry,cond,act,succeed,fail,running -
compile/load/save paths (
bt.compile,bt.to-dsl,bt.save,bt.load,bt.save-dsl,bt.load-dsl) -
per-instance blackboard with typed values and write metadata
-
scheduler-backed async actions and reactive pre-emption tracing (
node_preempt,node_halt)
plan-actionnode for strict per-tick budgeted planningplanner.planservice for direct scripted experiments- backend selection across MCTS, MPPI, and iLQR
- structured planner stats and logs
- capability registry (
cap.list,cap.describe) - async job API (
vla.submit,vla.poll,vla.cancel) - stream-aware polling (
:queued,:running,:streaming,:done,:error,:timeout,:cancelled) - opaque media handles (
image_handle,blob_handle) plus metadata accessors - structured per-job logs (JSONL sink + in-memory dump)
-
canonical backend-agnostic control surface:
env.info,env.attach,env.configureenv.reset,env.observe,env.act,env.stepenv.run-loop,env.debug-draw
-
backends are registered through explicit integration extensions (no legacy backend-specific Lisp namespaces)
- trace/log rings and dump APIs
- per-tree and per-node profiling stats
- scheduler stats and tick budget controls
- Getting oriented
- Terminology
- Getting started
- Examples overview
- Example: A* search
- Tutorial: A* (step by step)
- Example: Dijkstra with PQ
- Tutorial: Dijkstra (step by step)
- Example: PRM with PQ
- Tutorial: PRM (step by step)
- Language manual
- Built-ins overview (
env.*included) - Language reference index
- Behaviour trees
- Planning overview
planner.planrequest/resultplan-actionnode integration- Integration overview
env.*integration API- ROS2 tutorial
- VLA integration
- VLA nodes reference
- VLA request/response schema
- Observability
- Changelog
- Release Notes
- TODO
- Roadmap to 1.0
- Roadmap
Serve docs locally:
.venv-py311/bin/python -m mkdocs serveRun the suite:
ctest --test-dir build/dev --output-on-failure-
src/,include/: core runtime, evaluator, BT engine, planner, VLA integration -
integrations/: optional backend integrations (for exampleintegrations/pybullet/,integrations/webots/,integrations/ros2/) -
examples/bt/: compact BT scripts -
examples/repl_scripts/: end-to-end experiments and demos -
examples/pybullet_racecar/: racecar demo package -
examples/pybullet_epuck_goal/: differential-drive PyBullet surrogate for the shared wheeled flagship BT -
examples/pybullet_racecar/native/: demo bridge entrypoint that uses canonicalenv.api.v1via the PyBullet integration -
tests/: unit/integration coverage -
docs/: user and internals documentation
Contributions are welcome.
If you add or change a feature, update:
- user docs in
docs/ - feature reference pages under
docs/language/reference/ docs/docs-coverage.mdto keep reference coverage complete