Skip to content

Python Judgment Script Development Guide

Version Note

The Lua judgment script is being gradually phased out. All future script development work will be carried out on the Python branch.

1. How Python Scripts Work

Python scripts run embedded inside the CybertronJudgeConsole (hereafter "Judge") program. Judge retrieves the per-frame state of all actors from the hot zone and uses it to evaluate conditions and behaviors — for example, whether a vehicle crosses a lane line or runs a red light. Judge can also pass actor states to the embedded Python program, enabling fully custom judgment logic.

2. Python Judgment Script Structure

A custom judgment script package uploaded to SimOne is a ZIP archive that must contain at least an interface.py file.

Packaging Note

When creating the ZIP archive, compress the files directly — do not compress the folder that contains them.

interface.py must provide the following three key functions:

def on_init(config_str, main_vehicle_id):
    """Called when the case starts. main_vehicle_id is the ID of the ego vehicle."""
    ...

def on_loop(pre_actors, cur_actors, cur_t):
    """Called every frame. pre_actors is the previous frame's state, cur_actors is the current frame's state, cur_t is the timestamp."""
    ...

def on_exit():
    """Called before the case ends."""
    ...
  • on_init(config_str, main_vehicle_id): Called when the case starts. main_vehicle_id is the ego vehicle ID. Must return True on successful initialization.
  • on_loop(pre_actors, cur_actors, cur_t): Called every frame during execution. pre_actors contains the state of all actors from the previous frame, cur_actors contains the current frame's actor states, and cur_t is the current timestamp. Real-time judgment logic should be placed here and evaluated frame by frame.
  • on_exit(): Called before the case ends.

Each actor represents a dynamic entity in the scene (vehicle, pedestrian, bicycle) and has attributes such as position, dimensions, and current lane. These can be inspected frame by frame inside on_loop.

3. on_loop Return Values

on_loop must return a (return_code, message) tuple:

Return Code Description
CheckResult.PENDING No judgment result yet (the event of interest has not occurred); message is empty
CheckResult.PENDING_WITH_REPORT An event of interest has occurred; message is a custom event description that will be broadcast externally
CheckResult.FAIL The event has occurred; the case ends immediately with a failure status; message is a custom event description
CheckResult.SUC The event has occurred; the case ends immediately with a success status; message is a custom event description

Named Aliases

The constants above are also accessible via the simone namespace — e.g. simone.PENDING, simone.FAIL, simone.SUCC — and are equivalent to the CheckResult.X form.

4. Standalone Debugging

It is recommended to develop and test Python scripts on the standalone version before uploading to the cloud (if cloud execution is required).

Launch StartAllDebug.bat from the installation package. After starting a case, the CybertronJudgeConsole window will appear, allowing you to print log output from your Python script and observe it in the console, or use pdb for live debugging.

To avoid the repetitive cycle of packaging, uploading, and testing the Python script, set the judgment name in the SimOne case editor to the following special string:

87da536b-c9ac-4567-8e9e-7213b1e01349-test-only

Then place your judgment code in C:\simone_scripts and edit it there. Each time a case runs, SimOne will use the code directly from this directory instead of from the ZIP archive, eliminating the need to repeatedly modify, package, and re-upload the script.

5. API Reference

simone Namespace

All methods and properties exposed by the judgment engine are under the simone namespace.

Method Return Type Description
log_info(string) Print an info message to the console and log file
log_error(string) Print an error message
log_warning(string) Print a warning message
get_left_lane(string) string Get the lane to the left of the given lane, e.g. simone.get_left_lane(lane_name)
get_right_lane(string) string Get the lane to the right of the given lane, e.g. simone.get_right_lane(lane_name)
get_traffic_light(int id) string Query the current state of a traffic light by its OpenDRIVE ID; returns one of 'yellow', 'red', 'green', or 'unknown'

actor Object

An actor represents a vehicle or pedestrian in the scene. Attributes are accessed by index and type.

Method Return Type Description
get_bool(index) bool Get a boolean attribute, e.g. actor.get_bool(simone.ActorAttr.STOPPED)
get_double(index) double Get a numeric attribute, e.g. actor.get_double(simone.ActorAttr.DIST_TO_LEFT_LANE_LINE)
get_vec3(index) vec3 Get a vec3 attribute, e.g. actor.get_vec3(simone.ActorAttr.POS)
get_int(index) int Get an integer attribute, e.g. actor.get_int(ActorAttr.LEADING_VEHICLE_ID)
get_string(index) string Get a string attribute, e.g. actor.get_string(simone.ActorAttr.CENTER_LANE_NAME)
get_id() int Get the vehicle ID
get_type() int Get the vehicle type (ego vehicle, opponent vehicle, bicycle, etc.)

vec3 type: Has .x, .y, and .z properties.

ActorAttr Attribute Indices

Use the corresponding getter method for each attribute type, e.g. actor.get_bool(simone.ActorAttr.STOPPED).

Attribute Index Type Description
TIME double Timestamp when the actor's attributes were retrieved
NAME string Actor name
POS vec3 Vehicle position
YAW double Vehicle yaw angle
SIZE_3D vec3 Vehicle dimensions
LONG_SPEED double Longitudinal speed
LAT_SPEED double Lateral speed
LONG_ACCEL double Longitudinal acceleration
LAT_ACCEL double Lateral acceleration
LEFT_LIGHT_ON bool Whether the left turn signal is on
RIGHT_LIGHT_ON bool Whether the right turn signal is on
WARNING_LIGHT_ON bool Whether the hazard lights are on
STOPPED bool Whether the vehicle is stopped
STOPPED_TIME double Duration the vehicle has been stopped
DIST_TO_LANE_LINE double Distance to the nearest lane line (minimum of left and right)
DIST_TO_LEFT_LANE_LINE double Distance to the left lane line
DIST_TO_RIGHT_LANE_LINE double Distance to the right lane line
DIST_TO_CENTER_LANE_LINE double Distance to the lane centerline
DIST_TO_STOPLINE_WHEN_STOP double Distance to the stop line when stopped (returns NaN while moving)
TTC double Time to collision (TTC)
LEADING_VEHICLE_DISTANCE double Distance to the leading vehicle
LEADING_VEHICLE_ID int ID of the leading vehicle
TOUCH_DASH_LANE_LINE bool Whether the vehicle is touching a dashed lane line
TOUCH_SOLID_LANE_LINE bool Whether the vehicle is touching a solid lane line
OUT_OF_ROAD bool Whether the vehicle has left the road
CENTER_LANE_NAME string Name of the lane where the vehicle's center point is located
WHOLE_LANE_NAME string Name of the lane that the entire vehicle occupies
THROTTLE double Throttle value (0–1)
STEERING double Steering wheel angle, negative = left, positive = right (degrees)
BRAKE double Brake value (0–1)
GEAR int Current gear; -1 = reverse
PRIVATE_VEHICLE_DIR vec3 Vehicle heading direction
PRIVATE_ACCEL_3D vec3 Vehicle acceleration (3D)
PRIVATE_SPEED_3D vec3 Vehicle velocity (3D)
PRIVATE_CENTER_TO_LEFT_LANE_LINE double Distance from the vehicle center to the left lane line
PRIVATE_CENTER_TO_RIGHT_LANE_LINE double Distance from the vehicle center to the right lane line
PRIVATE_TOUCH_LEFT_LANE_LINE bool Whether the vehicle is touching the left lane line
PRIVATE_TOUCH_RIGHT_LANE_LINE bool Whether the vehicle is touching the right lane line

hdmap Interface

Method Description
hdmap.get_all_parking_space_ids() Get all parking space IDs
hdmap.get_parking_space_by_id(id) Get information for a specific parking space; returns {"pts": [pt_a, pt_b, pt_c, pt_d], "road_id": "..."} (ab: left side, bc: rear, cd: right side, da: front)

ActorType

Constant Description
ActorType.MAIN_VEHICLE Ego vehicle
ActorType.PEDESTRIAN Pedestrian
ActorType.BICYCLE Two-wheeled vehicle
ActorType.STATIC_OBJECT Static object

6. Combining Multiple Judgments

Single Python Environment Limitation

Due to current architectural constraints, Judge has only one Python environment. If a case includes multiple Python judgments, the on_init, on_loop, and other functions in each interface.py will overwrite each other and will not work correctly.

Recommended approach: Include multiple sub-judgment Python files in a single judgment package — for example, touch_lane_line.py for lane-crossing detection and traffic_light.py for red-light detection — and have the entry point interface.py import and invoke them all. This satisfies multiple judgment requirements within a single Python environment.

7. Example: Collision Detection Script

The following example demonstrates how to detect a collision between an opponent vehicle and the ego vehicle using Shapely to compute polygon distances:

import json
from simone import *
import shapely
from shapely.geometry import Polygon

main_vehicle_id = -1
threshold_distance = 0.5  # Collision detection distance threshold (meters)

def get_polygon(actor):
    """Construct a rotated rectangular polygon from the actor's position, heading, and dimensions."""
    pos = actor.get_vec3(ActorAttr.POS)
    yaw = actor.get_double(ActorAttr.YAW)
    size = actor.get_vec3(ActorAttr.SIZE_3D)
    width, length = size.x, size.y

    rect = Polygon([
        (pos.x - length / 2, pos.y - width / 2),
        (pos.x + length / 2, pos.y - width / 2),
        (pos.x + length / 2, pos.y + width / 2),
        (pos.x - length / 2, pos.y + width / 2),
    ])
    return shapely.affinity.rotate(rect, yaw, origin='center', use_radians=True)

def on_init(config_str, main_v_id):
    global main_vehicle_id
    main_vehicle_id = main_v_id

    # Example: read parking space information from the map
    ids = hdmap.get_all_parking_space_ids()
    if len(ids) > 0:
        parking = hdmap.get_parking_space_by_id(ids[0])
        log_info(f'first parking slot info: {parking}')

    return True

def on_loop(pre_actors, cur_actors, cur_t):
    main_actor = cur_actors[main_vehicle_id]
    main_polygon = get_polygon(main_actor)

    for actor_id, actor in cur_actors.items():
        if actor.get_id() == main_vehicle_id:
            continue

        target_polygon = get_polygon(actor)
        leading_id = actor.get_int(ActorAttr.LEADING_VEHICLE_ID)
        min_distance = main_polygon.distance(target_polygon)

        if min_distance <= threshold_distance and leading_id == main_vehicle_id:
            return (CheckResult.PENDING_WITH_REPORT, 'crash with main vehicle')

    return (CheckResult.PENDING, '')

def on_exit():
    print('python script exit')

See Also

  • Lua Judgment Script Management — Steps for uploading and binding a judgment script package in the SimOne interface (the upload entry point is the same for Python script packages)