İçeriğe geç

Bring your own motion planner to CommonRoad scenarios

Bu içerik henüz dilinizde mevcut değil.

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.

One loop end to end: draw a car-following scene, export CommonRoad XML, run a naive planner and replay its FAIL to the end, switch to IDM, rerun, and watch the same tab reload the new solution and play it through to PASS.
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.

Terminal window
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:

Terminal window
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_feasible

The 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.

Any of the three starting points gives your planner the same kind of file.

How
Draw oneDraw lanes, place vehicles, set initial speeds, mark one vehicle as Ego (external control) and pick a goal lane.
Open an existing OpenSCENARIO fileDrop 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 scenarioDrop 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.

Export ▸ .xml (CommonRoad). Put the file in a folder of its own; that folder is what the CLI will serve.

Read the scenario with commonroad-io, solve it, write a CommonRoadSolution next to it:

from commonroad.common.file_reader import CommonRoadFileReader
from commonroad.common.solution import CommonRoadSolutionWriter
scenario, planning_problem_set = CommonRoadFileReader("results/scenario.xml").open()
solution = my_planner(scenario, planning_problem_set) # your code
CommonRoadSolutionWriter(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.

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.

Terminal window
drawtonomy-cr verdict scenario.xml solution.xml
# writes solution.verdict.json next to the solution

It 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_feasible
wrote results/planner_solution.verdict.json

A failing check names the time steps it covers (and the obstacle, for a collision):

[FAIL] solution_feasible t=42..61

A 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.

Terminal window
drawtonomy-cr open ./results

It 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.xml
verdict: planner_solution.verdict.json
trace: planner_solution.planning-trace.json
serving /path/to/results at http://127.0.0.1:53101
https://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.json
Open 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.

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: solution
verdict: 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.xml

The 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.

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
Speedholds the initial speedslows to follow the leader
obstacle_collisionFAIL, with the time range and the obstacle idPASS
BadgeRUN FAIL, tooltip and toast Checker FAIL 1/4RUN PASS, tooltip and toast Checker PASS 4/4
Timelinea red ✗ marker at the collision secondno marker
Playbackruns to the end; the badge turns to FAIL once the head passes the collisionruns to the end, badge stays PASS
Planned trajectorystraight through the leaderbends 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.

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.xml

This 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 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:

Terminal window
curl -N http://127.0.0.1:53101/events
: connected
event: changed
data: {"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.

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:

Terminal window
drawtonomy-cr open ./results --copy

That prints the absolute paths of the files instead of serving them; drop them onto drawtonomy.com by hand.

flag
--solution / --verdict / --traceoverride the sniffed pick
--port Nfixed port (default: a free one)
--no-browserprint the URL without opening a browser
--copyprint the file paths instead of serving them (the Safari fallback)
--app-origin URLthe 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 *.

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-slim
RUN 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 /work
Terminal window
docker 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.xml

The 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.