AGENTS.md · hanruihua/ir-sim
AGENTS.md from hanruihua/ir-sim: 263 lines covering Project Overview, Development Commands, Architecture, Configuration and more.
Template by hanruihua
Install
Works with Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/hanruihua/ir-sim/main/AGENTS.md -o AGENTS.md
Read it and adapt it to your project before use.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: code-generation, testing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
An AGENTS.md file: instructions coding agents (Codex, Cursor, Copilot, Gemini CLI and others) read before working in a repo.
- For
- Codex, Cursor, GitHub Copilot, Gemini CLI, OpenCode, Claude Code
- Length
- 263 lines
- File
AGENTS.md
Sections
- Project Overview
- Development Commands
- Architecture
- Configuration
- Dependencies
- Testing Notes
- Git Commit Notes
- IR-sim documentation notes
- Note
- Citation
- Release Checklist
- Changelog Style
First lines (shown under the repo's MIT license)
# AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
## Project Overview
IR-SIM is an open-source, Python-based, lightweight robot simulator for navigation, control, and reinforcement learning. It uses YAML-driven configuration for defining robots, sensors, obstacles, and environments with built-in collision detection.
## Development Commands
```bash
# Install dependencies (using uv)
uv sync
# Run all tests
pytest
# Run tests with coverage report (coverage of irsim/ is on by default via pyproject)
pytest --cov-report=html
# Run a single test file
pytest tests/test_kinematics.py
# Run a specific test
pytest tests/test_kinematics.py::test_name
# Linting and formatting
ruff check # Check for lint errors
ruff check --fix # Auto-fix lint errors
ruff format # Format code
# Pre-commit hooks (install once)
pre-commit install
# Build documentation
cd docs && make html
```
## Architecture
### Entry Point
`irsim.make('config.yaml')` creates an environment from a YAML configuration file. The main simulation loop uses `env.step()`, `env.render()`, and `env.done()`.
### Core Components
**Environment Layer** (`irsim/env/`):
- `EnvBase` / `EnvBase3D`: Main environment classes handling simulation lifecycle
- `env_config.py`: YAML configuration parsing
- `env_plot.py` / `env_plot3d.py`: Matplotlib-based visualization (2D and 3D)
- `env_logger.py`: Environment logging
**World Layer** (`irsim/world/`):
- `World` / `World3D`: Core simulation state and collision detection (2D and 3D)
- `object_base.py`: Base class for all simulation objects (robots, obstacles)
- `object_factory.py`: Factory pattern for creating objects from YAML
- `object_group.py`: Grouping mechanism for coordinated multi-agent behaviors
**Robot Kinematics** (`irsim/world/robots/`):
- `robot_diff.py`: Differential drive
- `robot_omni.py`: Omnidirectional
- `robot_acker.py`: Ackermann steering (car-like)
- Handlers live in `irsim/lib/handler/kinematics_handler.py`; `PassiveKinematics` is the model of objects configured without kinematics (never drives itself, pushable in `contact` mode with a finite `mass`)
**Obstacles** (`irsim/world/obstacles/`):
- `obstacle_static.py`: Static obstacles
- `obstacle_diff.py`: Differential drive dynamic obstacles
- `obstacle_omni.py`: Omnidirectional dynamic obstacles
- `obstacle_acker.py`: Ackermann steering dynamic obstacles
**Behaviors** (`irsim/lib/behavior/`):
- Registry-based system using decorators (`behavior_registry.py`)
- Individual behaviors registered by robot type + behavior name:
- `diff`: `dash`, `rvo`, `sfm`
- `omni`: `dash`, `rvo`, `sfm`
- `omni_angular`: `dash`
- `acker`: `dash`
- Group behaviors in `group_behavior.py` and `group_behavior_methods.py`:
- `orca` (optimal reciprocal collision avoidance) - requires `pyrvo` package
- `sfm` (vectorized social force model stepping all members from one snapshot, with optional Moussaid 2010 social groups: coherence, repulsion, gaze)
- SFM algorithm implementation: `irsim/lib/algorithm/social_force_model.py` (anisotropic Moussaid-Helbing 2009 variant; `social_force_model` per agent, `SocialForceModelBatch` for a whole crowd)
**Contact / Physics** (`irsim/lib/algorithm/contact.py`):
- `collision_mode: contact` (world YAML) replaces stop-on-collision with engine-like pushing (PhysX rules reduced to `mass`, `friction`, `inertia`, `restitution` and an optional drive lag): after the kinematic step, overlapping pairs are separated along the SAT contact normal. Passive-passive and driven-driven pairs split by inverse mass (inelastic collision; `inv_mass` is 0 for static / `inf`-mass objects); a driven object pushes a passive body without yielding when its `friction_force` (`friction * mass * g`) is at least the summed load of the body and every touching passive body ahead of it in the push direction (`_ContactSolver._load`, per sweep), else it stalls; a passive body pressed on a blocker sticks inside the friction cone (mean of the two `friction`s) and slides outside it; pairs within `REST_TOLERANCE` block without moving. Passive bodies coast under ground friction (`PassiveKinematics.coast`). The world section carries the physics defaults as `WorldParam` fields validated by `World` through `irsim.util.util.check_number`, the one range check also behind object `mass`/`friction`/`inertia`/`restitution` and kinematics `tau`: `gravity` 9.81, `friction` 0.5 (PhysX material), `restitution` 0 (Isaac Lab), `drive_tau` 0 (instant, as Isaac's stiff drives at this step size; opt-in lag); objects read them at use time (`_world_param`) unless they set their own; `obj.passive` and `obj.friction_force` expose the classification. Rotation: `piece_mtv` also returns the contact point (deepest features, midway through the overlap); the split uses generalized inverse masses `w + (r x n)^2 / I` so an off-center push turns a pushable passive body (`inv_inertia`, `inertia` from the shape and `mass` unless set, `gyration` for angular braking); a driven object turns only when a contact makes it yield (stalled or blocked; `yaw_rate_row` on each handler says where the yaw rate goes); passive `action_dim` is 3 (`[vx, vy, yaw_rate]` world-frame). `restitution` (default 0 = Isaac Lab) makes passive bodies separate at e x approach speed after the fold; `contact_step(..., step_time)` fills `Contact.force` = impulse / dt^2 (XPBD) and `obj.contact.force` (a steadily pushed box reports mu m g; a driven object pushing into a contact is capped at its traction, so a stalled robot reports mu m g regardless of dt); `Contact` records keep their deepest push and are kept as `env.contacts` (raw pairs) and `obj.contact.reports` (`ContactReport` from the object's side: `other`, `point`, `normal` toward it, `depth`, `force`; `str()` prints a sentence), with `obj.contact.contact_time` / `air_time` / `started` / `ended` ticked each step and mirrored into `ObjectState` as `contact_force`, `contacts`, `contact_time`, `air_time`). Drive lag: `kinematics: {tau: ...}` (`KinematicsHandler.tau`, `obj.drive_tau` falls back to the world's `drive_tau`, default 0) makes `ObjectBase._drive_response` filter commands first-order in contact mode only; the same method caps the drive's speed change per step at `friction * gravity * dt` on the handler's `translation_rows` (wheel grip: a friction-0 robot cannot move, launches and stops take ~3 steps at 0.5) (`_drive_velocity` is the drive's own state, separate from the body velocity the contact fold edits); other modes track instantly
- Per-object `mass` (default `1.0` with kinematics, `inf` without). `ObjectBase._init_motion_state` is the one place that decides `static` (the flag with kinematics; without kinematics, static unless the mass is finite); the factory, plots, env and solver read `obj.static` instead of re-deriving it; `obj.pushable` (not static and finite mass) is the one definition behind `inv_mass` and the color default. Objects without kinematics get a `PassiveKinematics` handler (`kinematics_handler.py`; the `static` name gives the same model) whose `step` keeps the state, whose velocities are world-frame and whose `max_speed` is 0, so `ObjectBase` never branches on the handler kind (`ObjectStatic` only serves `kinematics: {name: static}`); a pushable one defaults to `palette_param.pushable` (Okabe-Ito orange `#E69F00`, print-safe) unless `color` is set, so pushable obstacles stand out from black static ones
- Convex pieces: exact circles, convex polygons, line segments (linestrings, grid-map boundaries); non-convex polygons via `shapely.constrained_delaunay_triangles`; pairs swept in anchored order with blocked-direction projection so chains against walls settle in one sweep
- `ObjectBase.apply_contact_displacement` / `apply_contact_velocity` are the only writes into an object's motion; everything else the step records goes into the object's `Contact2D` sensor, `obj.contact` (`world/sensors/contact2d.py`, registered in `SensorFactory` as `contact2d`: `in_contact`, `partners`, `records`, `force`, timers; cleared and ticked by the env, filled by the solver) and is read straight from it (`obj.contact.in_contact`, `partners`, `reports`, `force`, `contact_time`, `air_time`, `started`, `ended`; the object keeps no `contact_*` mirror properties); `collision_flag` stays False for resolved contacts
**Path Planners** (`irsim/lib/path_planners/`):
- `a_star.py`: A* grid-based path planning
- `rrt.py`: Rapidly-exploring Random Tree
- `rrt_star.py`: RRT* optimized path planning
- `informed_rrt_star.py`: Informed RRT* path planning
- `jps.py`: Jump Point Search (optimized A* variant)
- `probabilistic_road_map.py`: PRM path planning
**Sensors** (`irsim/world/sensors/`):
- `lidar2d.py`: 2D LiDAR simulation
- `fmcw_lidar2d.py`: Simplified 2D FMCW LiDAR with per-beam radial velocity
- `contact2d.py`: `Contact2D` (`type: contact2d`, named like `lidar2d`), the contact bookkeeping of one object (`in_contact`, `partners`, `records`, `force`, `contact_time`/`air_time`, `started`/`ended`) written by the env (`clear`/`tick`) and the contact solver (`add`/`add_force`); every object gets one built in as `obj.contact`, a plain attribute like `obj.lidar` (the boolean reading is `obj.contact.in_contact`; the object has no `contact_*` mirror properties, and a sensor and a flag never share a name), one listed under `sensors` is also drawn (contact points and force lines scaled by `force_scale`)
- `sensor_factory.py`: Factory for sensor instantiation
**Map** (`irsim/world/map/`):
- `obstacle_map.py`: Obstacle map representation
- `fog_map.py`: Fog-of-map overlay (`FogMap`, subclass of `Map`) revealed by lidar line of sight or robot field of view
- `grid_map_generator_base.py`: Base grid map generator
- `image_map_generator.py`: Image-based map generation
- `perlin_map_generator.py`: Perlin noise procedural map generation
- `binary_map_generator_hm3d/`: HM3D binary map generator
**GUI** (`irsim/gui/`):
- `keyboard_control.py`: Keyboard-based robot control (requires `pynput`)
- `mouse_control.py`: Mouse-based interaction
### Key Patterns
- **YAML Configuration**: All scenarios defined in human-readable YAML files
- **Factory Pattern**: Objects created via `object_factory.py` from YAML specs
- **Registry Pattern**: Behaviors and sensors registered via decorators for extensibility
- **Geometry via Shapely**: Collision detection uses Shapely library (>=2.1.2)
- **Centralized RNG**: All randomness routes through `irsim.util.random.rng` (a proxy over `numpy.random.Generator`); call `set_seed(seed)` to make runs reproducible
- **Palette param**: every default color is a field of `irsim/config/palette_param.py`, built like `env_param`/`world_param` (dataclass + module proxy). Okabe-Ito based, colour-blind and grayscale-print safe: `robot` `#009E73`, `robot_acker` `#117733`, `obstacle` black, `pushable` orange for pushable kinematics-free bodies, `arrow`, `fov`/`fov_edge`, `lidar`, `laser_highlight`, FMCW velocity colors, `marker`/`path`/`quiver` for the draw helpers, and `cycle` for groups configured with `color: 'cycle'`. Consumers read it when an object or plot is created (`KinematicsHandler.default_color`, dataclass `default_factory`, `None` defaults resolved at call time), so `palette_param.robot = ...` before `irsim.make()` restyles a scene. Mirror any changed default in `docs/source/yaml_config/configuration.md` (HTML tree + entries) and the Chinese catalog
### Directory Structure
```
irsim/ # Main package
├── env/ # Environment and visualization (2D/3D)
├── world/ # Core simulation components
│ ├── robots/ # Robot kinematics (diff, omni, acker)
│ ├── obstacles/ # Obstacle types (static, dynamic)
│ ├── sensors/ # Sensor implementations (lidar2d, fmcw_lidar2d, contact2d)
│ ├── map/ # Map generators (grid, image, perlin, hm3d)
│ └── description/ # Robot/vehicle visualization assets (PNG)
├── lib/ # Algorithms and behaviors
│ ├── behavior/ # Robot behaviors (dash, rvo, sfm, orca)
│ ├── algorithm/ # Core algorithms (kinematics, rvo, sfm, contact, generation)
│ ├── path_planners/ # Path planning (A*, RRT, RRT*, Informed RRT*, JPS, PRM)
│ └── handler/ # Geometry and kinematics handlers
├── gui/ # Keyboard/mouse controls
├── util/ # Utility functions
└── config/ # Configuration parameters
tests/ # Pytest test suite (16 test files)
usage/ # Example YAML configs and scripts (26 examples)
docs/ # Sphinx documentation (multilingual: en, zh_CN)
```
## Configuration
Ruff is configured in `pyproject.toml` with:
- Line length: 88
- Target: Python 3.10+
- First-party imports: `irsim`
- Lint rules: F, E, W, I, UP, B, RUF, C4, SIM, ISC, RET, PT
- Ignored: E501 (long lines allowed)
Type checking uses `ty` with custom rule configurations in `pyproject.toml`.
## DependenciesSignals
- Stars
- 1,151
- Forks
- 149
- Open issues
- 1
- Last commit (this folder)
- Oct 4, 2026 (24 days ago)
- Last release
- Oct 4, 2026 (24 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/hanruihua/ir-sim
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from hanruihua
- 0.596score
CLAUDE.md · hanruihua/ir-sim
hanruihua
CLAUDE.md from hanruihua/ir-sim: 263 lines covering Project Overview, Development Commands, Architecture, Configuration and more.
TemplateAFreshness: A★ 1.2KClaude Code - 0.591score
ir-sim
hanruihua
YAML-driven Python robot simulator for navigation, control and learning research, with collision detection and a Matplotlib renderer.
HarnessAFreshness: A★ 1.2K↓ 386/wk
Cite this page
AGENTS.md · hanruihua/ir-sim. Agent Mart. https://agentmart.dev/templates/hanruihua-ir-sim-agents-md (last updated 2026-10-24)