Dynamics & world
The world state, ego physics, map geometry, pedestrians, collisions.
Duckietown._actual_center — Method
_actual_center(pos, angle) -> Vector{Float64}Geometric centre of the agent (camera-to-wheelbase offset): pos + (CAMERA_FORWARD_DIST − ROBOT_LENGTH/2)·dir_vec (simulator._actual_center).
Duckietown._collision — Method
_collision(map, agent_corners, ducks) -> BoolSimulator._collision: SAT against all stacked static objects, then a per-object SAT against every dynamic duckie.
Duckietown._drivable_pos — Method
_drivable_pos(map, pos) -> BoolSimulator._drivable_pos: the tile at pos exists and is drivable.
Duckietown._inconvenient_spawn — Function
_inconvenient_spawn(map, pos, ducks) -> BoolSimulator._inconvenient_spawn: any visible object is within max(|max_coords|) · 0.5 · scale + MIN_SPAWN_OBJ_DIST of the candidate pose (3D norm; max_coords is the mesh bounding-box corner).
Duckietown._valid_pose — Function
_valid_pose(map, pos, angle, safety_factor=1.0) -> BoolSimulator._valid_pose: geometric centre, both wheels and the front of the agent on drivable tiles, and no collision with static or dynamic objects.
Duckietown.agent_boundbox — Method
agent_boundbox(true_pos, width, length, f_vec, r_vec) -> Matrix{Float64}Agent bounding box (4×2 x/z corners; collision.agent_boundbox, verbatim). Order: rear-left, rear-right, front-right, front-left (front at the end).
Duckietown.calculate_safety_radius — Method
calculate_safety_radius(min_coords, max_coords, scale) -> Float64objects.calculate_safety_radius: norm of the per-axis maximum absolute extent (x/z), scaled.
The reference mesh bounding boxes are float32 (duckietown-world obj meshes), and np.max([abs(min), abs(max)], axis=0) keeps that dtype, so the whole norm is computed in float32 before promotion by the float64 scale. The norm is evaluated as sqrt(x² + z²) (NumPy's naive reduction), not the BLAS hypot-style norm.
Duckietown.corner_matrix — Method
corner_matrix(corners) -> Matrix{Float64}Convert a Vector{NTuple{2,Float64}} corner list into the 4×2 matrix form.
Duckietown.cov_bias — Method
cov_bias(corners) -> Matrix{Float64}Population covariance (np.cov(rowvar=false, bias=true)) of the 4×2 corner matrix: 1/4 of the centred outer products, summed over corners.
Duckietown.generate_corners — Method
generate_corners(pos, min_coords, max_coords, theta, scale) -> MatrixObject bounding-box corners (4×2, x/z columns) in the exact order of objects.generate_corners: (min,min), (max,min), (max,max), (min,max), rotated about the object anchor.
Duckietown.generate_norm — Method
generate_norm(corners) -> Matrix{Float64}Two orthogonal axis normals (2×2) of a rectangle given its corners in generate_corners order.
Python computes np.cov(corners, rowvar=false, bias=true) followed by np.linalg.eig and returns vect.T (eigenvectors as rows). np.linalg.eig is LAPACK dgeev, whose eigenvector ORDER (natural, NOT sorted by eigenvalue) and SIGN conventions are part of the recorded reference state (DuckieObj.obj_norm is compared bit-level in the duckie parity fixtures), so Julia calls the same LAPACK driver rather than an analytic formula. An earlier analytic implementation produced axes that were equivalent for the SAT tests but ordered/signed differently — a real FJ3.4 state-parity bug that the fixture comparison caught.
Duckietown.get_agent_corners — Method
get_agent_corners(pos, angle) -> Matrix{Float64}simulator.get_agent_corners: bounding box of the geometric centre.
Duckietown.heading_vec — Method
heading_vec(angle) -> NTuple{3,Float64}Forward vector of an object at world heading angle: (cos, 0, -sin) (objects.heading_vec, verbatim).
Duckietown.intersects — Method
intersects(agent_corners, objs_stacked, agent_norm, norms_stacked) -> BoolTensor SAT against N stacked static objects (simulator.intersects, verbatim: every pair of projection intervals must overlap for a collision).
Duckietown.intersects_single_obj — Method
intersects_single_obj(agent_corners, obj_corners_T, agent_norm, obj_norm) -> BoolSAT against one dynamic object (objects.intersects_single_obj, verbatim; obj_corners_T is the object's 2×4 corner matrix).
Duckietown.rotate_point — Method
rotate_point(px, py, cx, cy, theta) -> (x, z)Rotate a 2D point around a center (graphics.rotate_point, verbatim).
Duckietown.tensor_sat_test — Method
tensor_sat_test(norm, corners) -> (mins, maxs)Projection intervals of corners (4×2 or stacked) on each axis of norm (2×2), min/max over the corner axis (collision.tensor_sat_test, verbatim).
Duckietown.tile_corners — Method
tile_corners(pos2, width) -> Matrix{Float64}Absolute corners of a tile given grid coordinates (i, j) and tile width (simulator.tile_corners, verbatim: [px·w − w, px·w + w, ...]).
Duckietown.DB18Parameters — Type
DB18ParametersNominal DB18 platform model: autonomous response (u1..w3), forced response gains (uar..wal), wheel radii R = 0.067/2, wheel distance D = 0.1, encoder resolution 2π/135.
Duckietown.DelayedCommand — Type
DelayedCommand(idx, told, used)Selection result: 0-based idx, the timestamp at the decision boundary (told), and the command (motor_left, motor_right) that gets applied.
Duckietown.SE2_from_se2 — Method
SE2_from_se2(vel) -> 3×3 MatrixExponential map (poses.SE2_from_se2, Bullo/Murray; |w| < 1e-8 branch).
Duckietown.SE2_from_translation_angle — Method
SE2_from_translation_angle(t, theta) -> 3×3 MatrixPose from translation and rotation (poses.SE2_from_translation_angle).
Duckietown.axis_observed_ticks — Method
axis_observed_ticks(axis_rad, p) -> (left, right)Encoder ticks int(round(axis_rad / resolution)) (Python round = round-half-to-even), used for wheel-speed observations.
Duckietown.cartesian_from_weird — Method
cartesian_from_weird(pos, angle, grid_height, tile_size) -> 3×3 Matrixpos = (x, y, z) -> SE(2) pose with cp = (x, grid_height * tile_size - z).
Duckietown.db18_model — Method
db18_model(p, commands, u, w) -> (x_ddot_long, x_ddot_ang)Body acceleration from motor commands and previous (u = longitudinal, w = angular) velocities (DynamicModel.model, verbatim). Commands are (motor_left, motor_right), each clipped to [-1, 1].
Duckietown.ego_tick — Method
ego_tick(world, action) -> DuckieWorldStateOne 1/30 s physics tick under action = (motor_left, motor_right) (clipped to [-1, 1]); returns a fresh world state (no shared mutation, branch-pure).
Duckietown.get_commands_at — Method
get_commands_at(history, t) -> DelayedCommandbisect_left on the command timestamps with the reference nearest-neighbour tie-break: when the previous timestamp is strictly closer than the next, the previous command is applied. Before the first timestamp, u0 = (0, 0). history entries are (t, u_L, u_R) in increasing t order.
Duckietown.initial_ego — Method
initial_ego(pos, angle, grid_height, tile_size) -> DuckieEgoStateFresh ego at rest: zero se(2) velocity, zero axis radians, empty command history, t0 = 0 (Simulator.reset state construction, verbatim).
Duckietown.se2_from_linear_angular — Method
se2_from_linear_angular(linear, angular) -> 3×3 MatrixBody velocity as se(2) (poses.se2_from_linear_angular).
Duckietown.se2_multiply — Method
se2_multiply(g, h) -> 3×3 MatrixMatrix-group product (SE2.multiply = np.dot(g, h)).
Duckietown.translation_angle_from_SE2 — Method
translation_angle_from_SE2(q) -> (t, angle)(poses.translation_angle_from_SE2; angle == pi maps to -pi exactly.)
Duckietown.weird_from_cartesian — Method
weird_from_cartesian(q, grid_height, tile_size) -> ((x, 0, z), angle)Inverse mapping; angle == pi is normalised to -pi inside translation_angle_from_SE2.
Duckietown.LanePosition — Type
LanePositionLane-relative pose (simulator.LanePosition): dist signed lateral offset (right negative), dot_dir clipped heading·tangent dot product, angle_deg/angle_rad signed heading error (right negative).
Duckietown.NotInLane — Type
NotInLaneRaised by get_lane_pos2 when the agent is not in a lane (simulator.NotInLane). Callers that are not on a drivable tile get nothing from closest_curve_point instead.
Duckietown._normalize_1e12 — Method
_normalize_1e12(vector) -> Vector{Float64}src/continuous_state.py::_normalize: divide by the Euclidean norm if it is > 1e-12, else return a zero vector (stricter than the > 0 variant used in state.py; both are ported faithfully).
Duckietown.bezier_closest — Function
bezier_closest(curve, p, t_bot=0.0, t_top=1.0, n=8) -> Float64Recursive midpoint search for the parameter of the closest point on a cubic Bezier (graphics.bezier_closest, verbatim: bisection depth 8, d_bot < d_top keeps the lower half).
Duckietown.bezier_point — Method
bezier_point(curve, t) -> Vector{Float64}Cubic Bezier point at parameter t ∈ [0, 1] for a 4×3 control-point matrix (rows = points, columns = x,y,z). Verbatim arithmetic port of gym_duckietown.graphics.bezier_point (pinned duckietown-gym-daffy-6.1.34): the same term order, so results are bit-identical for identical inputs (FJ2 parity fixture).
Duckietown.bezier_tangent — Method
bezier_tangent(curve, t) -> Vector{Float64}Unit tangent at parameter t (first derivative of the cubic Bezier), ported verbatim from gym_duckietown.graphics.bezier_tangent — including the unprotected division by the norm: a zero-length segment yields NaN entries, exactly as NumPy's p /= norm does (IEEE semantics).
Duckietown.closest_curve_point — Method
closest_curve_point(map, pos, angle) -> (Union{Nothing,Vector{Float64}}, Union{Nothing,Vector{Float64}})Simulator.closest_curve_point: closest point and unit tangent on the lane curve best aligned with the heading (largest dot(curve_heading, dir_vec)), or (nothing, nothing) off-drivable tiles.
curve_headings are normalised by the single Frobenius norm of the whole stack (NumPy semantics), exactly like the reference.
Duckietown.curve_matrix — Method
curve_matrix(curve) -> Matrix{Float64}Convert one Vector{NTuple{3,Float64}} control polygon (the form stored in TileSpec.curves) into the 4×3 matrix the Bezier helpers expect.
Duckietown.curve_signed_curvature — Method
curve_signed_curvature(curve, samples=33, straight_angle_threshold=0.05) -> Float64Average signed curvature of a directed Bezier lane: heading change between t = 0.05 and t = 0.95 divided by the arc length of samples sampled points (src/continuous_state.py::curve_signed_curvature). The sign is the y-component of the tangent cross product (left turns positive). Headings with |Δ| ≤ straight_angle_threshold are 0.0 (straight); zero arc length yields 0.0; samples < 3 raises ArgumentError (Python: ValueError). NaN propagation from degenerate control polygons is IEEE-identical.
Duckietown.get_dir_vec — Method
get_dir_vec(angle) -> NTuple{3,Float64}Forward vector (cos, 0, -sin) (simulator.get_dir_vec, verbatim).
Duckietown.get_lane_pos2 — Method
get_lane_pos2(map, pos, angle) -> LanePositionSimulator.get_lane_pos2: signed lateral distance and signed heading error relative to the closest point of the right-lane curve. Throws NotInLane off-lane (the reference raises it for non-drivable tiles).
Duckietown.get_right_vec — Method
get_right_vec(angle) -> NTuple{3,Float64}Right vector (sin, 0, cos) (simulator.get_right_vec, verbatim).
Duckietown.stack_curves — Method
stack_curves(curves) -> Array{Float64,3}Stack the per-tile curve control polygons (Vector{Matrix{Float64}}) into the (N,4,3) array used by closest_curve_point.
Duckietown.MAP_DIRECTIONS — Constant
directions_index(orient) -> Int["S", "E", "N", "W"].index(orient) (0..3) — _interpret_map.
Duckietown._get_tile — Method
_get_tile(map, i, j) -> Union{Nothing,TileSpec}Simulator._get_tile: bounds-checked lookup with 0-based grid coordinates (i, j) (matching get_grid_coords); returns nothing for tiles outside the map.
Duckietown.gen_rot_matrix — Method
gen_rot_matrix(axis, angle) -> Matrix{Float64}Quaternion rotation matrix (graphics.gen_rot_matrix, verbatim).
Duckietown.get_grid_coords — Method
get_grid_coords(map, pos) -> (Int, Int)Simulator.get_grid_coords: (floor(x/ts), floor(z/ts)), potentially outside the grid.
Duckietown.interpret_object_desc — Method
interpret_object_desc(; kind, pos, rotate_deg, height=nothing, scale=nothing,
static=true, optional=false) -> MapObjectDataDuckietownEnv.interpret_object for the descriptors produced by duckduck.src.duck_controller.prepare_task_map_data: pos is already the world-frame anchor, rotate_deg the world yaw in degrees, and scale is height / mesh.max_coords[2] when height is given (else scale). Throws ArgumentError when both height and scale are given (Python: assert ... cannot specify both height and scale).
Duckietown.map_kind_symbol — Method
map_kind_symbol(kind_s) -> SymbolSimulator tile-kind string → canonical symbol: 3way_left/3way_right/ 4way become :three_way_left/:three_way_right/:four_way. (Not to be confused with state_projection.classify_tile → TileType, the FJ2 lane-class projection.)
Duckietown.object_world_pose — Method
object_world_pose(grid_width, grid_height, tile_size, tile_pos, rotate_deg)
-> (pos3, angle)Map-frame object descriptor (pos in tile units, rotate in degrees) to the simulator's world anchor, verbatim per Simulator.interpret_object -> duckietown_world.map_loading.get_transform -> weird_from_cartesian:
x_cart = pos[1] * ts
y_cart = (grid_width - 1 - pos[2]) * ts # non-righthanded frame
angle = -deg2rad(rotate_deg)
world = (x_cart, 0, grid_height * ts - y_cart)For the square small_loop (3x3) this reduces to (pos[1] * ts, 0, (pos[2] + 1) * ts), which is where the injected stop sign [1.20, 2.10] -> (0.702, 1.8135) and duckie [1.62, 0.50] -> (0.9477, 0.8775) come from.
Duckietown.parse_map_tiles — Method
parse_map_tiles(tiles, tile_size) -> Matrix{TileSpec}Simulator._interpret_map for the grid only: each tile string ("curve_left/W", "asphalt", "empty", ...) becomes a TileSpec with its directed lane curves (drivable tiles only).
Duckietown.small_loop_map — Method
small_loop_map() -> RoadMapThe canonical experiment map: small_loop, tilesize 0.585, plus the injected stop sign at stopspawnpos [1.20, 2.10] rotated 180° (duckduck controller defaults; the duckie itself is dynamic state created by [`initialstate`](@ref)).
Duckietown.small_loop_tiles — Method
small_loop_tiles() -> Matrix{String}The small_loop map as tile strings, transcribed verbatim from duckietown_world/data/gd1/maps/small_loop.yaml (3×3, tilesize 0.585). Rows are j (z) rows, matching `interpret_map'stiles` layout.
Duckietown.tile_curves — Method
tile_curves(kind, angle_index, i, j, tile_size) -> Array{Float64,3}Simulator._get_curve: the tile's directed lane curves in world coordinates. angle_index is the tile orientation index (0..3, directions.index). The 4way template is rotated 4 times (rot·π/2 for rot ∈ 0:3); all others once by (angle_index·π)/2. Returns an (N,4,3) stack.
Duckietown.activate_duck — Method
activate_duck(world, i) -> DuckieWorldStateController activation: the duck starts walking (time = 0) and crossings_started[i] increments (DuckController.before_step core).
Duckietown.before_step — Method
before_step(world, cfg, rng) -> DuckieWorldState
before_step(world, cfg) -> DuckieWorldStateDecision-start trigger pass (DuckController.before_step, verbatim): pins wait_time to Inf for every inactive duck, then for each inactive, under-limit duck computes the crossing midpoint start + 0.5 * walk_distance * heading * sign(vel) and activates it when its distance to the ego is inside [trigger_min, trigger_max], the lane tangent at the ego pose says the crossing lies ahead, and the p_cross draw passes. Re-arming follows repeat_rearm_distance (0 disarms the limit test only via max_crossings_per_episode).
The 3-argument form draws from the external rng (mutating it) and never touches world.controller_rng — this is the generative-MDP semantics used by simulate_decision: stochasticity is supplied by the caller, not stored in the state. The RNG is consumed only on a fully eligible duck (inactive, under limit, armed, in the trigger window, crossing ahead), matching the reference call semantics draw-for-draw. The 2-argument form keeps the FJ3.5 stream-in-state behaviour: it draws from a copy of world.controller_rng and returns the advanced copy in the new world.
Duckietown.duck_step — Function
duck_step(world, i, dt=EGO_DT) -> DuckieWorldStateOne physics tick of duck i (DuckieObj.step, verbatim). Returns a fresh world state; inactive ducks only count down wait_time.
Duckietown.replace_duck — Method
replace_duck(world, i, duck) -> DuckieWorldStateFresh world with ducks[i] replaced; every other field is shared (the transition chain copies ducks before mutating, keeping branches pure).
Duckietown.DuckieEgoState — Type
DuckieEgoStateMinimal Markov-sufficient ego state of the simulator (FJ0 audit section C).
The DB18 motor model runs delayed dynamics (ApplyDelay, 0.15 s): the command applied at time t is the wheel command issued at t - 0.15. The delay window spans roughly 5 physics ticks (dt = 1/30 s), and therefore reaches into the previous decision when frame_skip = 6. Without the command history, the generative process would not be Markov — this field is the reason DuckieWorldState is branchable for MCTS/DPW.
pos: simulatorcur_pos,(x, 0, z)in world metres.angle: simulatorcur_angle, world heading (rad).v_long,omega: DB18 body-state linear/angular velocity of the underlyingDynamicModel.v0.speed: simulatorself.speed = norm(cur_pos - prev_pos) / delta_time(displacement speed of the last tick, NOTv_long) — this is thevthe raw-state extraction observes (env.speedinget_raw_state).step_count: physics ticks elapsed;timestamp: seconds elapsed.command_history:(t, u_L, u_R)wheel-command log over the delay window.q0: 3×3 SE(2) pose in cartesian coordinates (DB18 axis), i.e. the referenceDynamicModel.q0.v0: 3×3 se(2) body velocity of the last tick (the referencev0).axis_left_rad/axis_right_rad: accumulated wheel angles in radians.
Duckietown.DuckieState — Type
DuckieStateFull pedestrian (DuckieObj) sub-state.
pos,center,start: object anchor, walking position, walking origin.angle,heading: object yaw and forward vector(cos, 0, -sin).vel: walking speed magnitude (0.02 m/s in this setup);walk_distance: distance after whichfinish_walkflips heading and velocity.pedestrian_active: walking flag;pedestrian_wait_time: countdown that the DuckController pins toInfbefore every decision (no autonomous activation);time: seconds since activation.obj_corners: 2D (x, z) SAT corners (shifted during walking and restored by the controller reset — hence state, not derived data);obj_norm: 2×2 SAT axis normals;scale/safety_radius/min_coords/max_coords: mesh extent used by spawn checks and proximity rewards.
Duckietown.DuckieWorldState — Type
DuckieWorldStateThe canonical branchable latent dynamics state for generative planning (FJ0 audit section C; user constraint #2). It contains everything needed to reproduce the next decision deterministically:
- the ego (including the delayed-command window),
- all duckie objects and the crossing controller counters,
- the stop-sign poses, the stop memory, and the lane-fallback memory,
- the static map, and the controller RNG stream.
Projections onto the solver-facing states are pure functions: RawState = f_tab(world) and ContinuousState = f_cont(world) (FJ2/FJ3).
controller_rng is stored as a native MersenneTwister for now. Note: the Python stream is np.random.RandomState (MT19937 with legacy seeding); the exact stream compatibility needed for seed-exact parity is scheduled for FJ3 dynamics work.
Duckietown.MapObjectData — Type
MapObjectDataStatic object descriptor resolved during initial-state construction (FJ3.1).
Mirrors the fields the simulator carries per collidable WorldObj:
pos,angle,scale: object anchor pose (world frame).static,optional,visible: map-level flags (visible flips only under domain randomization).min_coords,max_coords: mesh bounding-box corners;safety_radius:SAFETY_RAD_MULT · norm(max(|min|, |max|) on x/z) · scale.corners: 4×2 SAT corners (objects.generate_cornersorder);norm: 2×2 axis normals;heading: forward vector(cos, 0, -sin).
Duckietown.RoadMap — Type
RoadMapStatic map data of one world (small_loop in all four experiments): tile size, grid of TileSpec, and the static collidable objects (sign_stop; duckies are dynamic state in DuckieWorldState). The map is immutable and shared across branch copies.
Duckietown.StopMemory — Type
StopMemoryWrapper-side stop memory needed by the reward process:
sigma_stop,hold_steps: the StopTracker dwell state (mirrored here so the world state is self-contained for branching).last_stop_id,last_d_stop: previous decision's stop-candidate identity and distance, required byStopTracker.update(prev, curr, prev_id, curr_id)for the "passed" test.
Duckietown.StopSignState — Type
StopSignStatePose of one static sign_stop object. Collision corners derive from the static mesh bounding box (FJ2 collision module).
Duckietown.TileSpec — Type
TileSpecOne map tile: kind (Python tile kind, lowercased; e.g. :straight, :curve_left, :curve_right, :3way_left, :4way, :asphalt), rotation in degrees, drivable flag, and the static directed lane curves (each a cubic Bézier control polygon of 4×3 points, in world coordinates).
Duckietown.branch — Method
branch(world, ego, ducks) -> DuckieWorldStateCopy a world state for simulated branching, deep-copying every mutable field (command history, duckie corners, counters, RNG stream) so that branch rollouts never alias shared mutation.