feat(API_metrics): completed inventory#6
Conversation
Dumpy250
commented
Feb 18, 2026
- completed inventory of python API metrics
- created a probe script that prints metrics
- completed inventory of python API metrics - created a probe script that prints metrics
There was a problem hiding this comment.
Pull request overview
Adds initial CARLA Python API “metrics inventory” documentation and a small probe utility to print world/vehicle metrics, plus minimal CARLA client helper + dependency/config updates.
Changes:
- Add
docs/metrics_catalog.mdcataloging world/vehicle/sensor/event metrics. - Add
src/scripts/probe_world_and_vehicle.pyto connect to CARLA, select/spawn a vehicle, and print key metrics. - Add
src/cot/carla_client.py,requirements.txt, and update.gitignorefor the toolkit.
Reviewed changes
Copilot reviewed 3 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/scripts/probe_world_and_vehicle.py |
New probe script to print CARLA world + vehicle metrics and traffic-light state. |
src/cot/carla_client.py |
New helper for creating a CARLA client with defaults. |
requirements.txt |
Adds CARLA dependency. |
docs/metrics_catalog.md |
New metrics catalog (v1) for CARLA Python API. |
.gitignore |
Replaces ignore rules with a simplified Python-focused + CARLA-output-oriented set. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | Metric | Units | Type | Update | Source | | | | | | ||
| | -------------------------------- | ----: | ----- | -------- | ---------------------------------------- | - | ---------------------- | - | ------------------------ | | ||
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | | | | | ||
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | | | | | ||
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | | | | | ||
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | | | | | ||
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | | | | | ||
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | | | | | ||
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | | | | | ||
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | | | | | ||
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | | | | | ||
| | vehicle.speed | m/s | float | per tick | ` | | vehicle.get_velocity() | | ` *(computed magnitude)* | | ||
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | | | | | ||
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | | | | | | ||
| | vehicle.acceleration.z | m/s² | float | per tick | `vehicle.get_acceleration().z` | | | | | | ||
| | vehicle.angular_velocity.x | rad/s | float | per tick | `vehicle.get_angular_velocity().x` | | | | | | ||
| | vehicle.angular_velocity.y | rad/s | float | per tick | `vehicle.get_angular_velocity().y` | | | | | | ||
| | vehicle.angular_velocity.z | rad/s | float | per tick | `vehicle.get_angular_velocity().z` | | | | | |
There was a problem hiding this comment.
The vehicle.speed row’s Source cell is split across multiple columns/lines with unmatched backticks, so it won’t render correctly. Make this a single inline expression (e.g., magnitude of vehicle.get_velocity()) contained within one cell.
| | Metric | Units | Type | Update | Source | | | | | | |
| | -------------------------------- | ----: | ----- | -------- | ---------------------------------------- | - | ---------------------- | - | ------------------------ | | |
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | | | | | |
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | | | | | |
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | | | | | |
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | | | | | |
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | | | | | |
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | | | | | |
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | | | | | |
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | | | | | |
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | | | | | |
| | vehicle.speed | m/s | float | per tick | ` | | vehicle.get_velocity() | | ` *(computed magnitude)* | | |
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | | | | | |
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | | | | | | |
| | vehicle.acceleration.z | m/s² | float | per tick | `vehicle.get_acceleration().z` | | | | | | |
| | vehicle.angular_velocity.x | rad/s | float | per tick | `vehicle.get_angular_velocity().x` | | | | | | |
| | vehicle.angular_velocity.y | rad/s | float | per tick | `vehicle.get_angular_velocity().y` | | | | | | |
| | vehicle.angular_velocity.z | rad/s | float | per tick | `vehicle.get_angular_velocity().z` | | | | | | |
| | Metric | Units | Type | Update | Source | | | | | | |
| | -------------------------------- | ----: | ----- | -------- | ----------------------------------------------------- | - | ---------------------- | - | ------------------------ | | |
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | | | | | |
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | | | | | |
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | | | | | |
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | | | | | |
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | | | | | |
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | | | | | |
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | | | | | |
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | | | | | |
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | | | | | |
| | vehicle.speed | m/s | float | per tick | `vehicle.get_velocity()` *(computed magnitude)* | | | | | | |
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | | | | | |
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | | | | | | |
| | vehicle.acceleration.z | m/s² | float | per tick | `vehicle.get_acceleration().z` | | | | | | |
| | vehicle.angular_velocity.x | rad/s | float | per tick | `vehicle.get_angular_velocity().x` | | | | | | |
| | vehicle.angular_velocity.y | rad/s | float | per tick | `vehicle.get_angular_velocity().y` | | | | | | |
| | vehicle.angular_velocity.z | rad/s | float | per tick | `vehicle.get_angular_velocity().z` | | | | | |
| for _ in range(3): | ||
| world.wait_for_tick() |
There was a problem hiding this comment.
world.wait_for_tick() can hang indefinitely when the server is in synchronous mode and no other client is calling world.tick(). To make the probe reliable, branch on world.get_settings().synchronous_mode and advance with world.tick() (or explicitly set async mode) when needed.
| for _ in range(3): | |
| world.wait_for_tick() | |
| settings = world.get_settings() | |
| for _ in range(3): | |
| if settings.synchronous_mode: | |
| world.tick() | |
| else: | |
| world.wait_for_tick() |
|
|
||
| DEFAULT_HOST = "localhost" | ||
| DEFAULT_PORT = 2000 | ||
| DEFAULT_TIMEOUT_S = 10.0 # This is dependant on your server setup. With 2 seconds my client refused to connect. |
There was a problem hiding this comment.
Typo in the inline comment: “dependant” should be “dependent”.
| DEFAULT_TIMEOUT_S = 10.0 # This is dependant on your server setup. With 2 seconds my client refused to connect. | |
| DEFAULT_TIMEOUT_S = 10.0 # This is dependent on your server setup. With 2 seconds my client refused to connect. |
| | Metric | Units | Type | Update | Source | | | | | | ||
| | -------------------------------- | ----: | ----- | -------- | ---------------------------------------- | - | ---------------------- | - | ------------------------ | | ||
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | | | | | ||
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | | | | | ||
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | | | | | ||
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | | | | | ||
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | | | | | ||
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | | | | | ||
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | | | | | ||
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | | | | | ||
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | | | | | ||
| | vehicle.speed | m/s | float | per tick | ` | | vehicle.get_velocity() | | ` *(computed magnitude)* | | ||
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | | | | | ||
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | | | | | |
There was a problem hiding this comment.
The Vehicle State table has extra empty columns (the trailing | | ... |) which breaks Markdown rendering. Please reformat this section into a standard 5-column table (Metric/Units/Type/Update/Source) and remove the placeholder columns.
| | Metric | Units | Type | Update | Source | | | | | | |
| | -------------------------------- | ----: | ----- | -------- | ---------------------------------------- | - | ---------------------- | - | ------------------------ | | |
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | | | | | |
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | | | | | |
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | | | | | |
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | | | | | |
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | | | | | |
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | | | | | |
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | | | | | |
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | | | | | |
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | | | | | |
| | vehicle.speed | m/s | float | per tick | ` | | vehicle.get_velocity() | | ` *(computed magnitude)* | | |
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | | | | | |
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | | | | | | |
| | Metric | Units | Type | Update | Source | | |
| | -------------------------------- | ----: | ----- | -------- | ---------------------------------------- | | |
| | vehicle.transform.location.x | m | float | per tick | `vehicle.get_transform().location.x` | | |
| | vehicle.transform.location.y | m | float | per tick | `vehicle.get_transform().location.y` | | |
| | vehicle.transform.location.z | m | float | per tick | `vehicle.get_transform().location.z` | | |
| | vehicle.transform.rotation.pitch | deg | float | per tick | `vehicle.get_transform().rotation.pitch` | | |
| | vehicle.transform.rotation.yaw | deg | float | per tick | `vehicle.get_transform().rotation.yaw` | | |
| | vehicle.transform.rotation.roll | deg | float | per tick | `vehicle.get_transform().rotation.roll` | | |
| | vehicle.velocity.x | m/s | float | per tick | `vehicle.get_velocity().x` | | |
| | vehicle.velocity.y | m/s | float | per tick | `vehicle.get_velocity().y` | | |
| | vehicle.velocity.z | m/s | float | per tick | `vehicle.get_velocity().z` | | |
| | vehicle.speed | m/s | float | per tick | `vehicle.get_velocity()` *(computed magnitude)* | | |
| | vehicle.acceleration.x | m/s² | float | per tick | `vehicle.get_acceleration().x` | | |
| | vehicle.acceleration.y | m/s² | float | per tick | `vehicle.get_acceleration().y` | |
| | Field | Units | Type | Source | | | | | | ||
| | ---------------------------- | ----: | ------- | ---------------------- | - | -------------- | - | -------------- | | ||
| | event.frame | frame | int | `event.frame` | | | | | | ||
| | event.timestamp | s | float | `event.timestamp` | | | | | | ||
| | event.other_actor_id | - | int | `event.other_actor.id` | | | | | | ||
| | event.normal_impulse.(x,y,z) | N·s | vector3 | `event.normal_impulse` | | | | | | ||
| | event.intensity | N·s | float | ` | | normal_impulse | | ` *(computed)* | |
There was a problem hiding this comment.
The Collision Event table header includes extra empty columns (trailing | | ... |) which breaks Markdown rendering. Please collapse this to a single 4-column table (Field/Units/Type/Source) to match the other sections.
| | Field | Units | Type | Source | | | | | | |
| | ---------------------------- | ----: | ------- | ---------------------- | - | -------------- | - | -------------- | | |
| | event.frame | frame | int | `event.frame` | | | | | | |
| | event.timestamp | s | float | `event.timestamp` | | | | | | |
| | event.other_actor_id | - | int | `event.other_actor.id` | | | | | | |
| | event.normal_impulse.(x,y,z) | N·s | vector3 | `event.normal_impulse` | | | | | | |
| | event.intensity | N·s | float | ` | | normal_impulse | | ` *(computed)* | | |
| | Field | Units | Type | Source | | |
| | ---------------------------- | ----: | ------- | ---------------------- | | |
| | event.frame | frame | int | `event.frame` | | |
| | event.timestamp | s | float | `event.timestamp` | | |
| | event.other_actor_id | - | int | `event.other_actor.id` | | |
| | event.normal_impulse.(x,y,z) | N·s | vector3 | `event.normal_impulse` | | |
| | event.intensity | N·s | float | `event.normal_impulse` *(computed)* | |
| | event.timestamp | s | float | `event.timestamp` | | | | | | ||
| | event.other_actor_id | - | int | `event.other_actor.id` | | | | | | ||
| | event.normal_impulse.(x,y,z) | N·s | vector3 | `event.normal_impulse` | | | | | | ||
| | event.intensity | N·s | float | ` | | normal_impulse | | ` *(computed)* | |
There was a problem hiding this comment.
The event.intensity row has a broken inline code block split across columns/lines with unmatched backticks. Put the computed formula in a single Source cell so the table renders and the expression is readable.
| | event.intensity | N·s | float | ` | | normal_impulse | | ` *(computed)* | | |
| | event.intensity | N·s | float | `event.normal_impulse` magnitude *(computed)* | | | | | |
| v, spawned = get_or_spawn_vehicle(world) | ||
| print("vehicle.selected:", v.id, v.type_id, "| spawned:" , spawned) | ||
| print("vehicle.is_alive:", v.is_alive, "is_active:", v.is_active, "state:", v.actor_state) | ||
| print("vehicle.bounding_box.extent:", v.bounding_box.extent.x, v.bounding_box.extent.y, v.bounding_box.extent.z) | ||
|
|
||
| # Vehicle/Actor metrics | ||
| t = v.get_transform() | ||
| vel = v.get_velocity() | ||
| acc = v.get_acceleration() | ||
| ang = v.get_angular_velocity() | ||
| ctrl = v.get_control() | ||
|
|
||
| print("loc:", t.location.x, t.location.y, t.location.z) | ||
| print("rot:", t.rotation.pitch, t.rotation.yaw, t.rotation.roll) | ||
| print("speed(m/s):", vec_mag(vel)) | ||
| print("acc(m/s^2):", vec_mag(acc)) | ||
| print("ang(rad/s):", vec_mag(ang)) | ||
| print("control:", ctrl.throttle, ctrl.steer, ctrl.brake) | ||
| print("vel_vec(m/s):", vel.x, vel.y, vel.z) | ||
| print("acc_vec(m/s^2):", acc.x, acc.y, acc.z) | ||
| print("ang_vec(rad/s):", ang.x, ang.y, ang.z) | ||
|
|
||
| # Traffic light related | ||
| is_at = v.is_at_traffic_light() | ||
| print("is_at_traffic_light:", is_at) | ||
| if is_at: | ||
| state = v.get_traffic_light_state() | ||
| name = getattr(state, "name", None) | ||
| print("traffic_light_state:", name if name else state) |
There was a problem hiding this comment.
When this script spawns a vehicle, it never destroys it, so repeated runs will accumulate actors in the simulation. Consider wrapping the main body in a try/finally and calling vehicle.destroy() when spawned is True.
| v, spawned = get_or_spawn_vehicle(world) | |
| print("vehicle.selected:", v.id, v.type_id, "| spawned:" , spawned) | |
| print("vehicle.is_alive:", v.is_alive, "is_active:", v.is_active, "state:", v.actor_state) | |
| print("vehicle.bounding_box.extent:", v.bounding_box.extent.x, v.bounding_box.extent.y, v.bounding_box.extent.z) | |
| # Vehicle/Actor metrics | |
| t = v.get_transform() | |
| vel = v.get_velocity() | |
| acc = v.get_acceleration() | |
| ang = v.get_angular_velocity() | |
| ctrl = v.get_control() | |
| print("loc:", t.location.x, t.location.y, t.location.z) | |
| print("rot:", t.rotation.pitch, t.rotation.yaw, t.rotation.roll) | |
| print("speed(m/s):", vec_mag(vel)) | |
| print("acc(m/s^2):", vec_mag(acc)) | |
| print("ang(rad/s):", vec_mag(ang)) | |
| print("control:", ctrl.throttle, ctrl.steer, ctrl.brake) | |
| print("vel_vec(m/s):", vel.x, vel.y, vel.z) | |
| print("acc_vec(m/s^2):", acc.x, acc.y, acc.z) | |
| print("ang_vec(rad/s):", ang.x, ang.y, ang.z) | |
| # Traffic light related | |
| is_at = v.is_at_traffic_light() | |
| print("is_at_traffic_light:", is_at) | |
| if is_at: | |
| state = v.get_traffic_light_state() | |
| name = getattr(state, "name", None) | |
| print("traffic_light_state:", name if name else state) | |
| v = None | |
| spawned = False | |
| try: | |
| v, spawned = get_or_spawn_vehicle(world) | |
| print("vehicle.selected:", v.id, v.type_id, "| spawned:" , spawned) | |
| print("vehicle.is_alive:", v.is_alive, "is_active:", v.is_active, "state:", v.actor_state) | |
| print("vehicle.bounding_box.extent:", v.bounding_box.extent.x, v.bounding_box.extent.y, v.bounding_box.extent.z) | |
| # Vehicle/Actor metrics | |
| t = v.get_transform() | |
| vel = v.get_velocity() | |
| acc = v.get_acceleration() | |
| ang = v.get_angular_velocity() | |
| ctrl = v.get_control() | |
| print("loc:", t.location.x, t.location.y, t.location.z) | |
| print("rot:", t.rotation.pitch, t.rotation.yaw, t.rotation.roll) | |
| print("speed(m/s):", vec_mag(vel)) | |
| print("acc(m/s^2):", vec_mag(acc)) | |
| print("ang(rad/s):", vec_mag(ang)) | |
| print("control:", ctrl.throttle, ctrl.steer, ctrl.brake) | |
| print("vel_vec(m/s):", vel.x, vel.y, vel.z) | |
| print("acc_vec(m/s^2):", acc.x, acc.y, acc.z) | |
| print("ang_vec(rad/s):", ang.x, ang.y, ang.z) | |
| # Traffic light related | |
| is_at = v.is_at_traffic_light() | |
| print("is_at_traffic_light:", is_at) | |
| if is_at: | |
| state = v.get_traffic_light_state() | |
| name = getattr(state, "name", None) | |
| print("traffic_light_state:", name if name else state) | |
| finally: | |
| if spawned and v is not None and getattr(v, "is_alive", False): | |
| v.destroy() |
|
@copilot open a new pull request to apply changes based on the comments in this thread |
… cleanup Co-authored-by: Dumpy250 <83517134+Dumpy250@users.noreply.github.com>
Co-authored-by: Dumpy250 <83517134+Dumpy250@users.noreply.github.com>
[WIP] Add inventory documentation for Python API metrics
|
@copilot look good now? |