CLAUDE.md · hanruihua/ir-sim
CLAUDE.md from hanruihua/ir-sim: 263 lines covering Project Overview, Development Commands, Architecture, Configuration and more.
Template by hanruihua
Chalk Labs
Need help deploying CLAUDE.md · hanruihua/ir-sim?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/hanruihua/ir-sim/main/CLAUDE.md -o CLAUDE.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
A CLAUDE.md file: project memory Claude Code loads at the start of every session.
- For
- Claude Code
- Length
- 263 lines
- File
CLAUDE.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)
# CLAUDE.md
This file provides guidance to Claude Code (claude.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 (26 days ago)
- Last release
- Oct 4, 2026 (26 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
AGENTS.md · hanruihua/ir-sim
hanruihua
AGENTS.md from hanruihua/ir-sim: 263 lines covering Project Overview, Development Commands, Architecture, Configuration and more.
TemplateAFreshness: A★ 1.2KCodex +5 - 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
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get CLAUDE.md · hanruihua/ir-sim running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
CLAUDE.md · hanruihua/ir-sim. Agent Mart. https://agentmart.dev/templates/hanruihua-ir-sim-claude-md (last updated 2026-10-24)