Isaac Lab layer¶
isaac_net.isaac puts a network into an Isaac Lab task. Only rigid_positions_local and the mdp event term touch Isaac Lab objects, and nothing in the package imports Isaac Lab at import time, so NetModule, MessageHistory and the mixin hooks also run on a CPU without Isaac (Tutorial 04 does exactly that).
| Name | Role |
|---|---|
NetModule |
the network of one environment batch, on any make_engine level, plus the Isaac radio, per-message tags and freshness outputs |
TrafficRequest |
messages per robot for one control step, with an optional per-message tag |
NetEnvMixin |
four hook calls that wire a NetModule into a DirectRLEnv |
MessageHistory |
the receiver's delayed view of per-robot payloads |
IsaacRadio, ParamRanges |
poses to SNR with per-environment parameters, several gNBs and line-of-sight blockage |
mdp.randomize_network |
an event term that redraws the per-environment radio parameters (network domain randomization) |
net_features |
the compact [E, R, 4] network observation |
Where the calls go in a DirectRLEnv¶
DirectRLEnv.step in Isaac Lab 3.0 calls _pre_physics_step, then the physics substeps, then _get_dones, _get_rewards, _reset_idx for finished environments, and finally _get_observations. The mixin's calls fit this order:
| Hook | Call | Why there |
|---|---|---|
_setup_scene |
self.net_setup(level, R, config, backend, **kwargs) |
builds the NetModule once for all environments |
_pre_physics_step |
(task code) read the start-of-step pose and decide what to send | messages are captured at the start of the step |
_get_dones |
out = self.net_step(poses_end, send, tag, cur_tag) |
the first hook after physics: it sees the end-of-step poses and the queues before any reset |
_reset_idx |
self.net_reset(env_ids) after super()._reset_idx(env_ids) |
partial reset of the finished environments only |
_get_observations |
self.net_obs() |
[E, R, 4] AoI, SNR, queued messages, delivered; zeros for environments that reset this step |
net_setup("off") removes the network: net_step returns None and net_obs returns zeros of the same shape, which is the ideal-link baseline. A multi-cell config needs the engine's own radio, net_setup(..., radio="engine").
NetModule outputs¶
NetModule.step returns a dict whose keys differ from the core engine's in a few places, because it is written for task code:
| Key | Shape | Meaning |
|---|---|---|
delivered |
[E, R] bool |
at least one message of the robot was delivered this step |
newest_cap |
[E, R] long |
newest capture step delivered this step, -1 if none |
last_cap |
[E, R] long |
newest capture step delivered this episode (0 means the state at reset) |
aoi_s |
[E, R] float |
age of that information at the end of the step, in seconds |
queue_len, queue_bytes |
[E, R] |
queue state after the step |
sinr_db, serving, blocked |
[E, R] |
radio of this step |
tag_delivered |
[E] bool |
a message tagged with the environment's current tag arrived (only if cur_tag is given) |
msg_delivered, timed_out, cap, cls, delay_s |
[E, R, F] |
per message slot, as in the engine; delay_s in seconds |
t |
[E] long |
the clock value of this step |
NetModule
¶
NetModule(level='L2-legacy', num_envs: int = None, num_robots: int = None, device='cuda', config: Optional[NRConfig] = None, backend: str = 'reference', *, isaac: Optional[IsaacNetCfg] = None, ranges: Optional[ParamRanges] = None, params: Optional[dict] = None, seed: Optional[int] = None, inject: bool = False, strict: bool = False, **isaac_overrides)
Network in the loop of a batch of E envs x R robots (see the module docstring).
reset
¶
Partial reset of env_ids (index tensor, bool mask, list or None = all): queues, MAC, fading, clock,
the shadowing field, the freshness state and the observation features. Radio parameters stay, unless
IsaacNetCfg.dr_mode is "reset", which redraws them. Other envs are bitwise unaffected.
submit
¶
Messages captured at the start of this control step (t is ignored: per-env clocks). Call before step.
step
¶
step(t, poses: Tensor, cur_tag: Optional[Tensor] = None, blocked_fn=None, blockers: Optional[Tensor] = None) -> dict
Advance every env by one control step (t is ignored: per-env clocks).
poses [E,R,3] (or [E,R,2]) env-local positions at the END of this control step.
cur_tag [E] long: the env's current tag (-1 = none) for tag_delivered.
blocked_fn(poses [E,R,3]) -> [E,R,G] bool line-of-sight blockage, evaluated per pose chunk (the per-env gNB
positions are net.radio.gnb_env [E,G,3]). Ignored with IsaacNetCfg.blockage = False. With radio="engine"
it becomes the engine radio's LOS source when NRConfig.los_source="callback" (multi-cell scene blockage;
the poses are in the radio frame, the gNBs at the config's cells), else it is ignored with a warning.
blockers [E,M,3] (x, y, class): extra dynamic blockers (humans, vehicles) as TR 38.901 model-B screens, for
radio="engine" at level L2 with NRConfig(blockage=True, blockage_model="screen").
Returns a dict:
delivered [E,R] bool at least one message of the robot was delivered this step
newest_cap [E,R] long newest capture step delivered this step (-1 if none), env clock
last_cap [E,R] long newest capture step delivered this episode (0 = the state at reset)
aoi_s [E,R] float age of that information at the end of the step, in seconds
queue_len, queue_bytes FIFO state after the step
sinr_db [E,R], rsrp_dbm [E,R], serving [E,R], blocked [E,R], los [E,R] radio of this step (rsrp =
SINR + the env's noise floor: the received power when there is no
interference; los = the engine radio's LOS state of the serving link, or
~blocked when the radio has none)
tag_delivered [E] bool if cur_tag is given
access_state [E,R] long, access_sleep_frac [E,R], rach_attempts [E,R], rlf [E,R] bool
passed through from the NR engine when it reports them (core/access.py:
NRConfig rach / drx; core/radio.py: rlf with several cells)
msg_delivered, timed_out [E,R,F] bool, cap, cls [E,R,F] long, delay_s [E,R,F] float (NaN if not
delivered), t [E] long per message slot as queued before the step, from the engine
set_params
¶
Per-env radio parameters (RADIO_PARAMS, gnb_offset_m) of env_ids. Never touched by reset().
sample_params
¶
Uniform draw of radio parameters within ranges ({name: (lo, hi)}, default: the module's ParamRanges).
TrafficRequest
dataclass
¶
Messages each robot enqueues this control step.
send: [E,R] long, 0 = nothing, c >= 1 = one message of traffic class c (size config.msg_sizes[c-1]).
tag: optional [E,R] long per-message label (-1 = none), e.g. the hazard id if the frame detects it.
NetEnvMixin
¶
net_setup
¶
net_setup(level, num_robots: Optional[int] = None, config=None, backend: str = 'reference', isaac: Optional[IsaacNetCfg] = None, markers=None, **kwargs)
Build the network for this env batch; call inside _setup_scene.
level: None or "off" (ideal link, no network features), or a make_engine level ("L0", "L0DR", "L1",
"L2-legacy", "L2", ...). config: NRConfig. backend: "reference" | "eager" | "graph" | "compile" |
"triton". isaac: IsaacNetCfg. markers: a NetMarkersCfg (or True) for viewport overlays, inert when
headless (isaac/markers.py). kwargs go to NetModule (params, seed, strict, ranges, and IsaacNetCfg
fields as shortcuts). A NetConfig (deprecated) is also accepted as level.
net_step
¶
net_step(poses_end: Optional[Tensor] = None, send: Optional[Tensor] = None, tag: Optional[Tensor] = None, cur_tag: Optional[Tensor] = None, blocked_fn=None) -> Optional[dict]
Submit this step's messages (captured at the start-of-step pose) and advance the network by one env
control step with the END-of-step poses [E,R,3] (None: read from IsaacNetCfg.pose_asset). Returns the
NetModule output dict, or None without a network.
net_obs
¶
[E,R,obs_dim] the IsaacNetCfg.obs_features of the last network step; zeros without a network, before the
first step, and for envs that reset since (their last output belongs to the previous episode).
rigid_positions_local
¶
[E,R,3] env-local body positions of a RigidObjectCollection, Articulation or RigidObject (3.0 ProxyArray or
2.x torch tensor). body_ids selects bodies (None = all).
MessageHistory
¶
Ring buffer of per-robot payloads indexed by env-clock capture step (the receiver's delayed view).
push(t_env, data) stores data [E,R,D] captured at each env's own clock (the start-of-step pose). After
net.step, update(out["newest_cap"]) replaces the receiver's copy for robots whose newest delivered capture is
fresher than what it holds; others keep the last payload (hold-last on loss). history_len must exceed
config.timeout_steps so that every deliverable capture is still stored.
net_features
¶
Deprecated: the default observation [E,R,4] (AoI, SNR, queued frames, delivered this step) with the one
normalization of isaac/obs.py. Use NetModule.obs() with IsaacNetCfg.obs_features instead.
IsaacRadio
¶
IsaacRadio(E: int, device, gnb_pos, ranges: ParamRanges, shadow_modes: int = 8, seed: Optional[int] = None)
Per-env radio state and parameters for E envs; snr_db(pos, blocked) -> [E,R,G].
reset
¶
Redraw the shadowing field of env_ids (None = all) in place, as core Radio.reset. Parameters stay.
set_params
¶
Overwrite per-env parameters of env_ids. A value is a scalar or a [len(env_ids)] tensor.
sample_params
¶
Uniform draw within ranges ({name: (lo, hi)}, default self.ranges) for env_ids, from self.gen.
snr_db
¶
Full-power single-subband uplink SNR per gNB in dB. pos [E,R,2|3] env-local (z = 0 if 2-D).
blocked [E,R,G] bool adds blockage_db where the line of sight is blocked. Returns [E,R,G].
ParamRanges
dataclass
¶
ParamRanges(p_tx_dbm: tuple = (23.0, 23.0), noise_dbm: tuple = (-90.0, -90.0), pl_const_db: tuple = (40.0, 40.0), pl_exp: tuple = (3.5, 3.5), shadow_sigma_db: tuple = (6.0, 6.0), blockage_db: tuple = (20.0, 20.0))
Per-env radio parameters: (lo, hi) of a uniform draw. The midpoint is the value before any randomization.
The defaults are the legacy single-cell radio (NRConfig defaults); ParamRanges.from_config(cfg) takes them
from a config. noise_dbm is noise plus interference per snr_ref_prbs-PRB subband.
randomize_network
¶
Resample per-env radio parameters uniformly within ranges for env_ids.
Parameters (isaac.radio.RADIO_PARAMS and gnb_offset_m): p_tx_dbm, noise_dbm, pl_const_db, pl_exp,
shadow_sigma_db, blockage_db, gnb_offset_m (per-env x/y offset of every gNB). ranges=None takes the radio keys
of the module's IsaacNetCfg.dr_ranges.
Example: EventTermCfg(func=randomize_network, mode="reset",
params={"ranges": {"pl_exp": (2.8, 4.0), "shadow_sigma_db": (3.0, 8.0)}})
In mode "reset" it runs inside DirectRLEnv._reset_idx (super) BEFORE net_reset; net_reset never touches
parameters unless IsaacNetCfg.dr_mode is "reset", so the drawn values hold for the new episode. Delay and loss
of L0 / L0DR are engine parameters: set them through IsaacNetCfg.dr_ranges (NRConfig dr_* / l0_* fields).
NetConfig
dataclass
¶
NetConfig(num_envs: int, num_robots: int, device: str = 'cuda', rung: Optional[str] = None, backend: Optional[str] = None, step_dt: Optional[float] = None, slot_dt: Optional[float] = None, pose_chunks: Optional[int] = None, frame_depth: Optional[int] = None, msg_sizes: Optional[Sequence[float]] = None, timeout_steps: Optional[int] = None, gnb_pos: Optional[Sequence[Sequence[float]]] = None, config: Optional[NRConfig] = None, isaac: Optional[IsaacNetCfg] = None)
Deprecated alias of the demo's configuration; it has no defaults of its own. Pass level, an NRConfig and an
IsaacNetCfg to NetModule instead. Every field left at None comes from config (an NRConfig, default
NRConfig()) or from IsaacNetCfg: rung None = "L2-legacy" ("L2" of the demo is the slot-level NetSlot, i.e.
level "L2-legacy"), backend None = "reference" ("ref" also means "reference"), step_dt = control_step_ms,
frame_depth = frame_buffer, msg_sizes and timeout_steps from the NRConfig, pose_chunks and gnb_pos from
IsaacNetCfg. to_kwargs() maps the fields onto NetModule(level, E, R, device, config, ...).