A Runtime-Recompiling Constraint Compiler for Real-Time Robotic Motion Planning & Safety Validation
Cudro is a lightweight, domain-specific compiler designed for robotics motion planning and real-time safety filtering. It takes declarative robot kinematic descriptions and task-space manifold constraints (.cudro specifications), lowers them to an inlined Expression DAG, differentiates them via forward-mode dual numbers, and generates specialized C kernels compiled just-in-time (JIT) at runtime in < 15 ms via libtcc.
State-of-the-art vectorized motion planners (e.g. McVAMP, IROS 2026) use ahead-of-time (AOT) tracing compilers to generate loop-unrolled SIMD kernels for constraint projection. However, AOT compilers require offline recompilation and relinking whenever a constraint or robot geometry changes.
Cudro closes this AOT → Runtime gap:
- Dynamic Task Adaptation: Accept newly perceived constraints (e.g., table height changes, new tool lengths, dynamic keep-out zones) on the fly.
- Instant Specialization: Compile specialized machine code directly in memory in milliseconds without restarting or rebuilding the host process.
- Manifold Projection: Damped Levenberg-Marquardt (LM) iterative solver inside the generated kernel projects unconstrained configurations onto safe task manifolds at high frequencies.
- Safety Layer for Vision-Language-Action (VLA) Models: Project noisy neural policy action proposals onto certified constraint manifolds at 100–1000 Hz.
Cudro is structured as a classical, clean three-part compiler pipeline:
[ .cudro Spec File ]
│
┌──────────────────▼──────────────────┐
│ 1. Front-End: Lexer & Parser │
│ - Tokenizer with caret error UI │
│ - Recursive descent parser │
└──────────────────┬──────────────────┘
│ AST
┌──────────────────▼──────────────────┐
│ 2. Semantic Analysis (Sema) │
│ - Kinematic tree validation │
│ - Symbol & reference resolution │
└──────────────────┬──────────────────┘
│ Validated AST
┌──────────────────▼──────────────────┐
│ 3. Lowering & Desugaring │
│ - FK inlining (Rodrigues rot) │
│ - Constant folding │
└──────────────────┬──────────────────┘
│ Expression DAG
┌──────────────────▼──────────────────┐
│ 4. Automatic Differentiation (AD) │
│ - Forward-mode dual numbers │
│ - Jacobian ∂g/∂q calculation │
└──────────────────┬──────────────────┘
│
┌──────────────────▼──────────────────┐
│ 5. Code Generation & JIT Runtime │
│ - Scalar & Batched Emitters │
│ - Levenberg-Marquardt solver │
│ - In-memory libtcc compilation │
└──────────────────┬──────────────────┘
│
Executable Function Pointers:
project(), evaluate_constraints(), project_batch()
- C++ Compiler: GCC 13+ or Clang (C++20 support required)
- Build System: CMake 3.16+
- JIT Library:
tcc/libtcc-dev - Linear Algebra:
Eigen3(libeigen3-devfor reference testing)
On Ubuntu / Debian:
sudo apt-get update
sudo apt-get install -y cmake g++ libtcc-dev tcc libeigen3-devgit clone <repo-url>
cd Cudro
# Configure & build
cmake -B build -S .
cmake --build buildCudro comes with a complete suite of 10 test suites guarded by AddressSanitizer (ASan) and UndefinedBehaviorSanitizer (UBSan):
ctest --test-dir build --output-on-failuresmoke: Toolchain and scaffold verificationlexer: Tokenization and syntax error recoveryparser: AST node creation, grammar validation, and panic-mode recoverysema: Duplicate detection, reference resolution, kinematic tree cycle checksdag: DAG IR builder, constant folding, inlined FK loweringad: Forward-mode dual numbers and analytical Jacobianskernels: Scalar and batched multi-configuration JIT execution and LM projectionreference: Differential validation against independent Eigen reference models across 10,000 configurationsplanner: Constrained motion planning (C-RRT-Connect) validating continuous manifold trajectory generation on multi-robot modelsfuzz: Fuzz-lite crash-freedom test under random byte streams
The cudro binary exposes every stage of the compiler pipeline:
# 1. Check syntax and semantic validity
./build/cudro --check spec/panda7.cudro
# 2. Inspect token stream
./build/cudro --dump-tokens spec/panda7.cudro
# 3. View Abstract Syntax Tree (AST)
./build/cudro --dump-ast spec/panda7.cudro
# 4. Inspect lowered Expression DAG
./build/cudro --dump-dag spec/panda7.cudro
# 5. Inspect computed analytical Jacobians
./build/cudro --dump-jacobian spec/panda7.cudro
# 6. Emit generated C source
./build/cudro --emit-c spec/panda7.cudro
# 7. Run in-memory JIT compile and evaluate manifold projection
./build/cudro --jit-run spec/panda7.cudro
# 8. Benchmark batched multi-configuration projection throughput
./build/cudro --jit-bench spec/planar2r.cudro
# 9. Plan a constraint-satisfying trajectory via in-kernel JIT solver
./build/cudro --plan spec/panda7.cudrorobot panda7 {
joint j1 { type revolute; axis [0,0,1]; origin [0,0,0.333]; limits [-2.8973, 2.8973]; }
joint j2 { type revolute; axis [0,1,0]; origin [0,0,0]; limits [-1.7628, 1.7628]; }
joint j3 { type revolute; axis [0,0,1]; origin [0,-0.316,0]; limits [-2.8973, 2.8973]; }
joint j4 { type revolute; axis [0,1,0]; origin [0,0,0.0825]; }
joint j5 { type revolute; axis [0,0,1]; origin [0,0.384,0]; limits [-2.8973, 2.8973]; }
joint j6 { type revolute; axis [0,1,0]; origin [0,0,0]; }
joint j7 { type revolute; axis [0,0,1]; origin [0,0.107,0]; limits [-2.8973, 2.8973]; }
link base { spheres [[0,0,0.06, 0.07]]; parent world; joint_ref j1; }
link arm1 { spheres [[0,0,0.15, 0.06]]; parent base; joint_ref j2; }
link arm2 { spheres [[0,0,0.12, 0.05]]; parent arm1; joint_ref j3; }
link forearm { spheres [[0,0,0.18, 0.048]]; parent arm2; joint_ref j5; }
link ee { spheres [[0,0,0.02, 0.03]]; parent forearm; joint_ref j7; }
}
task cup_on_table {
link ee;
plane { point_on_link [0,0,0.02]; normal [0,0,1]; offset 0.02; }
}
clearance { min_distance 0.03; }
You can use Cudro directly as an in-process library (cudro_core):
#include <cudro/lexer.hpp>
#include <cudro/parser.hpp>
#include <cudro/sema.hpp>
#include <cudro/lower.hpp>
#include <cudro/codegen_c.hpp>
#include <cudro/jit_tcc.hpp>
// 1. Parse & Check
cudro::DiagnosticBag diags;
cudro::Lexer lexer("spec.cudro", spec_source_string, diags);
auto tokens = lexer.tokenize();
cudro::Parser parser(tokens, diags);
auto spec = parser.parse();
cudro::Sema sema(spec, diags);
if (!sema.analyze()) {
cudro::print_diagnostics(diags, spec_source_string);
return;
}
// 2. Lower to Expression DAG
cudro::ExprDAG dag;
auto constraint_outputs = cudro::lower(spec, dag);
cudro::LowerResult lr;
lr.dag = std::move(dag);
lr.constraint_outputs = std::move(constraint_outputs);
lr.num_inputs = lr.dag.num_inputs();
// 3. Generate C code
std::string c_code = cudro::generate_scalar_c(lr);
// 4. JIT Compile in Memory (< 15 ms)
auto mod = cudro::TCCJIT::compile(c_code);
auto project_fn = mod.get_symbol<void(*)(const float*, int, float*)>("project");
auto eval_fn = mod.get_symbol<void(*)(const float*, int, float*)>("evaluate_constraints");
// 5. Execute in real-time control loop
std::vector<float> q_init = {0.2f, 0.3f};
std::vector<float> q_proj(lr.num_inputs);
project_fn(q_init.data(), lr.num_inputs, q_proj.data());| Metric | Measured Value (Post-Phase 2 Analytical Jacobians) | Improvement Factor |
|---|---|---|
| In-Memory JIT Compilation Latency | 5.1 – 9.5 ms | ~2.5× faster (from ~14 ms) |
| Batched Constraint Evaluation Throughput | 2,640,000 – 6,490,000 configs / sec | ~8× – 20× faster |
| Batched Manifold Projection Throughput | 85,920 – 120,000 full LM solves / sec | ~10× – 14× faster |
| Panda 7-DOF Projection Latency | 11.6 $\mu$s / solve (down from 16.7 $\mu$s) | Exact analytical gradient (zero truncation error) |
| Planar2R Lowered DAG Size | 25 nodes (down from 122) | 79.5% node reduction |
| Jacobian Codegen Strategy | Single-pass unified evaluator evaluate_dag(q, g, J) |
Eliminates |
| Differential Error vs Eigen Reference |
|
100% verified agreement (30,022 assertions) |
For a detailed file-by-file walkthrough of compiler concepts (lexer, recursive descent parsing, symbol tables, DAG lowering, dual-number AD, C emission, JIT compilation, and CUDA integration), see the Cudro Codebook.