POMDPPlanners.environments.nuplan_pomdp package
nuPlan POMDP environment: a forward-only world plus a swappable planner model.
This package adapts the nuPlan closed-loop planning simulator to the POMDPPlanners interface, following the same world/model split as the CARLA package:
nuplan_pomdp— the ground-truth world (NuPlanPOMDP), forward-only, emitting a raw{ego, agents}observation.nuplan_perception— the swappable per-channel observation (encoder) models the planner degrades the raw reading with.nuplan_generative_models— the planner-side generative model (policy.environment): dynamics + the composed observation model.nuplan_belief— the particle belief that stamps the perceived agent block onto its particles.
- class POMDPPlanners.environments.nuplan_pomdp.NuPlanPOMDP(discount_factor, scenario_loader=None, action_presets=None, max_tracked_agents=5, simulation_horizon=8.0, fixed_delta_seconds=0.1, reactive_agents=True, collision_distance=2.0, collision_penalty=100.0, desired_speed=8.0, out_lane_thresh=2.0, observation_extractor=None, seed=None, name=None, reward_range=None, output_dir=None, debug=False, use_queue_logger=False)[source]
Bases:
EnvironmentForward-only adapter exposing a nuPlan closed-loop session as a world POMDP.
The wrapper drives a nuPlan
Simulationas the ground-truth world of an episode. It advances the simulator exactly one iteration per real interaction and serves the resulting next state, observation and reward from a small cache, because the POMDPPlanners episode loop requests those three quantities through separate method calls while nuPlan produces them atomically. The state is the ego vehicle’s ground-truth kinematics; the observation is the ego proprioception plus a tracked-object list, so the world is genuinely partially observed. See the module docstring for the exact state and observation variables, units, and frames.Note
This is a world environment, not a generative model. It cannot sample a transition from an arbitrary state, so belief particle propagation and density queries are unsupported and raise
NotImplementedError/RuntimeError. Pair it with a generative model environment on the planner (policy.environment).- Parameters:
discount_factor (float)
max_tracked_agents (int)
simulation_horizon (float)
fixed_delta_seconds (float)
reactive_agents (bool)
collision_distance (float)
collision_penalty (float)
desired_speed (float)
out_lane_thresh (float)
observation_extractor (Callable[[Dict[str, ndarray]], Any] | None)
seed (int | None)
name (str | None)
output_dir (Path | None)
debug (bool)
use_queue_logger (bool)
- action_presets
Discrete
(acceleration, steering_angle)control pairs.
- max_tracked_agents
Number of nearest agents carried in state/observation.
- seed
Optional seed applied to the first
resetfor reproducibility.
Example
The environment is used as the forward-only world of an
EpisodeRunner, paired with a separate generative model on the planner. It requires the nuPlan devkit and a scenario loader, so this snippet is illustrative rather than executed:env = NuPlanPOMDP(discount_factor=0.95, scenario_loader=load_scenario) state = env.initial_state_dist().sample()[0] next_state, observation, reward = env.sample_next_step(state, 0)
- compute_metrics(histories)[source]
Compute nuPlan driving-quality metrics from episode histories.
- Parameters:
- Returns:
collision_rate: fraction of episodes that ended in a collision.average_progress: mean per-episode ground distance travelled (m).average_speed: mean ego speed over the driven trajectory (m/s).near_miss_count: mean number of near-miss events per episode.min_vehicle_distance: mean over episodes of the closest the ego came to any agent (m); episodes that saw no agent are excluded.
- Return type:
- hash_action(action)[source]
Return a hashable key consistent with action equality.
Used by tree-search planners to index action children of a belief node in O(1). The returned key MUST satisfy:
action_a == action_b (per env's notion of equality) ==> hash_action(action_a) == hash_action(action_b)
Subclasses with non-hashable actions (e.g.
np.ndarray) must override to return a hashable surrogate (tobytes()is the standard choice for ndarray actions, which mirrors thenp.array_equalsemantics used by the linear-scan fallback).
- hash_observation(observation)[source]
Return a hashable key consistent with
is_equal_observation().Used by tree-search planners to index belief children by observation in O(1). The returned key MUST satisfy the contract:
is_equal_observation(a, b) implies hash_observation(a) == hash_observation(b)
- Parameters:
observation (
Any) – Observation to hash.- Returns:
the observation itself when it is already hashable).
- Return type:
- Raises:
NotImplementedError – If the observation is not hashable and the subclass has not provided an override. Subclasses with non-hashable observations (e.g.
np.ndarray) MUST override.
- initial_observation_dist()[source]
Get the initial observation distribution.
- Return type:
- Returns:
Distribution over initial observations
Note
Subclasses must implement this method to define initial observations.
- initial_state_dist()[source]
Get the initial state distribution.
- Return type:
- Returns:
Distribution over initial states
Note
Subclasses must implement this method to define the starting distribution.
- is_equal_observation(observation1, observation2)[source]
Check if two observations are equal.
- Parameters:
- Return type:
- Returns:
True if observations are considered equal, False otherwise
Note
Subclasses must implement this method to define observation equality. This is particularly important for discrete observation spaces.
- is_terminal(state)[source]
Check if a state is terminal.
- Parameters:
state (
Any) – State to check for terminal condition- Return type:
- Returns:
True if the state is terminal, False otherwise
Note
Subclasses must implement this method to define terminal conditions.
- observation_log_probability(next_state, action, observations)[source]
Log-probability of each candidate observation under
(next_state, action).Returns
np.ndarrayof shape(N,)where N is the number of candidate observations. Subclasses must implement.
- reward(state, action, next_state=None)[source]
Calculate the immediate reward for a state-action(-next_state) tuple.
next_stateis the realised post-transition state when known (e.g. threaded bysample_next_step()), allowing rewards that depend on stochastic transition outcomes to use the same draw as the trajectory instead of resampling. Subclasses whose reward is a pure function of(state, action)may ignore it; subclasses whose reward depends on the realised next state (collision penalties, win bonuses) should consume it when provided and fall back to drawing/computing one whenNone.- Parameters:
- Return type:
- Returns:
Immediate reward value.
Note
Subclasses must implement this method to define reward structure.
- sample_next_state(state, action, n_samples=1)[source]
Sample one or more next states for
(state, action).Hot-path entry point used by MCTS planners and particle filters. Subclasses must implement.
- sample_observation(next_state, action, n_samples=1)[source]
Sample one or more observations for
(next_state, action).Hot-path entry point used by MCTS planners and particle filters. Subclasses must implement.
- class POMDPPlanners.environments.nuplan_pomdp.NuPlanPOMDPMetrics(*values)[source]
Bases:
EnumMetric names for the nuPlan POMDP environment.
- AVERAGE_PROGRESS = 'average_progress'
- AVERAGE_SPEED = 'average_speed'
- COLLISION_RATE = 'collision_rate'
- MIN_VEHICLE_DISTANCE = 'min_vehicle_distance'
- NEAR_MISS_COUNT = 'near_miss_count'
- class POMDPPlanners.environments.nuplan_pomdp.PerceivedAgentsBelief(particles, log_weights, max_tracked_agents=5, agent_pose_jitter=0.3, resampling=True, ess_factor=0.5)[source]
Bases:
WeightedParticleBeliefReinvigorationWeighted particle belief that stamps the observation’s agent block onto every particle.
After the standard particle-filter weight update and resample, the reinvigoration step writes the current observation’s
agentsblock into every particle’s agent slots (plus optional per-particle jitter), leaving the ego block to the filter and any trailing light slot untouched. The belief holds no perception state — perception is the planner model’s, applied upstream byencode_observation— so a plain observation with a perceivedagentsblock is all it needs.- Parameters:
- max_tracked_agents
Number of fixed agent slots carried in each particle.
- agent_pose_jitter
Std of Gaussian noise added to each stamped agent’s
[rel_x, rel_y, rel_yaw, rel_speed]pose, for particle diversity.
Example
>>> import numpy as np >>> np.random.seed(0) >>> from POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp import ( ... AGENT_SLOT_WIDTH, EGO_STATE_WIDTH) >>> width = EGO_STATE_WIDTH + 1 * AGENT_SLOT_WIDTH >>> particles = [np.zeros(width) for _ in range(4)] >>> belief = PerceivedAgentsBelief( ... particles=particles, ... log_weights=np.log(np.ones(4) / 4), ... max_tracked_agents=1, ... ) >>> observation = { # a perceived agent 8 m ahead ... "ego": np.zeros(7), ... "agents": np.array([1.0, 8.0, 0.0, 0.0, 5.0]), ... } >>> base = WeightedParticleBelief(particles=particles, log_weights=belief.log_weights) >>> refreshed = belief.reinvigorate("noop", observation, None, base) >>> bool(np.asarray(refreshed.particles)[0, EGO_STATE_WIDTH] == 1.0) # slot now present True
- reinvigorate(action, observation, pomdp, belief)[source]
Stamp the observation’s perceived agent block onto every particle.
- Return type:
- Parameters:
action (Any)
observation (Any)
pomdp (Environment | None)
belief (WeightedParticleBelief)
- POMDPPlanners.environments.nuplan_pomdp.assemble_state(ego_row, agent_rows, max_tracked_agents, light_row=None)[source]
Concatenate an ego row, padded agent slots, and a light slot into a state vector.
Pure numeric assembly of the nuPlan state layout, factored out of the live session so the state geometry can be exercised without a nuPlan installation. Agent rows are written into the nearest fixed slots (already ego-frame); missing slots are padded with zeros (
present == 0).- Parameters:
ego_row (
Union[Sequence[float],ndarray]) – TheEGO_STATE_WIDTHego block[x, y, yaw, vx, vy, lat, heading_err].agent_rows (
Union[Sequence[Sequence[float]],Sequence[ndarray]]) – Zero or more ego-frame agent rows[present, rel_x, rel_y, rel_yaw, rel_speed]; only the firstmax_tracked_agentsare kept.max_tracked_agents (
int) – Number of fixed agent slots to emit.light_row (
Union[Sequence[float],ndarray,None]) – OptionalLIGHT_SLOT_WIDTHtraffic-light slot; a zero (absent) slot is emitted whenNone.
- Return type:
- Returns:
The full state vector of width
EGO_STATE_WIDTH + max_tracked_agents * AGENT_SLOT_WIDTH + LIGHT_SLOT_WIDTH.
- POMDPPlanners.environments.nuplan_pomdp.driving_quality_reward(next_state, steering_angle, terminated, desired_speed, out_lane_thresh, collision_penalty)[source]
Score a transition with a gym-carla-style driving-quality reward.
Rewards along-route progress and penalises overspeed, drifting off the route baseline, harsh / high-speed steering, each elapsed step, and a terminal collision. Shared by the
NuPlanPOMDPworld and the planner-side factored model so the two score a transition identically by construction.- Parameters:
next_state (
ndarray) – Resulting ego state[x, y, yaw(rad), vx, vy, lat, heading_err].steering_angle (
float) – Steering command applied on the transition (from the action preset).terminated (
bool) – Whether the transition ended in a terminal collision.desired_speed (
float) – Target longitudinal speed (m/s); exceeding it is penalised.out_lane_thresh (
float) – Lateral offset (m) beyond which the ego is treated as off-route.collision_penalty (
float) – Penalty scale applied on a terminal collision.
- Return type:
- Returns:
The scalar reward for the transition.
- POMDPPlanners.environments.nuplan_pomdp.relative_agent_row(ego_x, ego_y, ego_yaw_rad, other_x, other_y, other_yaw_rad, other_speed)[source]
Express another agent’s pose/speed in the ego frame as a present slot row.
Returns
[1.0, rel_x, rel_y, rel_yaw, rel_speed]withrel_xpointing along the ego heading,rel_yto its left, andrel_yawwrapped to[-pi, pi].- Parameters:
ego_x (
float) – Ego x position in the map frame (m).ego_y (
float) – Ego y position in the map frame (m).ego_yaw_rad (
float) – Ego heading (rad).other_x (
float) – Other agent x position in the map frame (m).other_y (
float) – Other agent y position in the map frame (m).other_yaw_rad (
float) – Other agent heading (rad).other_speed (
float) – Other agent speed (m/s).
- Return type:
- Returns:
The ego-frame present slot row for the agent.
Subpackages
- POMDPPlanners.environments.nuplan_pomdp.nuplan_generative_models package
FactoredNuPlanModelPOMDPFactoredNuPlanModelPOMDP.observation_modelsFactoredNuPlanModelPOMDP.desired_speedFactoredNuPlanModelPOMDP.out_lane_threshFactoredNuPlanModelPOMDP.collision_penaltyFactoredNuPlanModelPOMDP.initial_observation_dist()FactoredNuPlanModelPOMDP.initial_state_dist()FactoredNuPlanModelPOMDP.is_terminal()FactoredNuPlanModelPOMDP.reward()FactoredNuPlanModelPOMDP.sample_next_state()FactoredNuPlanModelPOMDP.transition_log_probability()
KinematicNuPlanModelPOMDPKinematicNuPlanModelPOMDP.dtKinematicNuPlanModelPOMDP.wheelbaseKinematicNuPlanModelPOMDP.dragKinematicNuPlanModelPOMDP.collision_gapKinematicNuPlanModelPOMDP.collision_halfwidthKinematicNuPlanModelPOMDP.safe_distanceKinematicNuPlanModelPOMDP.stop_gapKinematicNuPlanModelPOMDP.is_terminal()KinematicNuPlanModelPOMDP.reward()KinematicNuPlanModelPOMDP.sample_next_state()KinematicNuPlanModelPOMDP.sample_next_state_batch()
NuPlanModelPOMDPNuPlanModelPOMDP.action_presetsNuPlanModelPOMDP.max_tracked_agentsNuPlanModelPOMDP.observation_modelsNuPlanModelPOMDP.encode_observation()NuPlanModelPOMDP.get_actions()NuPlanModelPOMDP.hash_action()NuPlanModelPOMDP.hash_observation()NuPlanModelPOMDP.is_equal_observation()NuPlanModelPOMDP.observation_log_probability()NuPlanModelPOMDP.sample_next_state()NuPlanModelPOMDP.sample_observation()NuPlanModelPOMDP.transition_log_probability()
- Submodules
- POMDPPlanners.environments.nuplan_pomdp.nuplan_generative_models.nuplan_factored_model_pomdp module
FactoredNuPlanModelPOMDPFactoredNuPlanModelPOMDP.observation_modelsFactoredNuPlanModelPOMDP.desired_speedFactoredNuPlanModelPOMDP.out_lane_threshFactoredNuPlanModelPOMDP.collision_penaltyFactoredNuPlanModelPOMDP.initial_observation_dist()FactoredNuPlanModelPOMDP.initial_state_dist()FactoredNuPlanModelPOMDP.is_terminal()FactoredNuPlanModelPOMDP.reward()FactoredNuPlanModelPOMDP.sample_next_state()FactoredNuPlanModelPOMDP.transition_log_probability()
- POMDPPlanners.environments.nuplan_pomdp.nuplan_generative_models.nuplan_kinematic_model_pomdp module
KinematicNuPlanModelPOMDPKinematicNuPlanModelPOMDP.dtKinematicNuPlanModelPOMDP.wheelbaseKinematicNuPlanModelPOMDP.dragKinematicNuPlanModelPOMDP.collision_gapKinematicNuPlanModelPOMDP.collision_halfwidthKinematicNuPlanModelPOMDP.safe_distanceKinematicNuPlanModelPOMDP.stop_gapKinematicNuPlanModelPOMDP.is_terminal()KinematicNuPlanModelPOMDP.reward()KinematicNuPlanModelPOMDP.sample_next_state()KinematicNuPlanModelPOMDP.sample_next_state_batch()
- POMDPPlanners.environments.nuplan_pomdp.nuplan_generative_models.nuplan_model_pomdp module
NuPlanModelPOMDPNuPlanModelPOMDP.action_presetsNuPlanModelPOMDP.max_tracked_agentsNuPlanModelPOMDP.observation_modelsNuPlanModelPOMDP.encode_observation()NuPlanModelPOMDP.get_actions()NuPlanModelPOMDP.hash_action()NuPlanModelPOMDP.hash_observation()NuPlanModelPOMDP.is_equal_observation()NuPlanModelPOMDP.observation_log_probability()NuPlanModelPOMDP.sample_next_state()NuPlanModelPOMDP.sample_observation()NuPlanModelPOMDP.transition_log_probability()
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception package
EgoObservationModelFactoredAgentObservationModelFactoredAgentObservationModel.max_tracked_agentsFactoredAgentObservationModel.perception_rangeFactoredAgentObservationModel.occlusion_radiusFactoredAgentObservationModel.pose_stdFactoredAgentObservationModel.detect_probFactoredAgentObservationModel.channelFactoredAgentObservationModel.log_probability()FactoredAgentObservationModel.perceive()FactoredAgentObservationModel.render()FactoredAgentObservationModel.supports_density
NuPlanObservationModelavailable_observation_models()build_observation_model()register_observation_model()- Subpackages
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_models package
EgoObservationModelFactoredAgentObservationModelavailable_observation_models()build_observation_model()register_observation_model()- Submodules
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_models.agent_models module
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_models.ego_models module
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_models.registry module
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_models package
- Submodules
- POMDPPlanners.environments.nuplan_pomdp.nuplan_perception.observation_model module
Submodules
POMDPPlanners.environments.nuplan_pomdp.nuplan_belief module
Plain particle belief that stamps the observed agent block onto its particles.
Perception lives on the planner’s generative model, not here and not in the world: the
forward-only NuPlanPOMDP emits a
raw, ground-truth observation, and the model’s
encode_observation()
degrades it into the perceived observation the belief receives, so the agents channel the
belief sees is already the tracked object list. The belief therefore does no perception at all.
It still cannot be a bare particle filter, though: a weight-only update can reweight and propagate
the agents a particle was seeded with, but it can never acquire a vehicle that appears
mid-episode, and a slot seeded empty stays empty forever. PerceivedAgentsBelief closes
that gap in the minimal way — after the ordinary particle-filter weight update and resample, it
replaces every particle’s ego-frame agent block with the observation’s agents block (plus
optional per-particle pose jitter for diversity), leaving the ego block to the filter and any
trailing light slot untouched. The perceived agents are the observation’s estimate, trusted
rather than re-filtered as a per-particle latent.
The returned belief is itself a PerceivedAgentsBelief, so the stamping repeats on every
step of the episode.
Note
The belief’s max_tracked_agents must match the width of the observation’s agents
block, since that block is written straight into each particle’s fixed agent slots.
- Classes:
PerceivedAgentsBelief: Particle belief that stamps the observed agent block onto particles.
- class POMDPPlanners.environments.nuplan_pomdp.nuplan_belief.PerceivedAgentsBelief(particles, log_weights, max_tracked_agents=5, agent_pose_jitter=0.3, resampling=True, ess_factor=0.5)[source]
Bases:
WeightedParticleBeliefReinvigorationWeighted particle belief that stamps the observation’s agent block onto every particle.
After the standard particle-filter weight update and resample, the reinvigoration step writes the current observation’s
agentsblock into every particle’s agent slots (plus optional per-particle jitter), leaving the ego block to the filter and any trailing light slot untouched. The belief holds no perception state — perception is the planner model’s, applied upstream byencode_observation— so a plain observation with a perceivedagentsblock is all it needs.- Parameters:
- max_tracked_agents
Number of fixed agent slots carried in each particle.
- agent_pose_jitter
Std of Gaussian noise added to each stamped agent’s
[rel_x, rel_y, rel_yaw, rel_speed]pose, for particle diversity.
Example
>>> import numpy as np >>> np.random.seed(0) >>> from POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp import ( ... AGENT_SLOT_WIDTH, EGO_STATE_WIDTH) >>> width = EGO_STATE_WIDTH + 1 * AGENT_SLOT_WIDTH >>> particles = [np.zeros(width) for _ in range(4)] >>> belief = PerceivedAgentsBelief( ... particles=particles, ... log_weights=np.log(np.ones(4) / 4), ... max_tracked_agents=1, ... ) >>> observation = { # a perceived agent 8 m ahead ... "ego": np.zeros(7), ... "agents": np.array([1.0, 8.0, 0.0, 0.0, 5.0]), ... } >>> base = WeightedParticleBelief(particles=particles, log_weights=belief.log_weights) >>> refreshed = belief.reinvigorate("noop", observation, None, base) >>> bool(np.asarray(refreshed.particles)[0, EGO_STATE_WIDTH] == 1.0) # slot now present True
- reinvigorate(action, observation, pomdp, belief)[source]
Stamp the observation’s perceived agent block onto every particle.
- Return type:
- Parameters:
action (Any)
observation (Any)
pomdp (Environment | None)
belief (WeightedParticleBelief)
POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp module
nuPlan POMDP world environment.
This module adapts the nuPlan closed-loop planning
simulator to the POMDPPlanners Environment
interface so it can serve as the ground-truth world in an
EpisodeRunner.
nuPlan is forward-only: a Simulation advances a single true state one
iteration per call, propagating the ego under a planned trajectory while reactive
background agents (IDM) respond. It cannot be queried for a transition/observation
density nor re-run from an arbitrary injected state, so it cannot act as a planner’s
generative model. In the two-environment episode design the planner keeps its own
generative model (policy.environment) and this wrapper only advances the single
true state forward, one step per real interaction. Consequently
NuPlanPOMDP.transition_log_probability() and
NuPlanPOMDP.observation_log_probability() intentionally raise
NotImplementedError — in the intended world/model split they are never called.
Unlike a fully-observed gym wrapper (observation equals state), nuPlan is genuinely
partially observed: the ego reads its own proprioception plus a tracked-object list
(DetectionsTracks) of the nearest agents, not the world’s full ground truth.
The state is the ego vehicle’s ground-truth kinematics and lane pose,
[x, y, yaw, vx, vy, lat, heading_err], followed by fixed slots for the
``max_tracked_agents`` nearest other agents (ground truth). Each agent slot is
[present, rel_x, rel_y, rel_yaw, rel_speed] expressed in the ego frame
(rel_x forward, rel_y left, rel_yaw in radians, rel_speed in
m/s); present is 1 for a filled slot and 0 for padding. The ego part is
read straight from the simulator, where:
x,y: ego rear-axle position in the map frame, in metres.yaw: ego heading about the map Z axis, in radians (nuPlan convention).vx,vy: ego linear-velocity components in the map frame, in metres per second.lat: signed lateral offset from the centre of the ego’s route baseline, in metres (positive to the baseline’s left).heading_err: ego heading minus the route-baseline direction, wrapped to[-pi, pi], in radians.
The state ends with one traffic-light slot,
[present, rel_x, rel_y, state_code, time_to_change] (ego frame; state_code is a
TRAFFIC_LIGHT_* code, time_to_change in seconds), carrying the light governing the
ego lane as ground truth (present == 0 when none affects it). It is always in the state
and is independent of whether the observation exposes the light.
(The vertical axis z and roll/pitch are intentionally omitted; the ego is modelled on
the ground plane.)
The lane-relative terms (lat, heading_err) drive a gym-carla-style driving-quality
reward: it rewards longitudinal progress along the route while penalising overspeed,
drifting off the baseline, and harsh / high-speed steering, plus a per-step time cost and a
terminal collision penalty. See REWARD_SPEED_WEIGHT and the sibling weights.
The observation is a multi-modal dict of native nuPlan payloads:
"ego"(always present): the ego’s proprioceptive kinematics[x, y, yaw, vx, vy, lat, heading_err]— nuPlan gives the ego near-perfect self-localisation, so this is the ego measurement channel."agents"(always present): themax_tracked_agentsagent slots of the state flattened, reported raw at their true ego-frame poses. The world applies no perception, so this is the ground-truth channel; range-gating, occlusion and sensor noise are the planner model’s observation model, not the world’s.
Any measurement noise is the planner model’s; the wrapper adds none.
- Classes:
NuPlanPOMDP: Forward-only adapter exposing a nuPlan session as a world Environment.
- class POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.NuPlanPOMDP(discount_factor, scenario_loader=None, action_presets=None, max_tracked_agents=5, simulation_horizon=8.0, fixed_delta_seconds=0.1, reactive_agents=True, collision_distance=2.0, collision_penalty=100.0, desired_speed=8.0, out_lane_thresh=2.0, observation_extractor=None, seed=None, name=None, reward_range=None, output_dir=None, debug=False, use_queue_logger=False)[source]
Bases:
EnvironmentForward-only adapter exposing a nuPlan closed-loop session as a world POMDP.
The wrapper drives a nuPlan
Simulationas the ground-truth world of an episode. It advances the simulator exactly one iteration per real interaction and serves the resulting next state, observation and reward from a small cache, because the POMDPPlanners episode loop requests those three quantities through separate method calls while nuPlan produces them atomically. The state is the ego vehicle’s ground-truth kinematics; the observation is the ego proprioception plus a tracked-object list, so the world is genuinely partially observed. See the module docstring for the exact state and observation variables, units, and frames.Note
This is a world environment, not a generative model. It cannot sample a transition from an arbitrary state, so belief particle propagation and density queries are unsupported and raise
NotImplementedError/RuntimeError. Pair it with a generative model environment on the planner (policy.environment).- Parameters:
discount_factor (float)
max_tracked_agents (int)
simulation_horizon (float)
fixed_delta_seconds (float)
reactive_agents (bool)
collision_distance (float)
collision_penalty (float)
desired_speed (float)
out_lane_thresh (float)
observation_extractor (Callable[[Dict[str, ndarray]], Any] | None)
seed (int | None)
name (str | None)
output_dir (Path | None)
debug (bool)
use_queue_logger (bool)
- action_presets
Discrete
(acceleration, steering_angle)control pairs.
- max_tracked_agents
Number of nearest agents carried in state/observation.
- seed
Optional seed applied to the first
resetfor reproducibility.
Example
The environment is used as the forward-only world of an
EpisodeRunner, paired with a separate generative model on the planner. It requires the nuPlan devkit and a scenario loader, so this snippet is illustrative rather than executed:env = NuPlanPOMDP(discount_factor=0.95, scenario_loader=load_scenario) state = env.initial_state_dist().sample()[0] next_state, observation, reward = env.sample_next_step(state, 0)
- compute_metrics(histories)[source]
Compute nuPlan driving-quality metrics from episode histories.
- Parameters:
- Returns:
collision_rate: fraction of episodes that ended in a collision.average_progress: mean per-episode ground distance travelled (m).average_speed: mean ego speed over the driven trajectory (m/s).near_miss_count: mean number of near-miss events per episode.min_vehicle_distance: mean over episodes of the closest the ego came to any agent (m); episodes that saw no agent are excluded.
- Return type:
- hash_action(action)[source]
Return a hashable key consistent with action equality.
Used by tree-search planners to index action children of a belief node in O(1). The returned key MUST satisfy:
action_a == action_b (per env's notion of equality) ==> hash_action(action_a) == hash_action(action_b)
Subclasses with non-hashable actions (e.g.
np.ndarray) must override to return a hashable surrogate (tobytes()is the standard choice for ndarray actions, which mirrors thenp.array_equalsemantics used by the linear-scan fallback).
- hash_observation(observation)[source]
Return a hashable key consistent with
is_equal_observation().Used by tree-search planners to index belief children by observation in O(1). The returned key MUST satisfy the contract:
is_equal_observation(a, b) implies hash_observation(a) == hash_observation(b)
- Parameters:
observation (
Any) – Observation to hash.- Returns:
the observation itself when it is already hashable).
- Return type:
- Raises:
NotImplementedError – If the observation is not hashable and the subclass has not provided an override. Subclasses with non-hashable observations (e.g.
np.ndarray) MUST override.
- initial_observation_dist()[source]
Get the initial observation distribution.
- Return type:
- Returns:
Distribution over initial observations
Note
Subclasses must implement this method to define initial observations.
- initial_state_dist()[source]
Get the initial state distribution.
- Return type:
- Returns:
Distribution over initial states
Note
Subclasses must implement this method to define the starting distribution.
- is_equal_observation(observation1, observation2)[source]
Check if two observations are equal.
- Parameters:
- Return type:
- Returns:
True if observations are considered equal, False otherwise
Note
Subclasses must implement this method to define observation equality. This is particularly important for discrete observation spaces.
- is_terminal(state)[source]
Check if a state is terminal.
- Parameters:
state (
Any) – State to check for terminal condition- Return type:
- Returns:
True if the state is terminal, False otherwise
Note
Subclasses must implement this method to define terminal conditions.
- observation_log_probability(next_state, action, observations)[source]
Log-probability of each candidate observation under
(next_state, action).Returns
np.ndarrayof shape(N,)where N is the number of candidate observations. Subclasses must implement.
- reward(state, action, next_state=None)[source]
Calculate the immediate reward for a state-action(-next_state) tuple.
next_stateis the realised post-transition state when known (e.g. threaded bysample_next_step()), allowing rewards that depend on stochastic transition outcomes to use the same draw as the trajectory instead of resampling. Subclasses whose reward is a pure function of(state, action)may ignore it; subclasses whose reward depends on the realised next state (collision penalties, win bonuses) should consume it when provided and fall back to drawing/computing one whenNone.- Parameters:
- Return type:
- Returns:
Immediate reward value.
Note
Subclasses must implement this method to define reward structure.
- sample_next_state(state, action, n_samples=1)[source]
Sample one or more next states for
(state, action).Hot-path entry point used by MCTS planners and particle filters. Subclasses must implement.
- sample_observation(next_state, action, n_samples=1)[source]
Sample one or more observations for
(next_state, action).Hot-path entry point used by MCTS planners and particle filters. Subclasses must implement.
- class POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.NuPlanPOMDPMetrics(*values)[source]
Bases:
EnumMetric names for the nuPlan POMDP environment.
- AVERAGE_PROGRESS = 'average_progress'
- AVERAGE_SPEED = 'average_speed'
- COLLISION_RATE = 'collision_rate'
- MIN_VEHICLE_DISTANCE = 'min_vehicle_distance'
- NEAR_MISS_COUNT = 'near_miss_count'
- POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.assemble_state(ego_row, agent_rows, max_tracked_agents, light_row=None)[source]
Concatenate an ego row, padded agent slots, and a light slot into a state vector.
Pure numeric assembly of the nuPlan state layout, factored out of the live session so the state geometry can be exercised without a nuPlan installation. Agent rows are written into the nearest fixed slots (already ego-frame); missing slots are padded with zeros (
present == 0).- Parameters:
ego_row (
Union[Sequence[float],ndarray]) – TheEGO_STATE_WIDTHego block[x, y, yaw, vx, vy, lat, heading_err].agent_rows (
Union[Sequence[Sequence[float]],Sequence[ndarray]]) – Zero or more ego-frame agent rows[present, rel_x, rel_y, rel_yaw, rel_speed]; only the firstmax_tracked_agentsare kept.max_tracked_agents (
int) – Number of fixed agent slots to emit.light_row (
Union[Sequence[float],ndarray,None]) – OptionalLIGHT_SLOT_WIDTHtraffic-light slot; a zero (absent) slot is emitted whenNone.
- Return type:
- Returns:
The full state vector of width
EGO_STATE_WIDTH + max_tracked_agents * AGENT_SLOT_WIDTH + LIGHT_SLOT_WIDTH.
- POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.driving_quality_reward(next_state, steering_angle, terminated, desired_speed, out_lane_thresh, collision_penalty)[source]
Score a transition with a gym-carla-style driving-quality reward.
Rewards along-route progress and penalises overspeed, drifting off the route baseline, harsh / high-speed steering, each elapsed step, and a terminal collision. Shared by the
NuPlanPOMDPworld and the planner-side factored model so the two score a transition identically by construction.- Parameters:
next_state (
ndarray) – Resulting ego state[x, y, yaw(rad), vx, vy, lat, heading_err].steering_angle (
float) – Steering command applied on the transition (from the action preset).terminated (
bool) – Whether the transition ended in a terminal collision.desired_speed (
float) – Target longitudinal speed (m/s); exceeding it is penalised.out_lane_thresh (
float) – Lateral offset (m) beyond which the ego is treated as off-route.collision_penalty (
float) – Penalty scale applied on a terminal collision.
- Return type:
- Returns:
The scalar reward for the transition.
- POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.relative_agent_row(ego_x, ego_y, ego_yaw_rad, other_x, other_y, other_yaw_rad, other_speed)[source]
Express another agent’s pose/speed in the ego frame as a present slot row.
Returns
[1.0, rel_x, rel_y, rel_yaw, rel_speed]withrel_xpointing along the ego heading,rel_yto its left, andrel_yawwrapped to[-pi, pi].- Parameters:
ego_x (
float) – Ego x position in the map frame (m).ego_y (
float) – Ego y position in the map frame (m).ego_yaw_rad (
float) – Ego heading (rad).other_x (
float) – Other agent x position in the map frame (m).other_y (
float) – Other agent y position in the map frame (m).other_yaw_rad (
float) – Other agent heading (rad).other_speed (
float) – Other agent speed (m/s).
- Return type:
- Returns:
The ego-frame present slot row for the agent.
- POMDPPlanners.environments.nuplan_pomdp.nuplan_pomdp.wrap_to_pi(angle)[source]
Wrap an angle in radians to the
[-pi, pi]interval.Every angle in the nuPlan state/observation layout (ego
yawandheading_err, each agent slot’srel_yaw) carries this invariant, so world, model and belief all route their angle arithmetic through here.