Introduction

This is a guide on how to create a SubsystemIOSim.kt file for your subsystem. This file will communicate with requests from the logic file to command a physics simulation who’s physical outputs will be reported back to the IO.

<aside> 🚨

Example code is provided, but that does not guarentee it’s correct! Check the latest version of WPILib to verify code. If you see incorrect code, please update it!

Note that this is basic simulation, and is required for every subsystem. If you are just creating your basic simulation file, you’re in the right place! For more complex applications and additions to the simulation, such as game piece logic, redirect yourselves towards the MapleSim documentation. [LINK TO MAPLESIM DOC HERE]

</aside>

Creating the object

The simulation class will be declared as an object (singleton) instead of a class, as only 1 instance of the simulation will ever be made.

object SubsystemIOSim : SubsystemIO { /* ... */ }

Creating the physics simulation

Here is where it gets tricky. WPILib offers many different simulation classes for robotics physics simulation, but there’s only a select few you really need to worry about.

First, know about the DCMotor class. DCMotor allows you to create a motor with specified nominal voltage, stall current, etc. Retrieving the pre-configured DCMotors can be done with the static DCMotor getMotorName(int numMotors) method, where MotorName is replaced by an actual motor’s name, and numMotors is the number of motors powering the mechanism of interest.

Arm (pivot) Simulation

You can create an arm (pivot) simulation using the SingleJointedArmSim class. https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/SingleJointedArmSim.html Note that this obviously represents an arm with a single joint (single point of turning). Here is an example constructor.

import edu.wpi.first.wpilibj.simulation.SingleJointedArmSim
import edu.wpi.first.math.system.plant.DCMotor
import com.team4099.robot2025.config.constants.SubsystemConstants

/* class fields */

private val subsystemSim = SingleJointedArmSim(
	DCMotor.getMotorName(numMotors),
	// reciprocal because in our code reductions are < 1, but in WPILib reductions are > 1
	1.0 / SubsystemConstants.GEAR_RATIO,
	// can be taken either from CAD or replaced with a call to SingleJointedArmSim.estimateMoi(double lengthMeters,double massKg)
	SubsystemConstants.MOMENT_OF_INERTIA.inKilogramMeterSquared,
	SubsystemConstants.ARM_LENGTH.inMeters,
	SubsystemConstants.MIN_ANGLE.inRadians,
	SubsystemConstants.MAX_ANGLE.inRadians,
	// simulate gravity boolean. can set to false if want easy tuning, but should set to true for FF tuning
	true,
	// likely the MIN_ANGLE or MAX_ANGLE
	SubsystemConstants.SIM_STARTING_ANGLE.inRadians
)

Elevator (linear extension) Simulation

You can create an elevator (extension) simulation using the ElevatorSim class. https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/ElevatorSim.html Note that this can be used for both vertical and horizontal elevators (laterators). Here is an example constructor.

import edu.wpi.first.wpilibj.simulation.ElevatorSim
import edu.wpi.first.math.system.plant.DCMotor
import com.team4099.robot2025.config.constants.SubsystemConstants

/* class fields */

private val subsystemSim = ElevatorSim(
	DCMotor.getMotorName(numMotors),
	// reciprocal because in our code reductions are < 1, but in WPILib reductions are > 1
	1.0 / SubsystemConstants.GEAR_RATIO,
	// should also include everything being lifted (mounted) on the carriage
  SubsystemConstants.CARRIAGE_MASS.inKilograms,
	SubsystemConstants.DRUM_RADIUS.inMeters,
	SubsystemConstants.MIN_HEIGHT.inMeters,
	SubsystemConstants.MAX_HEIGHT.inMeters,
	// simulate gravity boolean. only true if vertical elevator. can set to false if want easy tuning, but should set to true for FF tuning
	true,
	// likely the MIN_HEIGHT or MAX_HEIGHT
	SubsystemConstants.SIM_STARTING_HEIGHT.inMeters
)

Flywheel (rollers) Simulation

You can create a flywheel (rollers) simulation using the FlywheelSim class. https://github.wpilib.org/allwpilib/docs/release/java/edu/wpi/first/wpilibj/simulation/FlywheelSim.html Note that this can be used for open-loop rollers or a velocity-controlled flywheel. Here is an example constructor.

import edu.wpi.first.wpilibj.simulation.FlywheelSim
import edu.wpi.first.math.system.plant.DCMotor
import edu.wpi.first.math.system.plant.LinearSystemId
import com.team4099.robot2025.config.constants.SubsystemConstants

/* class fields */

private val subsystemSim = FlywheelSim(
	LinearSystemId.createFlywheelSystem(
	  DCMotor.getMotorName(numMotors),
	  SubsystemConstants.MOMENT_OF_INERTIA.inKilogramMeterSquared,
	  // reciprocal because in our code reductions are < 1, but in WPILib reductions are > 1
		1.0 / SubsystemConstants.GEAR_RATIO,
	),
	DCMotor.getMotorName(numMotors),
)

Feedback/Feedforward