40class CorePhoenixController :
public BaseController,
public ctre::phoenix6::hardware::core::CoreTalonFX {
41 static inline const std::unordered_map<PhoenixControllerType, frc::DCMotor> MOTOR_SPEC_MAP{
42 {KRAKEN_X60, frc::DCMotor::KrakenX60()}, {KRAKEN_X60_FOC, frc::DCMotor::KrakenX60FOC()},
43 {KRAKEN_X44, frc::DCMotor::KrakenX44()}, {KRAKEN_X44_FOC, frc::DCMotor::KrakenX44FOC()},
44 {FALCON, frc::DCMotor::Falcon500()}, {FALCON_FOC, frc::DCMotor::Falcon500FOC()}};
47 CorePhoenixController(valor::PhoenixControllerType controllerType,
int canID, valor::NeutralMode neutralMode,
bool inverted,
48 std::string canbus =
"");
50 explicit CorePhoenixController(valor::PhoenixControllerType controllerType)
51 :
BaseController{MOTOR_SPEC_MAP.at(controllerType)}, ctre::phoenix6::hardware::core::CoreTalonFX{-1} {}
53 virtual ~CorePhoenixController();
55 inline void ApplyConfig()
override { GetConfigurator().Apply(config, 5_s); }
57 inline void Reset()
override { SetEncoderPosition(0_tr); }
59 void SetNeutralMode(valor::NeutralMode mode,
bool saveImmediately =
false)
override;
61 void SetCurrentLimits(units::ampere_t statorCurrentLimit, units::ampere_t supplyCurrentLimit, units::ampere_t supplyCurrentThreshold,
62 units::second_t supplyTimeThreshold,
bool saveImmediately =
false);
64 void SetVoltageLimits(units::volt_t reverseLimit, units::volt_t forwardLimit,
bool saveImmediately =
false);
68 inline ctre::phoenix6::StatusSignal<units::turn_t>& GetPositionSignal(
bool refresh =
true) {
69 return ctre::phoenix6::hardware::core::CoreTalonFX::GetPosition(refresh);
72 units::turns_per_second_t
GetSpeed()
const override;
76 inline void SetEncoderPosition(units::turn_t position)
override { ctre::phoenix6::hardware::core::CoreTalonFX::SetPosition(position); }
78 void SetContinuousWrap(
bool continuousWrap,
bool saveImmediately =
false);
80 void PowerSetter(Output out)
override;
84 void SetupReverseHardwareLimit(
int canID, ctre::phoenix6::signals::ReverseLimitTypeValue type, units::turn_t autosetPosition = 0_tr,
85 bool saveImmediately =
false);
87 void SetupForwardHardwareLimit(
int canID, ctre::phoenix6::signals::ForwardLimitTypeValue type, units::turn_t autosetPosition = 0_tr,
88 bool saveImmediately =
false);
90 void SetForwardLimit(units::turn_t forward,
bool saveImmediately =
false)
override;
92 void SetReverseLimit(units::turn_t reverse,
bool saveImmediately =
false)
override;
94 void SetGearRatios(
double _rotorToSensor,
double _sensorToMech,
bool saveImmediately =
false)
override;
96 ctre::phoenix6::signals::MagnetHealthValue GetMagnetHealth();
98 units::turn_t GetAbsoluteEncoderPosition()
const override;
100 void SetupCANcoder(
int deviceId, units::turn_t offset,
bool clockwise, std::string canbus =
"", units::turn_t absoluteRange = 1_tr,
101 bool saveImmediately =
false);
103 inline ctre::phoenix6::hardware::CANcoder* GetCANcoder() {
return cancoder.get(); }
105 void SetOpenLoopRamp(units::second_t time,
bool saveImmediately =
false)
override;
107 units::volt_t CalculateAppliedVoltage()
override;
109 using BaseController::SetSimState;
110 void SetSimState(units::turn_t pos, units::turns_per_second_t vel, units::turns_per_second_squared_t accel);
115 static constexpr units::ampere_t STATOR_CURRENT_LIMIT = 40_A;
116 static constexpr units::ampere_t SUPPLY_CURRENT_LIMIT = 30_A;
117 static constexpr units::ampere_t SUPPLY_CURRENT_THRESHOLD = 30_A;
118 static constexpr units::millisecond_t SUPPLY_TIME_THRESHOLD = 1_s;
119 static constexpr units::ampere_t PEAK_TORQUE_CURRENT = 100_A;
120 static constexpr units::turn_t DEADBAND = 0.01_tr;
122 std::unique_ptr<CorePhoenixController> followerMotor;
124 std::unique_ptr<ctre::phoenix6::hardware::CANcoder> cancoder;
125 std::unique_ptr<ctre::phoenix6::configs::MagnetSensorConfigs> cancoderConfig;
127 ctre::phoenix6::configs::TalonFXConfiguration config;
129 ctre::phoenix6::controls::VoltageOut voltageRequest{0_V};
130 ctre::phoenix6::controls::DutyCycleOut dutyCycleRequest{0};
131 ctre::phoenix6::controls::TorqueCurrentFOC currentRequest{0_A};
136class PhoenixController :
public CorePhoenixController {
137 static_assert(std::is_base_of_v<ctre::phoenix6::controls::ControlRequest, VelocityOutput>);
138 static_assert(std::is_base_of_v<ctre::phoenix6::controls::ControlRequest, PositionOutput>);
141 PhoenixController(valor::PhoenixControllerType controllerType,
int canID, valor::NeutralMode neutralMode,
bool inverted,
142 std::string canbus =
"")
143 : CorePhoenixController{controllerType, canID, neutralMode, inverted, canbus} {
144 positionRequest.UpdateFreqHz = 0_Hz;
145 velocityRequest.UpdateFreqHz = 0_Hz;
148 explicit PhoenixController(PhoenixControllerType controllerType = KRAKEN_X60_FOC) : CorePhoenixController{controllerType} {}
150 inline void EnableFOC(
bool enable) {
151 TryEnableFOC(voltageRequest, enable);
152 TryEnableFOC(dutyCycleRequest, enable);
153 TryEnableFOC(currentRequest, enable);
154 TryEnableFOC(velocityRequest, enable);
155 TryEnableFOC(positionRequest, enable);
158 void SetNeutralMode(valor::NeutralMode mode,
bool saveImmediately =
false)
override {
159 if constexpr (
requires { positionRequest.OverrideBrakeDurNeutral; })
160 positionRequest.OverrideBrakeDurNeutral = mode == valor::NeutralMode::Brake;
162 positionRequest.OverrideCoastDurNeutral = mode == valor::NeutralMode::Coast;
164 if constexpr (
requires { velocityRequest.OverrideBrakeDurNeutral; })
165 velocityRequest.OverrideBrakeDurNeutral = mode == valor::NeutralMode::Brake;
167 velocityRequest.OverrideCoastDurNeutral = mode == valor::NeutralMode::Coast;
172 void PositionSetter(units::turn_t position, Output feedforward,
int slot)
override {
174 [=,
this](
auto&& ff) {
175 using FFType =
decltype(positionRequest.FeedForward);
176 if constexpr (std::is_same_v<std::decay_t<
decltype(ff)>, FFType>)
177 SetControl(positionRequest.WithSlot(slot).WithPosition(position).WithFeedForward(ff));
180 FRC_ReportWarning(
"Position control feedforward is of the wrong unit, using zero instead...");
181 SetControl(positionRequest.WithSlot(slot).WithPosition(position).WithFeedForward(FFType{0}));
187 using BaseController::SetPosition;
189 void SetPosition(units::turn_t position, units::turns_per_second_t maxVelocity, units::turns_per_second_squared_t maxAcceleration,
190 units::turns_per_second_cubed_t maxJerk, Output feedforward = 0,
int slot = 0) {
191 positionRequest.Velocity = maxVelocity;
192 positionRequest.Acceleration = maxAcceleration;
193 positionRequest.Jerk = maxJerk;
194 SetPosition(position, feedforward, slot);
197 void SpeedSetter(units::turns_per_second_t velocity, Output feedforward,
int slot)
override {
199 [=,
this](
auto&& ff) {
200 using FFType =
decltype(velocityRequest.FeedForward);
201 if constexpr (std::is_same_v<std::decay_t<
decltype(ff)>, FFType>)
202 SetControl(velocityRequest.WithSlot(slot).WithVelocity(velocity).WithFeedForward(ff));
205 FRC_ReportWarning(
"Velocity control feedforward is of the wrong unit, using zero instead...");
206 SetControl(velocityRequest.WithSlot(slot).WithVelocity(velocity).WithFeedForward(FFType{0}));
212 void UseTimesync(units::hertz_t controlFreq,
bool saveImmediately =
false) {
213 config.MotorOutput.ControlTimesyncFreqHz = controlFreq;
214 positionRequest.UseTimesync =
true;
215 velocityRequest.UseTimesync =
true;
218 GetConfigurator().Apply(config.MotorOutput);
222 if constexpr (
requires { positionRequest.Jerk; }) {
223 positionRequest.Jerk =
decltype(positionRequest.Jerk)(pidf.maxJerk.value());
224 positionRequest.Acceleration =
decltype(positionRequest.Acceleration)(pidf.maxAcceleration.value());
225 positionRequest.Velocity =
decltype(positionRequest.Velocity)(pidf.maxVelocity.value());
228 config.MotionMagic.MotionMagicJerk =
decltype(config.MotionMagic.MotionMagicJerk)(pidf.maxJerk.value());
229 config.MotionMagic.MotionMagicAcceleration =
decltype(config.MotionMagic.MotionMagicAcceleration)(pidf.maxAcceleration.value());
230 config.MotionMagic.MotionMagicCruiseVelocity =
decltype(config.MotionMagic.MotionMagicCruiseVelocity)(pidf.maxVelocity.value());
232 if (saveImmediately) {
233 GetConfigurator().Apply(config.MotionMagic);
237 SetPIDFSlot(config.Slot1, pidf, saveImmediately);
239 SetPIDFSlot(config.Slot2, pidf, saveImmediately);
241 SetPIDFSlot(config.Slot0, pidf, saveImmediately);
245 inline PositionOutput CreatePositionRequest() {
246 if constexpr (
requires { PositionOutput{0_tr}; })
249 return {0_tr, 0_tps, 0_tr_per_s_sq};
253 template <
typename T>
254 static constexpr void TryEnableFOC(T& request,
bool enable) {
255 if constexpr (
requires { request.EnableFOC; })
256 request.EnableFOC = enable;
260 void SetPIDFSlot(S& slot, valor::PIDF<> pidf,
bool saveImmediately) {
263 slot.kP = pidf.P.value();
264 slot.kI = pidf.I.value();
265 slot.kD = pidf.D.value();
266 slot.kS = pidf.kS.value();
268 slot.kV = (*pidf.kV).value();
269 else if constexpr (std::is_same_v<
decltype(positionRequest.FeedForward), units::ampere_t> ||
270 std::is_same_v<
decltype(velocityRequest.FeedForward), units::ampere_t>)
276 slot.GravityArmPositionOffset = pidf.GTarget;
277 slot.GravityType = pidf.GType == valor::GravityFFType::LINEAR ? ctre::phoenix6::signals::GravityTypeValue::Elevator_Static
278 : ctre::phoenix6::signals::GravityTypeValue::Arm_Cosine;
279 slot.kG = pidf.kG.value();
282 GetConfigurator().Apply(slot);
285 VelocityOutput velocityRequest{0_tps};
286 PositionOutput positionRequest = CreatePositionRequest();