Skip to content

Conversation

@vcharraut
Copy link
Collaborator

@vcharraut vcharraut commented Oct 30, 2025

Data Format & Architecture Specification

I. Format Structure

1. Header

  • Size: 12 bytes
  • Fields:
    • num_agents (int32)
    • num_road_elements (int32)
    • num_traffic_controls (int32)

2. Agent Structure

Core Fields

  • id (int32)
  • type (int32)
  • trajectory_length (int32)

Trajectory Arrays (Log - Ground Truth)

  • Size: trajectory_length
  • Position: log_trajectory_x, log_trajectory_y, log_trajectory_z (float32[])
  • Orientation: log_heading (float32[])
  • Dynamics: log_velocity_x, log_velocity_y (float32[])
  • Dimensions: log_length, log_width, log_height (float32[])
  • Validity: log_valid (int32[])

Route Information

  • routes_length (int32)
  • routes (int32[]) - List of lane IDs (Note: Currently supports only the first route)
  • goal_position (float32, float32, float32) - x, y, z
  • mark_as_expert (int32)
    • 1: No routes (Expert/Uncontrollable)
    • 0: Routes exist (Controllable)

3. RoadMapElement Structure

Core Fields

  • id (int32)
  • type (int32)
  • segment_length (int32)

Geometry Arrays

  • Size: segment_length
  • Coordinates: x, y, z (float32[])

Connectivity & Limits

  • num_entry (int32) & entry_lanes (int32[])
  • num_exit (int32) & exit_lanes (int32)
  • speed_limit (float32) - Unit: m/s

4. TrafficControlElement Structure

Core Fields

  • id (int32)
  • type (int32)
  • Position: x, y, z (float32, float32, float32)

State & Control

  • state_length (int32) and states (int32[])
  • num_controlled_lanes (int32) and controlled_lanes (int32[])

5. Metadata (Variable Length)

  • scenario_id (char[128])
  • map_id (int32)
  • dataset_name (char[64])
  • length (int32) - Number of timesteps
  • sdc_index (int32) - Ego vehicle ID
  • num_objects_of_interest + corresponding ID array
  • num_tracks_to_predict + corresponding ID array

II. Major Architectural Changes

1. New Data Structure Separation (datatypes.h)

Replaced a single Entity struct with three specialized structures:

  • Agent: Vehicles, pedestrians, cyclists.
  • RoadMapElement: Lanes, lines, edges.
  • TrafficControlElement: Traffic lights, stop signs, etc.

2. Renamed Fields: "Log" vs. "Sim"

Agent fields now clearly distinguish between:

  • Log trajectory: Ground truth from the dataset (e.g., log_trajectory_x).
  • Sim state: Current simulation state (e.g., sim_x, sim_y, sim_z).

3. Metadata Tracking

Added key fields to the Drive struct to improve scenario management:

  • scenario_id, map_index, dataset_name, log_length, sdc_index.
  • Tracking for "objects of interest" and "tracks to predict".

4. Updated Road Type System

  • Lane types: FREEWAY, SURFACE_STREET, BIKE_LANE
  • Road line types: 8 variations (e.g., broken/solid, white/yellow).
  • Road edge types: BOUNDARY, MEDIAN, SIDEWALK.
  • Removed types (from encoder): CROSSWALK, SPEED_BUMP, DRIVEWAY.

5. Traffic Control Updates

  • Defined types: TRAFFIC_LIGHT, STOP_SIGN, YIELD_SIGN.
  • Note: Maps containing traffic lights are currently skipped (not yet supported).

6. GridMap Entity Type System

GridMapEntity now uses an explicit entity_type field:

  1. DynamicAgent
  2. RoadMapElement
  3. TrafficControl

7. Agent Dimensions

DynamicAgent tracks time-varying dimensions:

  • log_length/width/height arrays (per timestep).
  • sim_length/width/height (current simulation values).

8. Route Information

Added route_length and route array to DynamicAgent to enable path planning and lane following.

  • Current Limit: Only one route is supported per agent.

III. Routes Logic Details

Description
A list of lane IDs representing the path for an agent to follow. The path is calculated to be the closest possible match to the ground truth trajectory.

Conditions

  • Must be a Vehicle.
  • Valid at timestep decided in config (default: 0).
  • Valid for a minimum duration of timesteps (default: 0).
  • Not Offroad: The bounding box must not cross a road edge OR be >5m from a lane.

Current Constraints

  • Only one route per agent is supported at this time.

III. Note on nuPlan Data

The format functions correctly with nuPlan data, but the source data is noted to be "messy" (requires careful handling).

… bounds checks and updating trajectory handling in visualization functions
Comment on lines 165 to 180
int is_road_lane(int type){
return (type >= 0 && type <= 9);
}

int is_drivable_road_lane(int type){
return (type == LANE_FREEWAY || type == LANE_SURFACE_STREET);
}

int is_road_line(int type){
return (type >= 10 && type <= 19);
}

int is_road_edge(int type){
return (type >= 20 && type <= 29);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't have a solution for this yet but this seems very error prone since it relies on someone remembering to change this if they add a new road type

Copy link
Collaborator Author

@vcharraut vcharraut Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I agree on that

and I don't know if we want to merge certain types

Comment on lines 165 to 173
int is_road_lane(int type){
return (type >= 0 && type <= 9);
}

int is_drivable_road_lane(int type){
return (type == LANE_FREEWAY || type == LANE_SURFACE_STREET);
}

int is_road_line(int type){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a second thing is that lane and line being separated by a single char also seems error prone

Copy link
Collaborator

@l1onh3art88 l1onh3art88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, I think the addition does improve readability as types improve but there is some excess bloat on the loading the binaries function and arguably unnecessary variable creations throughout. ~100 lines or so could be cut.

One potential issue in load binaries if drive.py is not adjusted to write traffic objects as is. Please check on this if traffic logic is not in yet.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK if daphne's PR was already merged on the no active agent condition. But it would be good to also address that here because there is now additional freeing logic.

return -1;
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a good portion of the code bloat is from error handling here. Are all of these checks really necessary? I know they can be helpful b/c debugging here is tough, but it is a large addition with effectively no function for the sim.

e->vy = 0;
e->vz = 0;
e->collided_before_goal = 0;
agent->sim_vx = 0.0f;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did vz get removed or forgotten here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it, we don't use it and I don't think it is gonna be relevant for the next steps

}

// Subtract mean from road elements
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set means should be refactorable to cut lines by half by combining loops.

agent->sim_heading = agent->log_heading[t];
agent->sim_vx = agent->log_velocity_x[t];
agent->sim_vy = agent->log_velocity_y[t];
agent->sim_length = agent->log_length[t];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we setting length width height and valid here and resetting to 0 above?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can be debateble, but for the data-driven approach, it might be better to have dynamic bounding boxes but it represents the perception noise better. Now because the noise comes the ego perception, it makes more sense to apply this in ghost mode (=control only sdc) rather than multi-agent

// Rotate to ego vehicle's frame
float rel_goal_x = goal_x*cos_heading + goal_y*sin_heading;
float rel_goal_y = -goal_x*sin_heading + goal_y*cos_heading;
float distance_to_goal = relative_distance_2d(0, 0, rel_goal_x, rel_goal_y);
// Shrink agent size
env->entities[agent_idx].width *= 0.7f;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this was in the original GPU drive, so is this no longer relevant?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, I added it back in set_start_position

int num_dynamic_agents, num_roads, num_traffic;
if (fread(&num_dynamic_agents, sizeof(int), 1, file) != 1 ||
fread(&num_roads, sizeof(int), 1, file) != 1 ||
fread(&num_traffic, sizeof(int), 1, file) != 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will break the loading of any map until traffic is in the .py file no? Should this not be an env variable passed through config to check and then read the header properly. I think if fread is used it jumps to the next mem slot.

@vcharraut vcharraut marked this pull request as ready for review November 17, 2025 22:10
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

  • Refactors unified Entity struct into three specialized types: DynamicAgent, RoadMapElement, and TrafficControlElement
  • Updates binary format to include route information, lane connectivity, traffic control states, and scenario metadata

Confidence Score: 1/5

  • This PR has critical memory management bugs that will cause leaks and potential crashes
  • Multiple critical memory leaks found: double allocation of static_agent_indices and expert_static_agent_indices arrays, incorrect malloc usage for entry_lanes/exit_lanes when counts are 0, and duplicate field declarations in structs
  • pufferlib/ocean/drive/drive.h requires immediate attention to fix memory leaks and duplicate declarations

Important Files Changed

Filename Overview
pufferlib/ocean/drive/drive.h Refactored entity handling with three typed arrays, but contains critical memory leaks (double allocation, incorrect malloc patterns) and duplicate field declarations

Sequence Diagram

sequenceDiagram
    participant Binary as "Binary File"
    participant LoadFunc as "load_map_binary()"
    participant Drive as "Drive struct"
    participant Memory as "Memory"

    LoadFunc->>Binary: "Open and read header"
    Binary-->>LoadFunc: "num_dynamic_agents, num_roads, num_traffic"
    LoadFunc->>Memory: "Allocate dynamic_agents array"
    LoadFunc->>Memory: "Allocate road_elements array"
    LoadFunc->>Memory: "Allocate traffic_elements array"
    
    loop For each dynamic agent
        LoadFunc->>Binary: "Read agent metadata"
        LoadFunc->>Memory: "Allocate trajectory arrays"
        LoadFunc->>Binary: "Read trajectory data"
        LoadFunc->>Binary: "Read route data"
    end
    
    loop For each road element
        LoadFunc->>Binary: "Read road metadata"
        LoadFunc->>Memory: "Allocate coordinate arrays"
        LoadFunc->>Binary: "Read coordinates"
        alt is_road_lane
            LoadFunc->>Memory: "Allocate entry/exit lane arrays"
            LoadFunc->>Binary: "Read lane connectivity"
        end
    end
    
    loop For each traffic element
        LoadFunc->>Binary: "Read traffic metadata"
        LoadFunc->>Memory: "Allocate states array"
        LoadFunc->>Binary: "Read traffic states"
    end
    
    LoadFunc->>Binary: "Read scenario metadata"
    LoadFunc->>Drive: "Populate metadata fields"
    LoadFunc->>Binary: "Close file"
Loading

Additional Comments (1)

  1. pufferlib/ocean/drive/drive.h, line 241-258 (link)

    logic: scenario_length field is declared twice in the Drive struct

4 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile
React with 👍 or 👎 to share your feedback on this new summary format

@daphne-cornelisse daphne-cornelisse added the enhancement New feature or request label Nov 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants