Python 脚本判定开发指南¶
版本说明
Lua 判定脚本逐步不再维护,后续脚本支持开发工作都将在 Python 分支上进行。
1. Python 脚本原理¶
Python 脚本内嵌在 CybertronJudgeConsole(后简称 Judge)程序中运行,Judge 程序通过从热区获取到所有 actors 每帧的状态,完成一些状态或者动作的判定,比如车辆是否压线、是否闯红灯等。同时 Judge 也可以将这些 actors 的状态传递给内嵌的 Python 程序,让 Python 程序完成更多的自定义判定。
2. Python 判定脚本结构¶
在 SimOne 中上传使用的自定义脚本判定包,是一个至少包含了 interface.py 文件的 ZIP 压缩包。
打包注意
压缩 ZIP 包时,直接对文件压缩,不要对包含文件的目录压缩。
interface.py 需要提供以下 3 个关键函数:
def on_init(config_str, main_vehicle_id):
"""案例启动时调用。main_vehicle_id 为主车 ID。"""
...
def on_loop(pre_actors, cur_actors, cur_t):
"""每帧调用。pre_actors 为上一帧状态,cur_actors 为当前帧状态,cur_t 为时间戳。"""
...
def on_exit():
"""案例结束前调用。"""
...
on_init(config_str, main_vehicle_id):在案例启动时被调用。main_vehicle_id为主车 ID。初始化成功须返回True。on_loop(pre_actors, cur_actors, cur_t):在运行的每一帧被调用。pre_actors是上一帧所有 actors 的状态,cur_actors是当前帧所有 actors 的状态,cur_t是当前时间戳。如果要执行实时判定,应该放在此函数中逐帧检测。on_exit():在案例结束前被调用。
每个 actor 代表场景中的一个动态实体(车辆、行人、自行车),具有位置、尺寸、所在车道等属性,可在 on_loop 中逐帧检测。
3. on_loop 返回值说明¶
on_loop 需要返回 (return_code, message) 元组:
| 返回码 | 说明 |
|---|---|
CheckResult.PENDING |
当前没有判定结果(感兴趣的事件未发生),message 为空 |
CheckResult.PENDING_WITH_REPORT |
有感兴趣的事件发生,message 为自定义事件描述,此信息会发送到外界 |
CheckResult.FAIL |
事件已发生,立刻以失败状态结束案例,message 为自定义事件描述 |
CheckResult.SUC |
事件已发生,立刻以成功状态结束案例,message 为自定义事件描述 |
命名别名
以上常量也可通过 simone 名字空间访问,例如 simone.PENDING、simone.FAIL、simone.SUCC,与 CheckResult.X 写法等价。
4. 单机版调试说明¶
推荐在单机版上测试开发 Python 脚本,测试通过后再上传云端(如需云端运行)。
从安装包里启动 StartAllDebug.bat,在启动案例后可以看到 CybertronJudgeConsole 控制台,这样就可以在 Python 脚本中打印日志,然后在控制台观测日志,或者直接用 pdb 在线调试。
为了减轻频繁打包、上传 Python 脚本、测试的流程,可以将判定名称在 SimOne 案例编辑界面改为以下特殊字符串:
87da536b-c9ac-4567-8e9e-7213b1e01349-test-only
然后将判定代码放在 C:\simone_scripts 中并在此目录修改调试,这样每次案例运行时将直接使用该目录中的代码,而不是 ZIP 包中的代码,省去了频繁修改代码 → 压缩打包 → 上传脚本的测试流程。
5. API 字段说明¶
simone 名字空间¶
判定暴露的方法和属性都在 simone 名字空间下。
| 方法 | 返回类型 | 说明 |
|---|---|---|
log_info(string) |
— | 打印日志到控制台和文件 |
log_error(string) |
— | 打印错误日志 |
log_warning(string) |
— | 打印警告日志 |
get_left_lane(string) |
string | 获取某个车道的左车道,如 simone.get_left_lane(lane_name) |
get_right_lane(string) |
string | 获取某个车道的右车道,如 simone.get_right_lane(lane_name) |
get_traffic_light(int id) |
string | 按 opendrive id 查询交通灯当前状态,返回 'yellow'、'red'、'green'、'unknown' 之一 |
actor 对象¶
actor 表示场景中的一辆车或行人,包含多种属性,通过索引值和类型确定。
| 方法 | 返回类型 | 说明 |
|---|---|---|
get_bool(index) |
bool | 获取布尔值属性,如 actor.get_bool(simone.ActorAttr.STOPPED) |
get_double(index) |
double | 获取数值属性,如 actor.get_double(simone.ActorAttr.DIST_TO_LEFT_LANE_LINE) |
get_vec3(index) |
vec3 | 获取 vec3 属性,如 actor.get_vec3(simone.ActorAttr.POS) |
get_int(index) |
int | 获取 int 属性,如 actor.get_int(ActorAttr.LEADING_VEHICLE_ID) |
get_string(index) |
string | 获取 string 属性,如 actor.get_string(simone.ActorAttr.CENTER_LANE_NAME) |
get_id() |
int | 获取车辆 id |
get_type() |
int | 获取车辆类型(主车、对手车、自行车等) |
vec3 类型:具有 .x、.y、.z 属性。
ActorAttr 属性索引¶
根据属性类型用对应方法获取,如 actor.get_bool(simone.ActorAttr.STOPPED)。
| 属性索引 | 类型 | 说明 |
|---|---|---|
TIME |
double | 车辆属性获取时的时间戳 |
NAME |
string | actor 名字 |
POS |
vec3 | 车辆位置 |
YAW |
double | 车辆 yaw 角度 |
SIZE_3D |
vec3 | 车辆尺寸 |
LONG_SPEED |
double | 车辆纵向速度 |
LAT_SPEED |
double | 车辆横向速度 |
LONG_ACCEL |
double | 车辆纵向加速度 |
LAT_ACCEL |
double | 车辆横向加速度 |
LEFT_LIGHT_ON |
bool | 左车灯是否打开 |
RIGHT_LIGHT_ON |
bool | 右车灯是否打开 |
WARNING_LIGHT_ON |
bool | 双闪是否打开 |
STOPPED |
bool | 是否处于停车状态 |
STOPPED_TIME |
double | 停止时间 |
DIST_TO_LANE_LINE |
double | 到左右车道线距离(取最小值) |
DIST_TO_LEFT_LANE_LINE |
double | 到左车道线距离 |
DIST_TO_RIGHT_LANE_LINE |
double | 到右车道线距离 |
DIST_TO_CENTER_LANE_LINE |
double | 到车道中心线距离 |
DIST_TO_STOPLINE_WHEN_STOP |
double | 停止状态时到停止线的距离(运动状态时返回 NaN) |
TTC |
double | TTC |
LEADING_VEHICLE_DISTANCE |
double | 前车距离 |
LEADING_VEHICLE_ID |
int | 前车 ID |
TOUCH_DASH_LANE_LINE |
bool | 是否压了虚线 |
TOUCH_SOLID_LANE_LINE |
bool | 是否压了实线 |
OUT_OF_ROAD |
bool | 是否开出路外 |
CENTER_LANE_NAME |
string | 车辆中心点所在车道名字 |
WHOLE_LANE_NAME |
string | 整车所在车道名字 |
THROTTLE |
double | 油门数值(0 ~ 1) |
STEERING |
double | 方向盘转角,左负右正(角度) |
BRAKE |
double | 刹车数值(0 ~ 1) |
GEAR |
int | 当前挡位,-1 为倒挡 |
PRIVATE_VEHICLE_DIR |
vec3 | 车辆方向 |
PRIVATE_ACCEL_3D |
vec3 | 车辆加速度 |
PRIVATE_SPEED_3D |
vec3 | 车辆速度 |
PRIVATE_CENTER_TO_LEFT_LANE_LINE |
double | 车辆中心点到左车道线距离 |
PRIVATE_CENTER_TO_RIGHT_LANE_LINE |
double | 车辆中心点到右车道线距离 |
PRIVATE_TOUCH_LEFT_LANE_LINE |
bool | 是否压了左车道线 |
PRIVATE_TOUCH_RIGHT_LANE_LINE |
bool | 是否压了右车道线 |
hdmap 接口¶
| 方法 | 说明 |
|---|---|
hdmap.get_all_parking_space_ids() |
获取所有停车位 id |
hdmap.get_parking_space_by_id(id) |
获取某个车位信息,返回 {"pts": [pt_a, pt_b, pt_c, pt_d], "road_id": "..."} (ab: 左, bc: 后, cd: 右, da: 前) |
ActorType¶
| 常量 | 说明 |
|---|---|
ActorType.MAIN_VEHICLE |
主车 |
ActorType.PEDESTRIAN |
行人 |
ActorType.BICYCLE |
两轮车 |
ActorType.STATIC_OBJECT |
静态物体 |
6. 多判定组合说明¶
单 Python 环境限制
目前因架构原因,Judge 内只有一个 Python 环境,所以如果一个案例有多个 Python 判定,interface.py 中的 on_init、on_loop 等函数会相互覆盖,无法正常运行。
推荐做法:在一个 Python 判定包中包含多个子判定 py 文件,例如判定压线的 touch_lane_line.py、判定红灯的 traffic_light.py,这些子判定都由入口 interface.py 引用,以满足多种判定需求。
7. 示例:碰撞检测脚本¶
以下示例演示如何检测对手车辆与主车的碰撞(使用 shapely 计算多边形距离):
import json
from simone import *
import shapely
from shapely.geometry import Polygon
main_vehicle_id = -1
threshold_distance = 0.5 # 碰撞判定距离阈值(米)
def get_polygon(actor):
"""根据 actor 的位置、朝向、尺寸构造旋转矩形多边形。"""
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
# 示例:读取地图停车位信息
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')
另请参阅¶
- Lua 脚本判定管理 — 在 SimOne 界面上传和绑定脚本判定包的操作步骤(Python 脚本包的上传入口相同)