.. _nan-guard: NaN Guard ========= The NaN guard captures simulation states when NaN/Inf is detected, helping debug numerical instability issues. Quick start ----------- Enable the NaN guard with a single CLI flag: .. code-block:: bash uv run train --enable-nan-guard True This automatically captures and saves simulation states when NaN/Inf is detected. You can also enable it programmatically: .. code-block:: python from mjlab.sim.sim import SimulationCfg from mjlab.utils.nan_guard import NanGuardCfg cfg = SimulationCfg( nan_guard=NanGuardCfg( enabled=True, buffer_size=100, output_dir="/tmp/mjlab/nan_dumps", max_envs_to_dump=5, ), ) Configuration ------------- ``enabled`` *(default: False)* Enable/disable NaN detection and dumping. ``buffer_size`` *(default: 100)* Number of recent simulation states to keep in the rolling buffer. ``output_dir`` *(default: "/tmp/mjlab/nan_dumps")* Directory where NaN dump files are saved. ``max_envs_to_dump`` *(default: 5)* Maximum number of NaN environments to dump to disk. All environments are tracked in the buffer, but only the first N are saved to reduce dump size. Behavior -------- - **Captures** simulation state before each step (``qpos``, ``qvel``, ``act`` if the model has actuator activations, and ``mocap_pos``/``mocap_quat`` if the model has mocap bodies) - **Detects** NaN/Inf in ``qpos``, ``qvel``, ``qacc``, ``qacc_warmstart``, and ``sensordata`` after each step - **Dumps** the rolling buffer and model to disk on first detection - **Stops** after the first dump to avoid spam When disabled, all operations are no-ops with negligible overhead. Output format ------------- Each NaN detection creates timestamped files plus latest symlinks: - ``nan_dump_TIMESTAMP.npz``: compressed state buffer - ``states_step_NNNNNN``: captured states per step (shape: ``[num_envs_dumped, state_size]``) - ``_metadata``: dict with ``num_envs_total``, ``nan_env_ids``, ``dumped_env_ids``, etc. - ``model_TIMESTAMP.mjb``: MuJoCo model in binary format - ``nan_dump_latest.npz``: symlink to most recent dump - ``model_latest.mjb``: symlink to most recent model Visualizing dumps ----------------- Use the interactive viewer to scrub through captured states: .. code-block:: bash # View latest dump. uv run viz-nan /tmp/mjlab/nan_dumps/nan_dump_latest.npz # View a specific dump. uv run viz-nan /tmp/mjlab/nan_dumps/nan_dump_20251014_123456.npz .. figure:: ../_static/content/nan_debug.gif :alt: NaN Debug Viewer NaN debug viewer. The viewer provides: - Step slider to scrub through the buffer - Environment slider to compare different environments - Info panel showing which environments have NaN/Inf - 3D visualization of the robot and terrain at each state NaN detection termination ------------------------- While the NaN guard helps **debug** NaN issues by capturing states, you can also **prevent** training crashes using the ``nan_detection`` termination term. This marks NaN environments as terminated, allowing them to reset while training continues: .. code-block:: python from mjlab.envs.mdp.terminations import nan_detection from mjlab.managers.termination_manager import TerminationTermCfg nan_term: TerminationTermCfg = field( default_factory=lambda: TerminationTermCfg( func=nan_detection, time_out=False, ) ) Terminations are logged as ``Episode_Termination/nan_term`` in your metrics. .. important:: ``nan_detection`` is a band-aid, not a cure. If NaNs occur during your task objective (e.g., NaNs happen when grasping), the policy will never learn to complete the task since it resets before receiving rewards. Monitor your ``Episode_Termination/nan_term`` metrics carefully. **When to use which:** - ``nan_guard``: debug and understand why NaNs occur (always do this first) - ``nan_detection``: keep training stable while working on a permanent fix