Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Maple-Sim #251

Merged
merged 14 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@ wpi.java.configureTestTasks(test)
tasks.withType(JavaCompile) {
options.compilerArgs.add '-XDstringConcat=inline'
}

repositories {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed when you're done testing after you've released the latest version of maple sim that works with YAGSL

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes ur right, that was the plan

mavenLocal()
}
4 changes: 4 additions & 0 deletions src/main/java/frc/robot/RobotContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ public RobotContainer()
*/
private void configureBindings()
{
if (Robot.isSimulation())
{
driverXbox.start().onTrue(Commands.runOnce(() -> drivebase.resetOdometry(new Pose2d(3, 3, new Rotation2d()))));
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
}
if (DriverStation.isTest())
{
driverXbox.b().whileTrue(drivebase.sysIdDriveMotorCommand());
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/frc/robot/subsystems/swervedrive/Vision.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public void updatePoseEstimation(SwerveDrive swerveDrive)
{
if (SwerveDriveTelemetry.isSimulation)
{
visionSim.update(swerveDrive.getPose());
visionSim.update(swerveDrive.getSimulationDriveTrainPose().orElse(swerveDrive.getPose()));
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved

}
for (Cameras camera : Cameras.values())
Expand Down
157 changes: 120 additions & 37 deletions src/main/java/swervelib/SwerveDrive.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
import edu.wpi.first.math.numbers.N3;
import edu.wpi.first.math.trajectory.Trajectory;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.Alert;
import edu.wpi.first.wpilibj.*;
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
import edu.wpi.first.wpilibj.Alert.AlertType;
import edu.wpi.first.wpilibj.Notifier;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import java.util.ArrayList;
Expand All @@ -31,6 +29,8 @@
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import frc.robot.Robot;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User code should never be imported

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my problem, was trying to use TimedRobot.kDefaultPeriod

import org.ironmaple.simulation.SimulatedArena;
import org.ironmaple.simulation.drivesims.SwerveDriveSimulation;
import org.ironmaple.simulation.drivesims.SwerveModuleSimulation;
Expand All @@ -48,6 +48,8 @@
import swervelib.telemetry.SwerveDriveTelemetry;
import swervelib.telemetry.SwerveDriveTelemetry.TelemetryVerbosity;

import static edu.wpi.first.units.Units.*;
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved

/**
* Swerve Drive class representing and controlling the swerve drive.
*/
Expand Down Expand Up @@ -196,48 +198,50 @@ public SwerveDrive(
kinematics = new SwerveDriveKinematics(config.moduleLocationsMeters);
odometryThread = new Notifier(this::updateOdometry);

this.swerveModules = config.modules;

// Create an integrator for angle if the robot is being simulated to emulate an IMU
// If the robot is real, instantiate the IMU instead.
if (SwerveDriveTelemetry.isSimulation)
{
simIMU = new SwerveIMUSimulation();
imuReadingCache = new Cache<>(simIMU::getGyroRotation3d, 5L);
DriveTrainSimulationConfig simulationConfig = DriveTrainSimulationConfig.Default()
.withBumperSize(
Meters.of(config.getTracklength()).plus(Inches.of(5)),
Meters.of(config.getTrackwidth()).plus(Inches.of(5)))
.withRobotMass(Kilograms.of(config.physicalCharacteristics.robotMassKg))
.withCustomModuleTranslations(config.moduleLocationsMeters)
.withGyro(config.getGyroSim())
.withSwerveModule(() -> new SwerveModuleSimulation(
config.getDriveMotorSim(),
config.getAngleMotorSim(),
config.physicalCharacteristics.driveMotorCurrentLimit,
config.physicalCharacteristics.conversionFactor.drive.gearRatio,
config.physicalCharacteristics.conversionFactor.angle.gearRatio,
config.physicalCharacteristics.driveFrictionVoltage,
config.physicalCharacteristics.angleFrictionVoltage,
config.physicalCharacteristics.wheelGripCoefficientOfFriction,
Units.inchesToMeters(config.physicalCharacteristics.conversionFactor.drive.diameter/2),
0.02));

mapleSimDrive = new SwerveDriveSimulation(simulationConfig, startingPose);

// feed module simulation instances to modules
for (int i = 0; i < swerveModules.length; i++)
{
this.swerveModules[i].getSimModule().setMapleSimModule(mapleSimDrive.getModules()[i]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For efficiencies sake you may want to avoid recursively fetching your modules with mapleSimDrive.getModules() and just save them off in a local var.

}

// Setup MapleSim
SwerveModuleSimulation simModule = new SwerveModuleSimulation(config.getDriveMotorSim(),
config.getAngleMotorSim(),
config.physicalCharacteristics.driveMotorCurrentLimit,
config.physicalCharacteristics.conversionFactor.drive.gearRatio,
config.physicalCharacteristics.conversionFactor.angle.gearRatio,
config.physicalCharacteristics.driveFrictionVoltage,
config.physicalCharacteristics.angleFrictionVoltage,
config.physicalCharacteristics.wheelGripCoefficientOfFriction,
config.physicalCharacteristics.conversionFactor.drive.diameter /
2,
config.physicalCharacteristics.steerRotationalInertia);
DriveTrainSimulationConfig simCfg = new DriveTrainSimulationConfig(config.physicalCharacteristics.robotMassKg,
config.getTracklength() +
Units.inchesToMeters(5),
config.getTrackwidth() +
Units.inchesToMeters(5),
config.getTracklength(),
config.getTrackwidth(),
() -> {
return simModule;
},
config.getGyroSim());
mapleSimDrive = new SwerveDriveSimulation(simCfg, startingPose);
// register the drivetrain simulation
SimulatedArena.getInstance().addDriveTrainSimulation(mapleSimDrive);
} else
{

simIMU = new SwerveIMUSimulation(mapleSimDrive.getGyroSimulation());
imuReadingCache = new Cache<>(simIMU::getGyroRotation3d, 5L);
} else {
imu = config.imu;
imu.factoryDefault();
imuReadingCache = new Cache<>(imu::getRotation3d, 5L);
}

this.swerveModules = config.modules;

// odometry = new SwerveDriveOdometry(kinematics, getYaw(), getModulePositions());
swerveDrivePoseEstimator =
new SwerveDrivePoseEstimator(
Expand Down Expand Up @@ -285,6 +289,10 @@ public SwerveDrive(
}

odometryThread.startPeriodic(SwerveDriveTelemetry.isSimulation ? 0.01 : 0.02);
if (SwerveDriveTelemetry.isSimulation)
{
SimulatedArena.overrideSimulationTimings(0.01, 2);
}

checkIfTunerXCompatible();
}
Expand Down Expand Up @@ -338,6 +346,7 @@ private void checkIfTunerXCompatible()
public void setOdometryPeriod(double period)
{
odometryThread.stop();
SimulatedArena.overrideSimulationTimings(period, 2);
odometryThread.startPeriodic(period);
}

Expand All @@ -347,6 +356,7 @@ public void setOdometryPeriod(double period)
public void stopOdometryThread()
{
odometryThread.stop();
SimulatedArena.overrideSimulationTimings(TimedRobot.kDefaultPeriod, 5);
}

/**
Expand Down Expand Up @@ -693,7 +703,7 @@ public void setChassisSpeeds(ChassisSpeeds chassisSpeeds)
}

/**
* Gets the current pose (position and rotation) of the robot, as reported by odometry.
* Gets the measured pose (position and rotation) of the robot, as reported by odometry.
*
* @return The robot's pose
*/
Expand All @@ -707,7 +717,25 @@ public Pose2d getPose()
}

/**
* Gets the current field-relative velocity (x, y and omega) of the robot
* Gets the actual pose of the drivetrain during simulation
*
* @return an optional Pose2d, representing the drivetrain pose during simulation, or an empty optional when running on real robot
* */
public Optional<Pose2d> getSimulationDriveTrainPose()
{
if (SwerveDriveTelemetry.isSimulation)
{
odometryLock.lock();
Pose2d simulationPose = mapleSimDrive.getSimulatedDriveTrainPose();
odometryLock.unlock();
return Optional.of(simulationPose);
}

return Optional.empty();
}

/**
* Gets the measured field-relative robot velocity (x, y and omega)
*
* @return A ChassisSpeeds object of the current field-relative velocity
*/
Expand All @@ -721,6 +749,24 @@ public ChassisSpeeds getFieldVelocity()
kinematics.toChassisSpeeds(getStates()), getOdometryHeading().unaryMinus());
}

/**
* Gets the actual field-relative robot velocity (x, y and omega) during simulation
*
* @return An optional ChassisSpeeds representing the actual field-relative velocity of the robot, or an empty optional when running on real robot
*/
public Optional<ChassisSpeeds> getSimulationFieldVelocity()
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
{
if (SwerveDriveTelemetry.isSimulation)
{
odometryLock.lock();
ChassisSpeeds simulationFieldRelativeVelocity = mapleSimDrive.getDriveTrainSimulatedChassisSpeedsFieldRelative();
odometryLock.unlock();
return Optional.of(simulationFieldRelativeVelocity);
}

return Optional.empty();
}

/**
* Gets the current robot-relative velocity (x, y and omega) of the robot
*
Expand All @@ -731,6 +777,24 @@ public ChassisSpeeds getRobotVelocity()
return kinematics.toChassisSpeeds(getStates());
}

/**
* Gets the actual robot-relative robot velocity (x, y and omega) during simulation
*
* @return An optional ChassisSpeeds representing the actual robot-relative velocity of the robot, or an empty optional when running on real robot
*/
public Optional<ChassisSpeeds> getSimulationRobotVelocity()
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
{
if (SwerveDriveTelemetry.isSimulation)
{
odometryLock.lock();
ChassisSpeeds simulationFieldRelativeVelocity = mapleSimDrive.getDriveTrainSimulatedChassisSpeedsRobotRelative();
odometryLock.unlock();
return Optional.of(simulationFieldRelativeVelocity);
}

return Optional.empty();
}

/**
* Resets odometry to the given pose. Gyro angle and module positions do not need to be reset when calling this
* method. However, if either gyro angle or module position is reset, this must be called in order for odometry to
Expand All @@ -744,6 +808,14 @@ public void resetOdometry(Pose2d pose)
swerveDrivePoseEstimator.resetPosition(getYaw(), getModulePositions(), pose);
odometryLock.unlock();
kinematics.toSwerveModuleStates(ChassisSpeeds.fromFieldRelativeSpeeds(0, 0, 0, getYaw()));

// teleport the robot to that pose if it's a simulation
if (SwerveDriveTelemetry.isSimulation)
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
{
odometryLock.lock();
mapleSimDrive.setSimulationWorldPose(pose);
odometryLock.unlock();
}
}

/**
Expand Down Expand Up @@ -1041,13 +1113,17 @@ public void updateOdometry()
// Update odometry
swerveDrivePoseEstimator.update(getYaw(), getModulePositions());

if (SwerveDriveTelemetry.isSimulation)
{
SimulatedArena.getInstance().simulationPeriodic();
}

// Update angle accumulator if the robot is simulated
if (SwerveDriveTelemetry.verbosity.ordinal() >= TelemetryVerbosity.INFO.ordinal())
{
Pose2d[] modulePoses = getSwerveModulePoses(swerveDrivePoseEstimator.getEstimatedPosition());
if (SwerveDriveTelemetry.isSimulation)
{
SimulatedArena.getInstance().simulationPeriodic();
simIMU.updateOdometry(
kinematics,
getStates(),
Expand All @@ -1066,7 +1142,14 @@ public void updateOdometry()

if (SwerveDriveTelemetry.verbosity.ordinal() >= TelemetryVerbosity.POSE.ordinal())
{
field.setRobotPose(swerveDrivePoseEstimator.getEstimatedPosition());
if (SwerveDriveTelemetry.isSimulation)
{
field.setRobotPose(mapleSimDrive.getSimulatedDriveTrainPose());
field.getObject("OdometryPose").setPose(swerveDrivePoseEstimator.getEstimatedPosition());
} else
{
field.setRobotPose(swerveDrivePoseEstimator.getEstimatedPosition());
}
}

double sumVelocity = 0;
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/swervelib/SwerveModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ public void setAnglePIDF(PIDFConfig config)
*/
public void setDesiredState(SwerveModuleState desiredState, boolean isOpenLoop, boolean force)
{
desiredState = SwerveModuleState.optimize(desiredState, Rotation2d.fromDegrees(getAbsolutePosition()));
desiredState = SwerveModuleState.optimize(desiredState, getState().angle);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getState() is slower than getAbsolutePosition

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a little strange

so no doubt your code works on real robot

but in sim (since there are no absolute encoders), the getAbsolutePosition() always return 0, so the state optimization is jittering.

So I think it's better to have a if-else statement to let it use different functions during sim and real

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes total sense, the absolute encoder is fetch is wrapped in a Cache so you could make it reference the maplesim absolute encoder in the constructor instead of the real one.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion, I'll do that


// If we are forcing the angle
if (!force && antiJitterEnabled)
Expand Down Expand Up @@ -699,4 +699,9 @@ public void updateTelemetry()
SmartDashboard.putNumber(adjAbsoluteAngleName, getAbsolutePosition());
SmartDashboard.putNumber(absoluteEncoderIssueName, getAbsoluteEncoderReadIssue() ? 1 : 0);
}

public SwerveModuleSimulation getSimModule()
thenetworkgrinch marked this conversation as resolved.
Show resolved Hide resolved
{
return simModule;
}
}
31 changes: 8 additions & 23 deletions src/main/java/swervelib/simulation/SwerveIMUSimulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
import org.ironmaple.simulation.drivesims.GyroSimulation;

import java.util.Optional;

/**
Expand All @@ -16,27 +18,13 @@
public class SwerveIMUSimulation
{

/**
* Main timer to control movement estimations.
*/
private final Timer timer;
/**
* The last time the timer was read, used to determine position changes.
*/
private double lastTime;
/**
* Heading of the robot.
*/
private double angle;
private final GyroSimulation gyroSimulation;

/**
* Create the swerve drive IMU simulation.
*/
public SwerveIMUSimulation()
{
timer = new Timer();
timer.start();
lastTime = timer.get();
public SwerveIMUSimulation(GyroSimulation gyroSimulation) {
this.gyroSimulation = gyroSimulation;
}

/**
Expand All @@ -46,7 +34,7 @@ public SwerveIMUSimulation()
*/
public Rotation2d getYaw()
{
return new Rotation2d(angle);
return gyroSimulation.getGyroReading();
}

/**
Expand Down Expand Up @@ -76,7 +64,7 @@ public Rotation2d getRoll()
*/
public Rotation3d getGyroRotation3d()
{
return new Rotation3d(0, 0, angle);
return new Rotation3d(0, 0, getYaw().getRadians());
}

/**
Expand Down Expand Up @@ -104,9 +92,6 @@ public void updateOdometry(
Pose2d[] modulePoses,
Field2d field)
{

angle += kinematics.toChassisSpeeds(states).omegaRadiansPerSecond * (timer.get() - lastTime);
lastTime = timer.get();
field.getObject("XModules").setPoses(modulePoses);
}

Expand All @@ -117,6 +102,6 @@ public void updateOdometry(
*/
public void setAngle(double angle)
{
this.angle = angle;
this.gyroSimulation.setRotation(Rotation2d.fromRadians(angle));
}
}
Loading