class Robot : public frc::TimedRobot {
public:
void AutonomousInit() override {
// Generate trajectory
frc::TrajectoryConfig config{3.0_mps, 2.0_mps_sq};
config.SetKinematics(kinematics);
trajectory = frc::TrajectoryGenerator::GenerateTrajectory(
frc::Pose2d{0_m, 0_m, 0_deg},
{
frc::Translation2d{1_m, 1_m},
frc::Translation2d{2_m, -1_m}
},
frc::Pose2d{3_m, 0_m, 0_deg},
config
);
// Reset odometry to starting pose
odometry.ResetPosition(
gyro.GetRotation2d(),
leftEncoder.GetDistance(),
rightEncoder.GetDistance(),
trajectory.InitialPose()
);
timer.Reset();
timer.Start();
}
void AutonomousPeriodic() override {
// Update odometry
auto pose = odometry.Update(
gyro.GetRotation2d(),
leftEncoder.GetDistance(),
rightEncoder.GetDistance()
);
if (timer.Get() < trajectory.TotalTime()) {
// Sample trajectory at current time
auto goal = trajectory.Sample(timer.Get());
// Calculate chassis speeds
auto adjustedSpeeds = ramseteController.Calculate(pose, goal);
// Convert to wheel speeds
auto wheelSpeeds = kinematics.ToWheelSpeeds(adjustedSpeeds);
// Apply feedforward + feedback control
leftMotor.SetVoltage(
feedforward.Calculate(wheelSpeeds.left) +
leftPID.Calculate(
leftEncoder.GetRate(),
wheelSpeeds.left.value()
)
);
rightMotor.SetVoltage(
feedforward.Calculate(wheelSpeeds.right) +
rightPID.Calculate(
rightEncoder.GetRate(),
wheelSpeeds.right.value()
)
);
} else {
// Trajectory complete - stop
leftMotor.Set(0);
rightMotor.Set(0);
}
}
private:
frc::DifferentialDriveKinematics kinematics{0.7_m};
frc::DifferentialDriveOdometry odometry{/* ... */};
frc::RamseteController ramseteController;
frc::SimpleMotorFeedforward<units::meters> feedforward{
1.0_V, 3.0_V / 1_mps
};
frc::PIDController leftPID{1.0, 0, 0};
frc::PIDController rightPID{1.0, 0, 0};
frc::Trajectory trajectory;
frc::Timer timer;
};