36 static_assert(std::is_base_of_v<ctre::phoenix6::controls::ControlRequest, VelocityOutput>);
37 static_assert(std::is_base_of_v<ctre::phoenix6::controls::ControlRequest, PositionOutput>);
39 static inline const std::unordered_map<PhoenixControllerType, frc::DCMotor> MOTOR_SPEC_MAP{
40 {KRAKEN_X60, frc::DCMotor::KrakenX60()}, {KRAKEN_X60_FOC, frc::DCMotor::KrakenX60FOC()},
41 {KRAKEN_X44, frc::DCMotor::KrakenX44()}, {KRAKEN_X44_FOC, frc::DCMotor::KrakenX44FOC()},
42 {FALCON, frc::DCMotor::Falcon500()}, {FALCON_FOC, frc::DCMotor::Falcon500FOC()}};
48 ctre::phoenix6::hardware::core::CoreTalonFX{
canID, ctre::phoenix6::CANBus{
canbus}} {
51 pidf.
P = FALCON_PIDF_KP;
52 pidf.
I = FALCON_PIDF_KI;
53 pidf.
D = FALCON_PIDF_KD;
58 config.MotorOutput.Inverted =
inverted;
61 req_pos_out.UpdateFreqHz = 0
_Hz;
63 req_vel_out.UpdateFreqHz = 0
_Hz;
66 PhoenixController::setCurrentLimits(STATOR_CURRENT_LIMIT, SUPPLY_CURRENT_LIMIT, SUPPLY_CURRENT_THRESHOLD, SUPPLY_TIME_THRESHOLD);
71 GetSimState().Orientation =
inverted ? ctre::phoenix6::sim::ChassisReference::Clockwise_Positive
72 : ctre::phoenix6::sim::ChassisReference::CounterClockwise_Positive;
75 GetSimState().SetMotorType(ctre::phoenix6::sim::TalonFXSimState::MotorType::KrakenX44);
77 GetSimState().SetMotorType(ctre::phoenix6::sim::TalonFXSimState::MotorType::KrakenX60);
82 void enableFOC(
bool enableFOC) { focEnabled = enableFOC; }
86 void reset()
override { setEncoderPosition(0
_tr); }
89 config.MotorOutput.DutyCycleNeutralDeadband = DEADBAND.value();
90 config.MotorOutput.NeutralMode =
mode == valor::NeutralMode::Brake ? ctre::phoenix6::signals::NeutralModeValue::Brake
91 : ctre::phoenix6::signals::NeutralModeValue::Coast;
100 config.CurrentLimits.StatorCurrentLimitEnable =
true;
102 config.CurrentLimits.SupplyCurrentLimitEnable =
true;
105 config.TorqueCurrent.PeakForwardTorqueCurrent = PEAK_TORQUE_CURRENT;
106 config.TorqueCurrent.PeakReverseTorqueCurrent = -PEAK_TORQUE_CURRENT;
144 if (getOpMode() != OperationalMode::Operational) {
152 ff += units::volt_t{req_pos_out.Slot < 2 ? (req_pos_out.Slot == 1 ? config.Slot1.kS : config.Slot0.kS) : config.Slot2.kS} *
157 req_pos_out.FeedForward =
ff;
161 using BaseController::setPosition;
162 void setPosition(units::turn_t
position, units::turns_per_second_t maxVelocity, units::turns_per_second_squared_t maxAcceleration,
163 units::turns_per_second_cubed_t maxJerk,
int slot = 0) {
164 req_pos_out.Velocity = maxVelocity;
165 req_pos_out.Acceleration = maxAcceleration;
166 req_pos_out.Jerk = maxJerk;
172 SetControl(tryEnableFOC(req_vel_out.WithSlot(
slot).WithVelocity(velocity)));
175 void powerSetter(
double out)
override {
SetControl(ctre::phoenix6::controls::DutyCycleOut{
out}.WithEnableFOC(focEnabled)); }
176 void powerSetter(units::volt_t
out)
override {
SetControl(ctre::phoenix6::controls::VoltageOut{
out}.WithEnableFOC(focEnabled)); }
177 void powerSetter(units::ampere_t
out)
override {
SetControl(ctre::phoenix6::controls::TorqueCurrentFOC{
out}); }
180 config.MotorOutput.ControlTimesyncFreqHz =
controlFreq;
181 req_pos_out.UseTimesync =
true;
182 req_vel_out.UseTimesync =
true;
189 followerMotor = std::make_unique<PhoenixController<>>(
190 PhoenixControllerType::KRAKEN_X60_FOC,
canID,
191 config.MotorOutput.NeutralMode == ctre::phoenix6::signals::NeutralModeValue::Brake ? NeutralMode::Brake : NeutralMode::Coast,
193 followerMotor->applyConfig();
194 followerMotor->SetControl(ctre::phoenix6::controls::StrictFollower(
GetDeviceID()));
195 LogChild(
"Follower", followerMotor.get());
199 if constexpr (
requires { req_pos_out.Jerk; }) {
200 req_pos_out.Jerk = pidf.
maxJerk;
204 config.MotionMagic.MotionMagicJerk = pidf.
maxJerk;
206 config.MotionMagic.MotionMagicCruiseVelocity = pidf.
maxVelocity;
220 void setupReverseHardwareLimit(
int canID, ctre::phoenix6::signals::ReverseLimitTypeValue type, units::turn_t
autosetPosition = 0
_tr,
222 config.HardwareLimitSwitch.ReverseLimitType = type;
223 config.HardwareLimitSwitch.ReverseLimitEnable =
true;
224 config.HardwareLimitSwitch.ReverseLimitRemoteSensorID =
canID;
225 config.HardwareLimitSwitch.ReverseLimitAutosetPositionValue =
autosetPosition;
226 config.HardwareLimitSwitch.ReverseLimitAutosetPositionEnable =
false;
227 config.HardwareLimitSwitch.ReverseLimitSource = ctre::phoenix6::signals::ReverseLimitSourceValue::RemoteCANdiS1;
233 void setupForwardHardwareLimit(
int canID, ctre::phoenix6::signals::ForwardLimitTypeValue type, units::turn_t
autosetPosition = 0
_tr,
235 config.HardwareLimitSwitch.ForwardLimitType = type;
236 config.HardwareLimitSwitch.ForwardLimitEnable =
true;
237 config.HardwareLimitSwitch.ForwardLimitRemoteSensorID =
canID;
238 config.HardwareLimitSwitch.ForwardLimitAutosetPositionValue =
autosetPosition;
239 config.HardwareLimitSwitch.ForwardLimitAutosetPositionEnable =
true;
240 config.HardwareLimitSwitch.ForwardLimitSource = ctre::phoenix6::signals::ReverseLimitSourceValue::RemoteCANdiS1;
247 config.SoftwareLimitSwitch.ForwardSoftLimitEnable =
true;
248 config.SoftwareLimitSwitch.ForwardSoftLimitThreshold =
forward;
255 config.SoftwareLimitSwitch.ReverseSoftLimitEnable =
true;
256 config.SoftwareLimitSwitch.ReverseSoftLimitThreshold =
reverse;
266 config.Feedback.RotorToSensorRatio = rotorToSensor;
267 config.Feedback.SensorToMechanismRatio = sensorToMech;
273 ctre::phoenix6::signals::MagnetHealthValue getMagnetHealth() {
275 return ctre::phoenix6::signals::MagnetHealthValue::Magnet_Invalid;
276 return cancoder->GetMagnetHealth(
false).GetValue();
279 units::turn_t getAbsEncoderPosition()
override {
280 if (cancoder ==
nullptr)
282 return cancoder->GetAbsolutePosition(
false).GetValue();
287 cancoder = std::make_unique<ctre::phoenix6::hardware::CANcoder>(
deviceId, ctre::phoenix6::CANBus{
canbus});
288 wpi::SendableRegistry::Remove(cancoder.get());
290 cancoderConfig = std::make_unique<ctre::phoenix6::configs::MagnetSensorConfigs>();
291 cancoderConfig->AbsoluteSensorDiscontinuityPoint =
absoluteRange;
292 cancoderConfig->SensorDirection =
clockwise ? ctre::phoenix6::signals::SensorDirectionValue::Clockwise_Positive
293 : ctre::phoenix6::signals::SensorDirectionValue::CounterClockwise_Positive;
294 cancoderConfig->MagnetOffset = -
offset;
295 cancoder->GetConfigurator().Apply(*cancoderConfig);
297 config.Feedback.FeedbackRemoteSensorID = cancoder->GetDeviceID();
298 config.Feedback.FeedbackSensorSource = ctre::phoenix6::signals::FeedbackSensorSourceValue::FusedCANcoder;
300 cancoder->GetSimState().Orientation =
clockwise ? ctre::phoenix6::sim::ChassisReference::Clockwise_Positive
301 : ctre::phoenix6::sim::ChassisReference::CounterClockwise_Positive;
303 util::PhoenixSignalManager::GetInstance().AddSignals(cancoder->GetNetwork(), cancoder->GetAbsolutePosition(),
304 cancoder->GetMagnetHealth(), cancoder->GetFault_BadMagnet());
310 ctre::phoenix6::hardware::CANcoder* getCANCoder() {
return cancoder.get(); }
318 config.OpenLoopRamps.DutyCycleOpenLoopRampPeriod =
time;
323 units::volt_t calculateAppliedVoltage()
override {
324 GetSimState().SetSupplyVoltage(frc::RobotController::GetBatteryVoltage());
326 cancoder->GetSimState().SetSupplyVoltage(frc::RobotController::GetBatteryVoltage());
332 void setSimState(frc::sim::DCMotorSim& sim)
override {
333 GetSimState().SetRawRotorPosition(sim.GetAngularPosition() * rotorToSensor * sensorToMech);
334 GetSimState().SetRotorVelocity(sim.GetAngularVelocity() * rotorToSensor * sensorToMech);
335 GetSimState().SetRotorAcceleration(sim.GetAngularAcceleration() * rotorToSensor * sensorToMech);
345 cancoder->GetSimState().SetRawPosition(sim.GetAngularPosition() * sensorToMech - cancoderConfig->MagnetOffset);
346 cancoder->GetSimState().SetVelocity(sim.GetAngularVelocity() * sensorToMech);
350 bool GetFault_BadMagnet(
bool refresh =
true) {
352 auto&
faultSignal = cancoder->GetFault_BadMagnet();
367 WriteLog(
"Absolute Position", cancoder->GetAbsolutePosition(
false).GetValue());
371 static constexpr units::ampere_t SUPPLY_CURRENT_THRESHOLD = 65
_A;
372 static constexpr units::ampere_t STATOR_CURRENT_LIMIT = 80
_A;
373 static constexpr units::ampere_t SUPPLY_CURRENT_LIMIT = 80
_A;
374 static constexpr units::millisecond_t SUPPLY_TIME_THRESHOLD = 1000
_ms;
375 static constexpr units::ampere_t PEAK_TORQUE_CURRENT = 100
_A;
376 static constexpr units::turn_t DEADBAND = 0.01
_tr;
378 static constexpr double FALCON_PIDF_KP = 10.0f;
379 static constexpr double FALCON_PIDF_KI = 0.0f;
380 static constexpr double FALCON_PIDF_KD = 0.0f;
381 static constexpr units::turns_per_second_t FALCON_PIDF_KV = 6
_tps;
382 static constexpr units::turns_per_second_squared_t FALCON_PIDF_KA = 130
_tr_per_s_sq;
383 static constexpr units::turns_per_second_cubed_t FALCON_PIDF_KJ = 650
_tr_per_s_cu;
397 slot.GravityArmPositionOffset = pidf.aFFTarget;
398 slot.GravityType = pidf.aFFType == valor::FeedForwardType::LINEAR ? ctre::phoenix6::signals::GravityTypeValue::Elevator_Static
399 : ctre::phoenix6::signals::GravityTypeValue::Arm_Cosine;
408 template <
typename T>
410 if constexpr (
requires {
request.EnableFOC; })
411 request.EnableFOC = focEnabled;
422 std::unique_ptr<PhoenixController<>> followerMotor;
427 std::unique_ptr<ctre::phoenix6::hardware::CANcoder> cancoder;
428 std::unique_ptr<ctre::phoenix6::configs::MagnetSensorConfigs> cancoderConfig;
430 ctre::phoenix6::configs::TalonFXConfiguration config;
432 bool focEnabled =
true;