Bring your own motion planner to CommonRoad scenarios
이 콘텐츠는 아직 번역되지 않았습니다.
By the end of this page your own motion planner solves any scenario drawtonomy exports, and you can see its answer, its verdict and where it went wrong, in the browser.
drawtonomy does not run your planner. Your planner stays where it already runs, in whatever language it is written in, and the two of you exchange files.
The contract
Section titled “The contract”scenario.xml (CommonRoad 2020a, exported from drawtonomy) │ ▼[your planner] ── required ──▶ solution.xml (CommonRoadSolution, commonroad-io) │ ├── optional ──▶ solution.planning-trace.json (drawtonomy_cr.trace.TraceWriter) │ ▼ (drawtonomy-cr post-processes)solution.verdict.json (drawtonomy-verdict/1, the official checker's 4 verdicts)The solution XML is the only required file. Replay and drawtonomy’s own collision check work without the other two. The verdict adds the official checker’s answer; the planning trace adds what your planner intended at each replanning cycle.
The three names follow one rule, and the CLI assumes it:
<solution stem>.verdict.json and <solution stem>.planning-trace.json.
0. Install
Section titled “0. Install”pip install "drawtonomy-commonroad[checker]"Python 3.11+. commonroad-io is the only hard dependency. The [checker]
extra pulls commonroad-drivability-checker, which needs Linux x86_64;
without it everything except verdict still works, and verdict exits with
code 3 and one line saying so.
One of the four checks needs a second package. boundary_collision
triangulates the road with triangle (Shewchuk’s Triangle), which is not
free for commercial use, so it is not a default dependency. Add the
[boundary] extra once you have read
its licence:
pip install "drawtonomy-commonroad[checker,boundary]"Without it nothing breaks: boundary_collision comes back as SKIP, not
FAIL, the other three checks still run, and the exit code is still 0.
[PASS] obstacle_collision[SKIP] boundary_collision (pip install triangle)[PASS] goal_reached[PASS] solution_feasibleThe sidecar records "status": "SKIP" with a message saying the check was
skipped, and drawtonomy leaves it out of the badge’s count: Checker PASS 3/3, not a red 3/4. See
SKIP.
1. Get a scenario
Section titled “1. Get a scenario”Any of the three starting points gives your planner the same kind of file.
| How | |
|---|---|
| Draw one | Draw lanes, place vehicles, set initial speeds, mark one vehicle as Ego (external control) and pick a goal lane. |
| Open an existing OpenSCENARIO file | Drop the .xosc and its .xodr on the canvas, or open it from GitHub with ?open=, then mark the ego and pick a goal lane. |
| Open a CommonRoad benchmark scenario | Drop the .xml on the canvas. The ego and the goal lane come with the file. |
The two prerequisites, with screenshots, are in Export a scenario.
2. Export CommonRoad XML
Section titled “2. Export CommonRoad XML”Export ▸ .xml (CommonRoad). Put the file in a folder of its own; that
folder is what the CLI will serve.
3. Write your planner
Section titled “3. Write your planner”Read the scenario with commonroad-io, solve it, write a
CommonRoadSolution next to it:
from commonroad.common.file_reader import CommonRoadFileReaderfrom commonroad.common.solution import CommonRoadSolutionWriter
scenario, planning_problem_set = CommonRoadFileReader("results/scenario.xml").open()solution = my_planner(scenario, planning_problem_set) # your codeCommonRoadSolutionWriter(solution).write_to_file( output_path="results/", filename="planner_solution.xml", overwrite=True)Pass filename yourself. Without it commonroad-io derives the name from the
solution’s identifiers (solution_KS2:JB1:ZAM_…:2020a.xml), which still works
because the CLI sniffs by content, not by name, but the sidecars are then
named after that stem too.
An example to copy
Section titled “An example to copy”Start from
examples/idm_planner/idm_planner.py:
one file that depends on nothing but commonroad-io and numpy. The ego
follows the centreline of the lanelet it starts on, and IDM car-following
sets its speed. It also takes --mode naive, which holds the initial speed
instead of planning; on the bundled cut-in scenario naive collides while
idm passes all four checks.
The file is split into two halves so you can tell them apart:
PLANNER-SPECIFIC is the planning, and DRAWTONOMY HAND-OFF at the bottom
is the whole contract. Replace the first half with yours and keep the second.
examples/reactive_planner/
wires commonroad-reactive-planner end to end the same way, including a
planning trace from a cyclic replanning loop.
What a drawtonomy scenario looks like to a planner
Section titled “What a drawtonomy scenario looks like to a planner”A scenario drawtonomy exported is valid CommonRoad, but it is shaped by what you drew rather than by a recorded drive. Four things differ from the benchmark corpus, and each of them can break a planner silently.
| What to do | |
|---|---|
Lanelets have no successor or predecessor unless one lane’s end actually meets the next lane’s start. Parallel through-lanes export with adjacentLeft / adjacentRight only. | Build the reference path from the initial lanelet’s centerline, which already spans the whole drawn lane, and follow successor only as an extension when it is there. A route built by walking successor comes out one lanelet long. |
| A straight lane is a 2-point polyline. Boundaries keep the point count you drew with; a 120 m straight is two vertices 120 m apart. | Project positions onto the segment between vertices, never onto the nearest vertex. Snapping to the nearest vertex on such a lanelet can be off by half the lanelet’s length, and the symptom is a planner that quietly never sees the vehicle in front of it. Lanelet.interpolate_position and the official route planner already work on segments. |
| The ego’s initial state is the planning problem, taken from the vehicle you marked and the initial speed you set. There is no ego shape in a CommonRoad planning problem. | Declare the body your planner planned with in the solution’s vehicle_type, and in a planning trace if you write one. |
| The goal is a lanelet, optionally a stretch of it and optionally with a speed interval. It is not a pose. | Read it from the planning problem’s goal, not from a waypoint. |
dt is 0.1 s. Time steps in the scenario, the solution and the verdict
sidecar are all integers counted from 0, so seconds are step × dt.
4. Get the verdict
Section titled “4. Get the verdict”drawtonomy-cr verdict scenario.xml solution.xml# writes solution.verdict.json next to the solutionIt runs the official checker’s four tests, prints one line each, and writes a
drawtonomy-verdict/1 sidecar:
[PASS] obstacle_collision[PASS] boundary_collision[PASS] goal_reached[PASS] solution_feasiblewrote results/planner_solution.verdict.jsonA failing check names the time steps it covers (and the obstacle, for a collision):
[FAIL] solution_feasible t=42..61A check that could not be judged is a [SKIP] with the next step in
brackets, not a FAIL:
[SKIP] boundary_collision (pip install triangle)Why it failed is in the sidecar’s message, and the app shows it on the
badge.
Exit codes: 0 when the sidecar was written (a FAIL verdict lives inside the JSON, it is not an error), 3 when the checker is not installed.
See Verdict reference for what the four checks mean and how the badge reads.
5. Open it and iterate
Section titled “5. Open it and iterate”drawtonomy-cr open ./resultsIt sniffs the folder for the four files by content (<commonRoad,
<CommonRoadSolution, drawtonomy-verdict/1,
drawtonomy-planning-trace-v1), computes the verdict if it is missing and
the checker is installed, serves the folder on 127.0.0.1, prints the URL
and opens it:
solution: planner_solution.xmlverdict: planner_solution.verdict.jsontrace: planner_solution.planning-trace.jsonserving /path/to/results at http://127.0.0.1:53101https://drawtonomy.com/?open=http%3A%2F%2F127.0.0.1%3A53101%2Fscenario.xml&solution=planner_solution.xml&verdict=planner_solution.verdict.json&trace=planner_solution.planning-trace.jsonOpen the URL in Chrome or Firefox (Safari blocks http://127.0.0.1 from an https page).Watching for changes. Press Ctrl+C to stop.It names every companion it found, one line each, and the URL carries the
ones it has: a folder with only a solution prints just the solution: line
and a URL with only &solution=. The serving line carries the folder’s own
absolute path, and the port is a free one unless you pass --port.
Without the checker installed, the verdict step says so once and the scenario still opens:
No verdict: the official checker is not installed, so the scenario opens without it (install with: pip install "drawtonomy-commonroad[checker]", Linux x86_64 only).The tab opens with the scene, your solution replaying as the ego, and the checker’s verdict on the run badge.
Rerun the planner and the tab follows
Section titled “Rerun the planner and the tab follows”Leave the CLI running and the tab open. Rerun your planner; the tab picks up the new solution, the new verdict and the new trace on its own. There is no refresh, no re-drop, and no dialog.
Two rules make that safe:
- The scenario is never reloaded. Only the files your planner writes are re-fetched. If you edit the scene, export again: the loop starts over from step 2.
- When only the solution changes, the old checker result is dropped until it is recomputed. A verdict the CLI computed itself is recomputed automatically after every solution change; a verdict file you wrote yourself is never touched, so the badge simply has no checker line until you update it.
When that second rule drops a verdict you wrote, the CLI says so on the line after the change, with the command to put it back:
changed: solutionverdict: planner_solution.verdict.json is now older than the solution; checker results are cleared in the tab until you recompute: drawtonomy-cr verdict scenario.xml planner_solution.xmlThe CLI also waits for a file’s size and mtime to hold still for 500 ms before serving it, so a half-written solution is never read.
Naive fails, IDM passes
Section titled “Naive fails, IDM passes”The example planner’s two modes show what the loop tells you. Same scene, same ego, one difference: whether the planner looks at the vehicle in front.
--mode naive | --mode idm | |
|---|---|---|
| Speed | holds the initial speed | slows to follow the leader |
obstacle_collision | FAIL, with the time range and the obstacle id | PASS |
| Badge | RUN FAIL, tooltip and toast Checker FAIL 1/4 | RUN PASS, tooltip and toast Checker PASS 4/4 |
| Timeline | a red ✗ marker at the collision second | no marker |
| Playback | runs to the end; the badge turns to FAIL once the head passes the collision | runs to the end, badge stays PASS |
| Planned trajectory | straight through the leader | bends and slows before it |
The number after the word counts that word: FAIL 1/4 means one of the four
judged checks failed, PASS 4/4 means all four passed. Each check that did
not pass gets its own line in the tooltip.
More than one solution in the folder
Section titled “More than one solution in the folder”The CLI never picks silently: it takes the first by name for each kind and
says what it ignored, one line each, before the solution: lines.
2 solution files found; using idm.xml (naive.xml ignored)2 verdict files found; using idm.verdict.json (naive.verdict.json ignored)2 trace files found; using idm.planning-trace.json (naive.planning-trace.json ignored)Name order, not modification time, so the pick is the same every run, and
because the three sidecars share the solution’s stem they line up on the
same variant. To choose the other one, name it: --solution naive.xml (and
--verdict / --trace likewise).
A verdict older than its solution is not served
Section titled “A verdict older than its solution is not served”A verdict judges the solution it was computed for. If the one in the folder is older than the solution next to it, the CLI does not serve it and says how to recompute it:
verdict: naive.verdict.json is older than the solution and is not shown. Recompute: drawtonomy-cr verdict scenario.xml naive.xmlThis applies to a verdict you wrote; one the CLI computed itself is simply recomputed.
It keeps watching <solution stem>.verdict.json either way, including
while no such file exists. A verdict written after open started (a CI job
finishing, for example) is picked up by the tab with no reload.
The change stream
Section titled “The change stream”The tab subscribes to GET /events on the CLI’s server
(text/event-stream). Useful if you want to check the connection before
wiring this into CI:
curl -N http://127.0.0.1:53101/events: connected
event: changeddata: {"files":["solution","verdict"],"names":{"solution":"planner_solution.xml","verdict":"planner_solution.verdict.json"}}files names the kinds that changed (solution, verdict, trace) and
names maps each to its path within the served folder. A kind appears only
when it is actually servable: a verdict that is missing, or older than its
solution, is left out of both. Comment lines (:) are the initial handshake
and a keepalive every 30 s.
Browsers
Section titled “Browsers”Chrome and Firefox only. Safari blocks http://127.0.0.1 requests from an
https:// page as mixed content, so the tab cannot read the folder. For
Safari, use the fallback:
drawtonomy-cr open ./results --copyThat prints the absolute paths of the files instead of serving them; drop them onto drawtonomy.com by hand.
| flag | |
|---|---|
--solution / --verdict / --trace | override the sniffed pick |
--port N | fixed port (default: a free one) |
--no-browser | print the URL without opening a browser |
--copy | print the file paths instead of serving them (the Safari fallback) |
--app-origin URL | the origin allowed to read the files (default https://drawtonomy.com) |
The server binds 127.0.0.1 only, answers GET / HEAD / OPTIONS only,
serves nothing outside the folder (.. and absolute paths give 404), and
names exactly one origin in Access-Control-Allow-Origin, never *.
- Verdict reference for the four official checks, the sidecar format and the badge.
- Planning trace for what the planner intended, not just what it drove.
- Export a scenario for the prerequisites in step 1.
Appendix: running the checker where it cannot be installed
Section titled “Appendix: running the checker where it cannot be installed”commonroad-drivability-checker publishes wheels for Linux x86_64 only. Run
the verdict step in a container instead. This Dockerfile is self-contained:
FROM --platform=linux/amd64 python:3.11-slimRUN apt-get update && apt-get install -y --no-install-recommends gcc libc6-dev \ && rm -rf /var/lib/apt/lists/*RUN pip install --no-cache-dir \ --only-binary=commonroad-drivability-checker,commonroad-clcs \ "drawtonomy-commonroad[checker,boundary]"WORKDIR /workdocker build --platform linux/amd64 -t cr-verdict .docker run --rm --platform linux/amd64 -v "$PWD:/work" cr-verdict \ drawtonomy-cr verdict /work/scenario.xml /work/solution.xmlThe verdict sidecar lands next to the solution through the mount, and a
running drawtonomy-cr open picks it up. Removing ,boundary from the pip
line makes boundary_collision report SKIP.