This document covers how the physics simulation works, how to run it, and how to use AdvantageScope for visualization.
The robot code supports full physics simulation using MapleSim, a simulation framework built on the dyn4j 2D rigid-body physics engine. When running in simulation mode, MapleSim provides realistic swerve drive physics, field collisions, game piece interactions, and motor behavior.
The simulation runs at 200Hz (0.005s period) on a dedicated thread, separate from the main robot loop (50Hz).
# Launch simulation
./gradlew simulateJavaThis starts:
- The robot code in simulation mode
- A Simulation GUI window for controlling robot state
- A simulated Driver Station connection
- Select robot mode: Drag "Teleoperated" or "Autonomous" to enable those modes
- Connect a controller: Plug in a gamepad to drive the robot in teleop
- View in AdvantageScope: Open AdvantageScope and connect to
localhostto visualize
AdvantageScope is the recommended visualization tool for simulation:
- Download from GitHub Releases
- Open AdvantageScope
- Connect to the running simulation via File > Connect to Simulator (or
localhost) - Use the 3D Field tab to see the robot and game pieces
- Use the 2D Field tab with the
FieldNetworkTable entry to see the robot pose
Published NetworkTables data:
DriveState/Pose- Robot pose (Pose2d struct)DriveState/Speeds- Chassis speeds (ChassisSpeeds struct)DriveState/ModuleStates- Current swerve module statesDriveState/ModuleTargets- Target swerve module statesDriveState/ModulePositions- Module positions for odometryDriveState/Timestamp- Odometry timestampDriveState/OdometryFrequency- Odometry update frequencyField/robotPose- Robot pose as double array [x, y, deg]FieldSimulation/Fuel- Game piece positions (Pose3d array)
SwerveSubsystem constructor
└─ if (Utils.isSimulation()) startSimThread()
├─ Creates MapleSimSwerveDrivetrain
│ ├─ Builds DriveTrainSimulationConfig (mass, bumpers, gyro, modules)
│ ├─ Creates SwerveDriveSimulation (physics body)
│ ├─ Creates IntakeSimulation (game piece pickup)
│ ├─ Registers with SimulatedArena
│ └─ Adds sample game piece (Fuel at 1,1)
└─ Starts Notifier at 200Hz → calls simDrivetrain.update()
Every 5ms, the simulation:
public void update() {
// 1. Step the physics simulation (all bodies, collisions, forces)
SimulatedArena.getInstance().simulationPeriodic();
// 2. Inject simulated gyro values from physics
pigeonSim.setRawYaw(mapleSimDrive.getSimulatedDriveTrainPose().getRotation());
pigeonSim.setAngularVelocityZ(chassisSpeeds.omegaRadiansPerSecond);
}Each SimSwerveModule also injects motor/encoder sim state:
- Drive motor: position, velocity, supply voltage → reads motor voltage command
- Steer motor: position, velocity, supply voltage → reads motor voltage command
- CANcoder: position, velocity, supply voltage
File: frc/robot/subsystems/simulation/MapleSimSwerveDrivetrain.java
Bridges CTRE's swerve hardware API with MapleSim physics. Contains:
- Constructor - Builds the simulation configuration, creates physics bodies, registers with arena
update()- Steps physics and injects gyro stateSimSwerveModule(inner class) - Per-module motor controller simulationTalonFXMotorControllerSim- Feeds motor encoder state from physics, reads voltage commandsTalonFXMotorControllerWithRemoteCanCoderSim- Same but also updates CANcoder sim stateregulateModuleConstantsForSimulation()- Adjusts module constants for simulation compatibility:- Disables motor inversions (handled differently in sim)
- Zeroes encoder offsets
- Adjusts steer PID (kP=70, kD=4.5 for sim)
- Reduces friction voltages
Simulation parameters:
| Parameter | Value |
|---|---|
| Robot mass | 110 lbs (49.9 kg) |
| Bumper size | 30" x 30" |
| Drive motors | Kraken X60 FOC |
| Steer motors | Kraken X60 FOC |
| Wheel COF | 1.2 |
| Sim period | 0.005s (200Hz) |
File: frc/robot/subsystems/simulation/MapSimSwerveTelemetry.java
Publishes simulation telemetry to NetworkTables for AdvantageScope visualization. Includes:
- Drive state (pose, speeds, module states/targets/positions)
- Field2d robot position
- Mechanism2d module visualizations (speed/direction per module)
- Game piece positions (
FieldSimulation/Fuel) - SignalLogger data for CTRE's signal logging
Only active in simulation (registered in ControlBoard.tryInit() when Utils.isSimulation() is true).
File: frc/robot/subsystems/simulation/ElevatorWristSim.java
A placeholder Mechanism2d visualization for a future elevator + wrist mechanism. Currently:
- All dimension constants are 0 (not configured)
- Has a 3-stage elevator (S1, S2, S3) with a wrist at the top
- Publishes to SmartDashboard as
ElevatorWrist - Tracks state transitions with timestamps
This will be fleshed out when the physical elevator/wrist mechanism is designed.
The MapleSim source code is bundled under org.ironmaple.simulation. Key components:
File: org/ironmaple/simulation/SimulatedArena.java
The central physics world that manages all simulation objects. Uses the dyn4j physics engine for 2D rigid body dynamics.
Key methods:
simulationPeriodic()- Steps the physics simulationaddDriveTrainSimulation()- Registers a robot drivetrainaddGamePiece()- Adds a game piece to the fieldclearGamePieces()- Removes all game piecesgetGamePiecesArrayByType(String)- Gets positions of game pieces by type (for visualization)overrideSimulationTimings()- Sets simulation period and substeps
File: org/ironmaple/simulation/drivesims/SwerveDriveSimulation.java
Simulates the physics of a swerve drivetrain: forces from drive wheels, friction, collisions with field elements.
Key methods:
getSimulatedDriveTrainPose()- Current robot pose in simulationgetDriveTrainSimulatedChassisSpeedsRobotRelative()- Current velocitysetSimulationWorldPose()- Teleport the robot to a positiongetModules()- Access individual module simulations
Each swerve module simulates:
- Drive motor torque → wheel force
- Steer motor torque → wheel angle
- Tire friction model (slip angle, wheel COF)
- Ground contact dynamics
File: org/ironmaple/simulation/IntakeSimulation.java
Simulates game piece collection through contact detection:
- Configured as "over the bumper" intake on the FRONT side
- 10" x 10" contact zone
- Can hold up to 30 game pieces
startIntake()/stopIntake()to enable/disable collection
File: org/ironmaple/simulation/seasonspecific/rebuilt2026/
Arena2026Rebuilt.java- Field layout with obstacles, hubs, outpostsRebuiltFuelOnField.java- Fuel game piece physics (on the ground)RebuiltFuelOnFly.java- Fuel game piece in the air (projectile)RebuiltHub.java- Hub scoring targetRebuiltOutpost.java- Human player station
File: org/ironmaple/simulation/motorsims/
MapleMotorSim.java- DC motor physics simulationSimMotorConfigs.java- Motor characteristic configurationsSimMotorState.java- Motor state (angle, velocity)SimulatedBattery.java- Battery voltage model (voltage droop under load)SimulatedMotorController.java- Interface for motor controller simulation
File: org/ironmaple/simulation/drivesims/COTS.java
Pre-configured "Commercial Off-The-Shelf" hardware models:
ofPigeon2()- Pigeon 2 gyro simulation configuration
File: org/ironmaple/utils/
FieldMirroringUtils.java- Coordinate mirroring for Red/Blue allianceGeometryConvertor.java- Converts between WPILib and dyn4j geometry typesMapleCommonMath.java- Interpolation, clamping, and other math utilitiesSwerveStateProjection.java- Projects swerve module states for visualization
- Robot doesn't move in sim: Make sure you're in "Teleoperated" or "Autonomous" mode in the Sim GUI
- Robot spins wildly: Steer PID might need simulation-specific tuning (handled by
regulateModuleConstantsForSimulation()) - No game pieces visible: Check AdvantageScope's 3D field tab and ensure
FieldSimulation/Fuelis being published - Incorrect starting position: The simulated robot starts at (3, 3) by default. Use
resetPose()to change it.
- Simulation is 2D only (no vertical dynamics for climbing, elevator, etc.)
- Game piece scoring detection is simplified
- Motor models are approximations of real motor behavior
- CAN bus latency is not simulated
- Vision simulation is not implemented (Limelight returns null poses in sim)