jaxdem.rl.environments.swarm_roller#
Environment where multiple rolling agents cooperatively cover a set of objectives.
Classes
|
Cooperative objective coverage with torque-controlled rolling spheres. |
- class jaxdem.rl.environments.swarm_roller.SwarmRoller(state: State, system: System, env_params: dict[str, Any], n_lidar_rays: int, num_objectives: int)#
Bases:
EnvironmentCooperative objective coverage with torque-controlled rolling spheres.
Objectives are unassigned points on a jittered grid at floor height; agents spawn in the surrounding padding ring. Reward and objective/peer LiDAR semantics match
SwarmNavigator. Rolling dynamics matchMultiRoller: gravity, frictional contact with the \(z=0\) floor, normal spring particle contacts, and the naive all-pairs collider. No tangential particle-contact history is stored.Each physics step smooths the requested torque:
\[\mathbf{u}_{i,t}=\alpha\mathbf{a}_{i,t} +(1-\alpha)\mathbf{u}_{i,t-1},\qquad \boldsymbol{\tau}_{i,t}=\mathbf{u}_{i,t}-\mu\boldsymbol{\omega}_{i,t}, \qquad \mathbf{f}_{i,t}=-\mu\mathbf{v}_{i,t}.\]action_alphasets \(\alpha\) (default 0.22);frictionsets \(\mu\). The previous applied torque belongs to the immediately preceding physics step, independent of checkpoints. Reset initializes it to zero. A constant request completes 99 percent of its transition in 19 physics steps with the default smoothing coefficient.The shared coverage potential and per-action reward are
\[\Phi_i(s)=\max_{k\in V_i}(w_{ik}B(d_{ik})) +\eta\sum_{k\in V_i}w_{ik}P(d_{ik}/a_i),\qquad r_{i,t}=\Phi_i(s_{t+1})-\Phi_i(s_{\mathrm{checkpoint}}).\]Here \(a_i\) is the particle radius, \(B(d)=\exp[-(d/(1.5a_i))^4]\), and \(w_{ik}=1/(1+C_{ik})\) shares objective value with locally estimated peer claims.
reward()documents the approach potential \(P\), claim estimate \(C\), parameters, and references. Empty scans give zero potential. There is no separate occupancy bonus or kinetic-energy term. Agent and objective counts may differ.By default, observations hide objectives below half the LiDAR range if another detected agent is estimated to cover them. Goals within the observer’s own radius remain visible. Reward always uses the raw scans.
prev_potentialstores only the action-start baseline; current LiDAR readings are computed from live state without persistent sensor caches.Notes
The observation vector per agent is:
Feature
Size
Planar velocity
2Angular velocity
3Normalized objective LiDAR
n_lidar_raysNormalized agent/wall LiDAR
n_lidar_raysWith the default 12 angular bins, each observation has 29 entries. Velocities are not normalized. As in
MultiRoller, the 2-D LiDAR utility bins by azimuth but uses full center distances in three dimensions. All centers initially lie at the same height, so these are planar ranges. If particles lift off the floor, the bin-center occupancy estimate ignores relative elevation and becomes less accurate.With
dt=0.002andskip_frames=49, a policy action spans 50 physics steps (0.1 seconds).jaxdem.utils.advance_actioncheckpoints once before these steps; reward is read before the next checkpoint or reset. Reset and unchanged states give zero reward. Methods describe one environment; usejax.vmapfor independent batches.Examples
>>> import jax >>> import jax.numpy as jnp >>> from jaxdem.rl import Environment >>> from jaxdem.utils import advance_action >>> env = Environment.create("swarmRoller") >>> env = env.reset(env, jax.random.key(0)) >>> action = jnp.zeros((env.max_num_agents, env.action_space_size)) >>> env, terminated, truncated = advance_action(env, action, skip_frames=49) >>> reward = env.reward(env) >>> observation = env.observation(env)
- n_lidar_rays: int#
Number of angular bins for each LiDAR sensor.
- num_objectives: int#
Number of objectives sampled per environment.
- classmethod Create(N: int = 64, num_objectives: int = 64, box_size: float = 20.0, box_padding: float = 10.0, max_steps: int = 10000, friction: float = 0.2, action_alpha: float = 0.22, lidar_range: float = 16.0, n_lidar_rays: int = 12, attraction_coeff: float = 0.1, attraction_constant: float = 0.01, attraction_quadratic: float = 0.02, attraction_decay: float = 0.3333333333333333, sharing_range: float = 1.5, hide_occupied_objectives: bool = True) SwarmRoller[source]#
Create a swarm roller with shared objective value and filtered LiDAR.
- Parameters:
N (int) – Number of agents.
num_objectives (int) – Number of objectives sampled per environment.
box_size (float) – Side length of the square domain that holds the objectives.
box_padding (float) – Total extra domain side length in particle radii. The spawn ring extends by half this amount on each side of the objective box.
max_steps (int) – Episode length in physics steps.
friction (float) – Translational drag and angular damping coefficient, applied as
-friction * veland-friction * ang_vel.action_alpha (float) – Fraction of the requested torque applied by the smoothing update each physics step, in
[0, 1]. Defaults to 0.22, as inMultiRoller. One disables smoothing.lidar_range (float) – Maximum detection range \(L\) for the LiDAR sensors.
n_lidar_rays (int) – Number of angular LiDAR bins spanning \([-\pi, \pi)\).
attraction_coeff (float) – Nonnegative weight of the summed approach potential, default 0.1. Peer claims reduce both approach and settling values. Zero disables only the approach term.
attraction_constant (float) – Nonnegative coefficients \(c,b\) of the attraction strength \((c+b x^2)e^{-\lambda x}\), with \(x=d/\mathrm{rad}_i\).
attraction_quadratic (float) – Nonnegative coefficients \(c,b\) of the attraction strength \((c+b x^2)e^{-\lambda x}\), with \(x=d/\mathrm{rad}_i\).
attraction_decay (float) – Positive exponential decay \(\lambda\) in radius units.
sharing_range (float) – Positive quartic claim scale in observing-agent radii. The default matches the settling scale, 1.5.
hide_occupied_objectives (bool) – Enabled by default. Hide readings strictly below half the LiDAR range when a detected peer appears within one observing-agent radius of the objective. Preserve goals within the observer’s radius. A hidden reading becomes zero; farther goals behind it are not revealed. Applies only to observations, never reward or checkpoints. Set to False to recover the unfiltered observation.
- Returns:
The constructed environment. Call
reset()before use.- Return type:
- Raises:
ValueError – If an attraction coefficient is nonfinite or negative, or
attraction_decayorsharing_rangeis nonfinite or nonpositive; oraction_alphais outside[0, 1].
Notes
Construction allocates placeholder agent and objective arrays. Reset samples the geometry and initializes the physical system and reward baseline.
N,num_objectives, andn_lidar_raysdetermine array shapes. The remaining controls are stored inenv_paramsas JAX arrays.
- static reset(env: SwarmRoller, key: Array | ndarray | bool | number | bool | int | float | complex) Environment[source]#
Sample floor-level agents and objectives and initialize rolling physics.
- Parameters:
env (SwarmRoller) – Environment retaining its configured geometry, sensor, actuator, and shared-reward parameters.
key (ArrayLike) – JAX random number generator key, split for agents and objectives.
- Returns:
Initialized environment with zero velocities and applied torque, restarted step count, and the initial potential checkpointed. Reward is initially zero.
- Return type:
Notes
Agents have unit radius and mass. Objectives and agents start at
z=1. The jittered grid requests center spacing 2.05; undersized cells cannot guarantee that separation. The domain’s horizontal side length isbox_size + box_padding; its lower vertical boundary is-1, allowing floor contact before boundary reflection. Reset installs gravity(0, 0, -1), the same frictional floor force as MultiRoller, elastic spring particle contacts, and a naive collider. The physics time step is 0.002. Objective coordinates of shape(num_objectives, 3)are stored inenv_params["objective"].
- static checkpoint(env: SwarmRoller, action: Array) Environment[source]#
Save the LiDAR-derived potential at the start of a policy action.
Physics steps preserve this potential through all skipped frames. The baseline is independent of
action. Read reward before the next checkpoint or reset replaces it. Unchanged states give zero reward.- Parameters:
env (SwarmRoller) – Environment immediately before the next policy action.
action (jax.Array) – Per-agent torque requests, normally shape
(N, 3). Accepted for the environment checkpoint interface; its value is unused.
- Returns:
Environment with
env_params["prev_potential"]replaced by the current per-agent potential of shape(N,). Physical state and observations are not advanced. The immediately preceding applied torque is preserved independently.- Return type:
- static step(env: SwarmRoller, action: Array) Environment[source]#
Smooth the requested torque, then advance physics with damping.
applied_actionfollows each accepted physics step.prev_potentialremains fixed until the next checkpoint or reset.- Parameters:
env (Environment) – The current environment.
action (jax.Array) – The per-agent torque vectors.
- Returns:
The updated environment state.
- Return type:
- static observation(env: SwarmRoller) Array[source]#
Return planar velocity, angular velocity, and filtered local LiDAR.
- Parameters:
env (SwarmRoller) – Environment whose current state is observed. Checkpointed potentials are not used to construct the observation.
- Returns:
Shape
(N, 5 + 2 * n_lidar_rays). Each row concatenates planar velocity (two components), angular velocity (three components), normalized objective bins, then normalized agent/wall bins. Velocities are in simulation units; LiDAR channels lie in[0, 1]. This matchesSwarmNavigatorsensor semantics andMultiRollervelocity channels.- Return type:
jax.Array
Notes
A valid raw LiDAR return is normalized as \(p_k=\max(0,1-d_k/L)\). Empty and masked bins are zero.
hide_occupied_objectives=Falsereturns unfiltered goal readings. Only LiDAR channels are normalized. With filtering enabled, hide objective bin \(k\) when\[a_i < d_{ik} < L/2,\qquad \min_{q\in Q_i}\widehat\delta_{ikq}\le a_i,\]where \(a_i\) is the observing radius, \(L\) the LiDAR range, and \(Q_i\) the valid detected peers, excluding walls and self. Bin-center directions give the approximate separation
\[\widehat\delta_{ikq}^2=\max\!\left( d_{ik}^2+d_{iq}^2-2d_{ik}d_{iq}\cos(\theta_k-\theta_q),0\right).\]Every objective bin is compared with every peer bin. Own goals within one radius and readings at or beyond half range remain visible. Hidden readings become zero without revealing occluded goals. Reward retains the raw scans. Angular quantization and hidden peers can cause occupancy errors; this is not an exact global coverage test.
The half-range mask and own-goal exception were developed for this environment’s experiments. The congestion-game and reward-shaping references in
reward()motivate the reward, not this observation filter, and do not guarantee that discarding observations is harmless.
- static reward(env: SwarmRoller) Array[source]#
Return the action-start difference in the shared coverage potential.
- Parameters:
env (SwarmRoller) – Environment at the endpoint of the action interval, with
prev_potentialcaptured bycheckpoint()orreset().- Returns:
Per-agent rewards of shape
(N,). Positive values indicate increased potential; negative values indicate decreased potential. Reading reward does not update the baseline or physical state.- Return type:
jax.Array
Notes
Reward uses raw objective detections regardless of the observation mask. It is evaluated over the complete action interval, not summed once per skipped physics frame. There is no separate occupancy bonus.
Normalized goal distance is \(x=d/a_i\), where \(a_i\) is the observing agent’s radius. Attraction strength and its integrated potential are
\[A(x)=(c+bx^2)e^{-\lambda x},\qquad P(x)=e^{-\lambda x}\left[\frac{c}{\lambda} +b\left(\frac{x^2}{\lambda}+\frac{2x}{\lambda^2} +\frac{2}{\lambda^3}\right)\right],\qquad -P'(x)=A(x).\]attraction_constant,attraction_quadratic, andattraction_decayset \(c,b,\lambda\). Defaults 0.01, 0.02, and 1/3 place the strength peak near 5.92 radii; strength at two radii is about 47 percent of the peak. The potential itself is monotone.The potential estimates peer claims on each visible objective from all pairs of objective and peer LiDAR bins:
\[\begin{split}\begin{aligned} C_{ik} &= \sum_{q\in Q_i} \exp\!\left[-\left(\frac{\widehat\delta_{ikq}}{\rho a_i}\right)^4\right], \\ w_{ik} &= \frac{1}{1+C_{ik}}, \\ B_{ik} &= \exp\!\left[-\left(\frac{d_{ik}}{1.5a_i}\right)^4\right], \\ \Phi_i &= \max_{k\in V_i}(w_{ik}B_{ik}) +\eta\sum_{k\in V_i}w_{ik}P(d_{ik}/a_i), \\ r_{i,t} &= \Phi_i(s_{t+1})-\Phi_i(s_{\mathrm{checkpoint}}). \end{aligned}\end{split}\]sharing_rangesets \(\rho\) (default 1.5) andattraction_coeffsets \(\eta\) (default 0.1). The denominator includes one prospective claim for self, so arrival never suppresses the agent’s own value. An objective with no detected claimants has full value; \(n-1\) unit peer claims divide both terms by \(n\). Settling uses the best shared score, rather than an unweighted nearest goal or a sum of nearby settling bonuses. All weights are positive; there is no contact penalty or negative attraction.This approximates equal sharing in a congestion game (Rosenthal, 1973; see References). Soft counts, angular quantization, occlusion, approach shaping, and discounted physical motion do not inherit the discrete game’s full-coverage equilibrium guarantee. Empty scans have zero potential. No claim assignments are stored.
Potential-based reward shaping is studied by Ng, Harada, and Russell (1999), with a stochastic-game extension by Lu, Schwartz, and Givigi (2011); see References. Their invariance results concern adding \(\gamma\Phi_i(s')-\Phi_i(s)\) to an existing reward, with suitable terminal conditions. Here the undiscounted difference is itself the reward; for \(\gamma<1\) it is not the discount-correct shaping term. These results do not guarantee coverage or convergence, nor do they apply to the observation filter.
Separation for objective bin \(k\) and valid peer bin \(q\) uses bin-center angles:
\[\widehat{\delta}_{ikq}^2=\max\!\left( d_{ik}^2+d_{iq}^2-2d_{ik}d_{iq}\cos(\theta_k-\theta_q),0\right).\]All bin pairs are compared, including across the angular wraparound. Walls, self IDs, and empty peer bins contribute zero. No peer positions or other agents’ objective scans are used. A wall can hide a peer in its bin, and bin-center directions approximate the true bearing. \(V_i\) contains nonempty raw objective bins; empty scans have zero potential, including goals exactly at sensor range. Setting
attraction_coeff=0retains only the best shared settling value.Reset and unchanged states give zero reward to floating-point precision. Undiscounted action rewards telescope across contiguous intervals with matching endpoint checkpoints and fixed parameters; episode resets begin a new baseline.
References
Rosenthal, R. W. (1973). “A class of games possessing pure-strategy Nash equilibria.” International Journal of Game Theory, 2, 65-67. https://doi.org/10.1007/BF01737559
Ng, A. Y., Harada, D., and Russell, S. (1999). “Policy invariance under reward transformations: Theory and application to reward shaping.” Proceedings of ICML, 278-287. https://ai.stanford.edu/~ang/papers/shaping-icml99.pdf
Lu, X., Schwartz, H. M., and Givigi, S. N. (2011). “Policy invariance under reward transformations for general-sum stochastic games.” Journal of Artificial Intelligence Research, 41, 397-406. https://doi.org/10.1613/jair.3384
- static truncated(env: SwarmRoller) Array[source]#
Return whether the environment has reached its physics-step limit.
- Parameters:
env (SwarmRoller) – Current environment supplying
system.step_countandenv_params["max_steps"].- Returns:
Scalar boolean, true when
step_count >= max_steps. The limit counts physics steps, including skipped frames, not policy actions. This method does not test coverage or reset the environment.- Return type:
jax.Array
- property action_space_size: int[source]#
Flattened action size per agent.
- Returns:
Three torque components.
step()normally receives shape(max_num_agents, 3).- Return type:
int
- property action_space_shape: tuple[int][source]#
Unflattened shape of one agent’s torque request.
- Returns:
(3,), used to reshape per-agent torque vectors instep().- Return type:
tuple[int]
- property observation_space_size: int[source]#
Flattened observation size per agent.
- Returns:
5 + 2 * n_lidar_rays(29 by default).observation()returns shape(max_num_agents, observation_space_size). Filtering does not change sensor sizes or channel order.- Return type:
int