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_idis the ego vehicle ID. Must returnTrueon successful initialization.on_loop(pre_actors, cur_actors, cur_t): Called every frame during execution.pre_actorscontains the state of all actors from the previous frame,cur_actorscontains the current frame's actor states, andcur_tis 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)