Valor 6800 1.0
Loading...
Searching...
No Matches
PhoenixController.h
1
2#pragma once
3
4#include <ctime>
5#include <ctre/phoenix6/sim/ChassisReference.hpp>
6#include <memory>
7#include <string>
8#include <unordered_map>
9
10#include <ctre/phoenix6/CANBus.hpp>
11#include <ctre/phoenix6/CANcoder.hpp>
12#include <ctre/phoenix6/TalonFX.hpp>
13#include <units/voltage.h>
14#include <frc/RobotController.h>
15
16#include "valkyrie/controllers/BaseController.h"
17#include "valkyrie/util/PhoenixSignalManager.h"
18
19// Conversion guide:
20// https://v6.docs.ctr-electronics.com/en/latest/docs/migration/migration-guide/closed-loop-guide.html
21
22namespace valor {
23
24enum PhoenixControllerType { KRAKEN_X60_FOC, KRAKEN_X60, KRAKEN_X44, KRAKEN_X44_FOC, FALCON_FOC, FALCON };
25
26template <class VelocityOutput = ctre::phoenix6::controls::VelocityVoltage,
27 class PositionOutput = ctre::phoenix6::controls::DynamicMotionMagicVoltage>
28#ifdef _MSC_VER
29#pragma warning(push)
30#pragma warning(disable : 4250) // Disable "inherits via dominance" warning
31#endif
32class PhoenixController : public BaseController, public ctre::phoenix6::hardware::core::CoreTalonFX {
33#ifdef _MSC_VER
34#pragma warning(pop)
35#endif
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>);
38
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()}};
43
44 public:
45 PhoenixController(valor::PhoenixControllerType controllerType, int canID, valor::NeutralMode neutralMode, bool inverted,
46 std::string canbus = "", units::millisecond_t _loggingPeriod = 100_ms)
47 : BaseController{MOTOR_SPEC_MAP.at(controllerType)},
48 ctre::phoenix6::hardware::core::CoreTalonFX{canID, ctre::phoenix6::CANBus{canbus}} {
49 // Remove default LiveWindow entry for TalonFX
50 valor::PIDF pidf;
51 pidf.P = FALCON_PIDF_KP;
52 pidf.I = FALCON_PIDF_KI;
53 pidf.D = FALCON_PIDF_KD;
54 pidf.error = 0_tr;
55 pidf.maxVelocity = FALCON_PIDF_KV;
56 pidf.maxAcceleration = FALCON_PIDF_KA;
57
58 config.MotorOutput.Inverted = inverted;
59
60 req_pos_out.Slot = 0;
61 req_pos_out.UpdateFreqHz = 0_Hz;
62 req_vel_out.Slot = 0;
63 req_vel_out.UpdateFreqHz = 0_Hz;
64
65 PhoenixController::setNeutralMode(neutralMode);
66 PhoenixController::setCurrentLimits(STATOR_CURRENT_LIMIT, SUPPLY_CURRENT_LIMIT, SUPPLY_CURRENT_THRESHOLD, SUPPLY_TIME_THRESHOLD);
68 util::PhoenixSignalManager::GetInstance().AddSignals(GetNetwork(), GetPosition(false), GetVelocity(false), GetStatorCurrent(false),
69 GetMotorVoltage(false));
70
71 GetSimState().Orientation = inverted ? ctre::phoenix6::sim::ChassisReference::Clockwise_Positive
72 : ctre::phoenix6::sim::ChassisReference::CounterClockwise_Positive;
73
74 if (controllerType == KRAKEN_X44 || controllerType == KRAKEN_X44_FOC)
75 GetSimState().SetMotorType(ctre::phoenix6::sim::TalonFXSimState::MotorType::KrakenX44);
76 else
77 GetSimState().SetMotorType(ctre::phoenix6::sim::TalonFXSimState::MotorType::KrakenX60);
78
80 };
81
82 void enableFOC(bool enableFOC) { focEnabled = enableFOC; }
83
84 void applyConfig() override { GetConfigurator().Apply(config, 5_s); }
85
86 void reset() override { setEncoderPosition(0_tr); }
87
88 void setNeutralMode(valor::NeutralMode mode, bool saveImmediately = false) {
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;
92
94 GetConfigurator().Apply(config.MotorOutput);
95 }
96
97 void setCurrentLimits(units::ampere_t statorCurrentLimit, units::ampere_t supplyCurrentLimit, units::ampere_t supplyCurrentThreshold,
98 units::second_t supplyTimeThreshold, bool saveImmediately = false) {
99 config.CurrentLimits.StatorCurrentLimit = statorCurrentLimit;
100 config.CurrentLimits.StatorCurrentLimitEnable = true;
101 config.CurrentLimits.SupplyCurrentLimit = supplyCurrentLimit;
102 config.CurrentLimits.SupplyCurrentLimitEnable = true;
103 config.CurrentLimits.SupplyCurrentLowerTime = supplyTimeThreshold;
104 config.CurrentLimits.SupplyCurrentLowerLimit = supplyCurrentThreshold;
105 config.TorqueCurrent.PeakForwardTorqueCurrent = PEAK_TORQUE_CURRENT;
106 config.TorqueCurrent.PeakReverseTorqueCurrent = -PEAK_TORQUE_CURRENT;
107
108 if (saveImmediately) {
109 GetConfigurator().Apply(config.CurrentLimits);
110 GetConfigurator().Apply(config.TorqueCurrent);
111 }
112 }
113
114 void setVoltageLimits(units::volt_t reverseLimit, units::volt_t forwardLimit, bool saveImmediately = false) {
115 config.Voltage.PeakForwardVoltage = forwardLimit;
116 config.Voltage.PeakReverseVoltage = reverseLimit;
117
118 if (saveImmediately) {
119 GetConfigurator().Apply(config.Voltage);
120 }
121 }
122
123 units::turn_t getPosition() override { return GetPosition(false).GetValue(); }
124
125 units::turns_per_second_t getSpeed() override { return GetVelocity(false).GetValue(); }
126
127 units::ampere_t getCurrent() override { return GetStatorCurrent(false).GetValue(); }
128
129 void setEncoderPosition(units::turn_t position) override { SetPosition(position); }
130
131 void setContinuousWrap(bool continuousWrap, bool saveImmediately = false) {
132 config.ClosedLoopGeneral.ContinuousWrap = continuousWrap;
133
134 if (saveImmediately)
135 GetConfigurator().Apply(config.ClosedLoopGeneral);
136 }
137
138 void positionSetter(units::turn_t position, int slot = 0) override {
139 WriteLog("reqPosition", position);
140 SetControl(tryEnableFOC(req_pos_out.WithSlot(slot).WithPosition(position)));
141 }
142
143 void setPositionWithFeedForward(units::turn_t position, units::turns_per_second_t feedforwardVelocity, int slot = 0) override {
144 if (getOpMode() != OperationalMode::Operational) {
146 return;
147 }
148 // Convert velocity to voltage: kV * velocity + kS * sign(velocity)
149 double kV = (voltageCompensation / getMaxMechSpeed()).value();
150 units::volt_t ff = units::volt_t{kV * feedforwardVelocity.value()};
151 if (units::math::abs(feedforwardVelocity) > 0.01_tps) {
152 ff += units::volt_t{req_pos_out.Slot < 2 ? (req_pos_out.Slot == 1 ? config.Slot1.kS : config.Slot0.kS) : config.Slot2.kS} *
153 (feedforwardVelocity.value() > 0 ? 1.0 : -1.0);
154 }
155 WriteLog("reqPosition", position);
156 WriteLog("reqFeedForward", ff);
157 req_pos_out.FeedForward = ff;
158 SetControl(tryEnableFOC(req_pos_out.WithSlot(slot).WithPosition(position)));
159 }
160
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;
167 setPosition(position, slot);
168 }
169
170 void speedSetter(units::turns_per_second_t velocity, int slot = 0) override {
171 WriteLog("reqSpeed", velocity);
172 SetControl(tryEnableFOC(req_vel_out.WithSlot(slot).WithVelocity(velocity)));
173 }
174
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}); }
178
179 void useTimesync(units::hertz_t controlFreq, bool saveImmediately = false) {
180 config.MotorOutput.ControlTimesyncFreqHz = controlFreq;
181 req_pos_out.UseTimesync = true;
182 req_vel_out.UseTimesync = true;
183
184 if (saveImmediately)
185 GetConfigurator().Apply(config.MotorOutput);
186 }
187
188 void setupFollower(int canID, bool followerInverted = false) override {
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,
192 followerInverted, std::string{GetNetwork().GetName()});
193 followerMotor->applyConfig();
194 followerMotor->SetControl(ctre::phoenix6::controls::StrictFollower(GetDeviceID()));
195 LogChild("Follower", followerMotor.get());
196 }
197
198 void setPIDF(valor::PIDF pidf, int slot = 0, bool saveImmediately = false) override {
199 if constexpr (requires { req_pos_out.Jerk; }) {
200 req_pos_out.Jerk = pidf.maxJerk;
201 req_pos_out.Acceleration = pidf.maxAcceleration;
202 req_pos_out.Velocity = pidf.maxVelocity;
203 }
204 config.MotionMagic.MotionMagicJerk = pidf.maxJerk;
205 config.MotionMagic.MotionMagicAcceleration = pidf.maxAcceleration;
206 config.MotionMagic.MotionMagicCruiseVelocity = pidf.maxVelocity;
207
208 if (saveImmediately) {
209 GetConfigurator().Apply(config.MotionMagic);
210 }
211
212 if (slot == 1)
213 setPIDFSlot(config.Slot1, pidf, saveImmediately);
214 else if (slot == 2)
215 setPIDFSlot(config.Slot2, pidf, saveImmediately);
216 else
217 setPIDFSlot(config.Slot0, pidf, saveImmediately); // Default case
218 }
219
220 void setupReverseHardwareLimit(int canID, ctre::phoenix6::signals::ReverseLimitTypeValue type, units::turn_t autosetPosition = 0_tr,
221 bool saveImmediately = false) {
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;
228
229 if (saveImmediately)
230 GetConfigurator().Apply(config.HardwareLimitSwitch);
231 }
232
233 void setupForwardHardwareLimit(int canID, ctre::phoenix6::signals::ForwardLimitTypeValue type, units::turn_t autosetPosition = 0_tr,
234 bool saveImmediately = false) {
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;
241
242 if (saveImmediately)
243 GetConfigurator().Apply(config.HardwareLimitSwitch);
244 }
245
246 void setForwardLimit(units::turn_t forward, bool saveImmediately = false) override {
247 config.SoftwareLimitSwitch.ForwardSoftLimitEnable = true;
248 config.SoftwareLimitSwitch.ForwardSoftLimitThreshold = forward;
249
250 if (saveImmediately)
251 GetConfigurator().Apply(config.SoftwareLimitSwitch);
252 }
253
254 void setReverseLimit(units::turn_t reverse, bool saveImmediately = false) override {
255 config.SoftwareLimitSwitch.ReverseSoftLimitEnable = true;
256 config.SoftwareLimitSwitch.ReverseSoftLimitThreshold = reverse;
257
258 if (saveImmediately)
259 GetConfigurator().Apply(config.SoftwareLimitSwitch);
260 }
261
262 void setGearRatios(double _rotorToSensor, double _sensorToMech, bool saveImmediately = false) override {
263 rotorToSensor = _rotorToSensor;
264 sensorToMech = _sensorToMech;
265
266 config.Feedback.RotorToSensorRatio = rotorToSensor;
267 config.Feedback.SensorToMechanismRatio = sensorToMech;
268
269 if (saveImmediately)
270 GetConfigurator().Apply(config.Feedback);
271 }
272
273 ctre::phoenix6::signals::MagnetHealthValue getMagnetHealth() {
274 if (!cancoder)
275 return ctre::phoenix6::signals::MagnetHealthValue::Magnet_Invalid;
276 return cancoder->GetMagnetHealth(false).GetValue();
277 }
278
279 units::turn_t getAbsEncoderPosition() override {
280 if (cancoder == nullptr)
281 return 0_tr;
282 return cancoder->GetAbsolutePosition(false).GetValue();
283 }
284
285 void setupCANCoder(int deviceId, units::turn_t offset, bool clockwise, std::string canbus = "", units::turn_t absoluteRange = 1_tr,
286 bool saveImmediately = false) {
287 cancoder = std::make_unique<ctre::phoenix6::hardware::CANcoder>(deviceId, ctre::phoenix6::CANBus{canbus});
288 wpi::SendableRegistry::Remove(cancoder.get());
289
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);
296
297 config.Feedback.FeedbackRemoteSensorID = cancoder->GetDeviceID();
298 config.Feedback.FeedbackSensorSource = ctre::phoenix6::signals::FeedbackSensorSourceValue::FusedCANcoder;
299
300 cancoder->GetSimState().Orientation = clockwise ? ctre::phoenix6::sim::ChassisReference::Clockwise_Positive
301 : ctre::phoenix6::sim::ChassisReference::CounterClockwise_Positive;
302
303 util::PhoenixSignalManager::GetInstance().AddSignals(cancoder->GetNetwork(), cancoder->GetAbsolutePosition(),
304 cancoder->GetMagnetHealth(), cancoder->GetFault_BadMagnet());
305
306 if (saveImmediately)
307 GetConfigurator().Apply(config.Feedback);
308 }
309
310 ctre::phoenix6::hardware::CANcoder* getCANCoder() { return cancoder.get(); }
311
312 float getBusUtil(const char* canBusName) {
313 // @todo Initialize a CANBus, and get utilization
314 return 0;
315 }
316
317 void setOpenLoopRamp(units::second_t time, bool saveImmediately = false) override {
318 config.OpenLoopRamps.DutyCycleOpenLoopRampPeriod = time;
319 if (saveImmediately)
320 GetConfigurator().Apply(config.OpenLoopRamps);
321 }
322
323 units::volt_t calculateAppliedVoltage() override {
324 GetSimState().SetSupplyVoltage(frc::RobotController::GetBatteryVoltage());
325 if (cancoder)
326 cancoder->GetSimState().SetSupplyVoltage(frc::RobotController::GetBatteryVoltage());
327
328 // Return the motor's applied voltage
329 return GetSimState().GetMotorVoltage();
330 }
331
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);
336
337 /*
338 The + cancoderMagnetOffset is needed because the magnet offset is needed in
339 physical space to align motor and cancoder zero. SetRawPosition bypasses the
340 magnet offset, which means that the magnet offset is applied on top of it
341 and skews the reported position of the cancoder, which by proxy skews the
342 position of the motor that is using it as a feedback sensor.
343 */
344 if (cancoder) {
345 cancoder->GetSimState().SetRawPosition(sim.GetAngularPosition() * sensorToMech - cancoderConfig->MagnetOffset);
346 cancoder->GetSimState().SetVelocity(sim.GetAngularVelocity() * sensorToMech);
347 }
348 }
349
350 bool GetFault_BadMagnet(bool refresh = true) {
351 if (cancoder) {
352 auto& faultSignal = cancoder->GetFault_BadMagnet();
353 if (refresh) {
354 faultSignal.Refresh();
355 }
356 return faultSignal.GetValue();
357 }
358 return false;
359 }
360
361 void LoggablePeriodic() override {
362 WriteLog("Stator Current", GetStatorCurrent(false).GetValue());
363 WriteLog("Position", GetPosition(false).GetValue());
364 WriteLog("Speed", GetVelocity(false).GetValue());
365 WriteLog("Out Volt", GetMotorVoltage(false).GetValue());
366 if (cancoder)
367 WriteLog("Absolute Position", cancoder->GetAbsolutePosition(false).GetValue());
368 }
369
370 private:
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;
377
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;
384
385 template <class S>
386 void setPIDFSlot(S& slot, valor::PIDF pidf, bool saveImmediately) {
387 // Generic PIDF configurations
388 // Numerator for closed loop controls will be in volts
389 slot.kP = pidf.P;
390 slot.kI = pidf.I;
391 slot.kD = pidf.D;
392 slot.kS = pidf.S;
393 slot.kV = pidf.kV.value_or((voltageCompensation / getMaxMechSpeed()).value());
394
395 // Feedforward gain configuration
396 if (pidf.aFF != 0) {
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;
400 slot.kG = pidf.aFF;
401 }
402
403 if (saveImmediately)
404 GetConfigurator().Apply(slot);
405 }
406
407 // Helper function for enableFOC that only enables if the template has the field "EnableFOC"
408 template <typename T>
409 inline T tryEnableFOC(T request) {
410 if constexpr (requires { request.EnableFOC; })
411 request.EnableFOC = focEnabled;
412 return request;
413 }
414
415 inline PositionOutput createPositionRequest() {
416 if constexpr (requires { PositionOutput{0_tr}; })
417 return {0_tr};
418 else
419 return {0_tr, 0_tps, 0_tr_per_s_sq};
420 }
421
422 std::unique_ptr<PhoenixController<>> followerMotor;
423
424 VelocityOutput req_vel_out{0_tps};
425 PositionOutput req_pos_out = createPositionRequest();
426
427 std::unique_ptr<ctre::phoenix6::hardware::CANcoder> cancoder;
428 std::unique_ptr<ctre::phoenix6::configs::MagnetSensorConfigs> cancoderConfig;
429
430 ctre::phoenix6::configs::TalonFXConfiguration config;
431
432 bool focEnabled = true;
433};
434
435} // namespace valor
Abstract class that all Valor controllers's should implement.
Definition BaseController.h:53
virtual void setPositionWithFeedForward(units::turn_t position, units::turns_per_second_t feedforwardVelocity, int slot=0)
Send the motor to a specific position with a velocity feedforward.
Definition BaseController.h:174
units::turns_per_second_t getMaxMechSpeed()
Get the mechanisms's maximum speed.
Definition BaseController.h:93
T ReadLog(std::string_view field, const T &defaultValue={})
Read a value from NetworkTables for the given field.
Definition Loggable.h:343
T WriteLog(std::string_view field, const T &data)
Publish a value to NetworkTables under the given field.
Definition Loggable.h:313
void LogChild(std::string_view name, Loggable *child)
Register a child Loggable under a named subtree.
void setLoggingPeriod(units::millisecond_t period)
Set the minimum period between LoggablePeriodic() invocations.
Definition Loggable.h:251
Definition PhoenixController.h:32
void setPIDF(valor::PIDF pidf, int slot=0, bool saveImmediately=false) override
Change the PIDF values for the motor.
Definition PhoenixController.h:198
void LoggablePeriodic() override
Periodic callback for logging updates.
Definition PhoenixController.h:361
units::ampere_t getCurrent() override
Get the motors output current.
Definition PhoenixController.h:127
units::turns_per_second_t getSpeed() override
Get the motors speed.
Definition PhoenixController.h:125
void setReverseLimit(units::turn_t reverse, bool saveImmediately=false) override
Set the reverse soft limit for the motor.
Definition PhoenixController.h:254
units::turn_t getPosition() override
Get the motors position.
Definition PhoenixController.h:123
void reset() override
Resets the motor and any state.
Definition PhoenixController.h:86
void speedSetter(units::turns_per_second_t velocity, int slot=0) override
Send the motor to a specific speed.
Definition PhoenixController.h:170
void positionSetter(units::turn_t position, int slot=0) override
Send the motor to a specific position.
Definition PhoenixController.h:138
void setupFollower(int canID, bool followerInverted=false) override
If a motor is paired with another motor, setup that other motor as a follower.
Definition PhoenixController.h:188
void setForwardLimit(units::turn_t forward, bool saveImmediately=false) override
Set the forward soft limit for the motor.
Definition PhoenixController.h:246
void setGearRatios(double _rotorToSensor, double _sensorToMech, bool saveImmediately=false) override
Set the gear ratios for the motor.
Definition PhoenixController.h:262
void setPositionWithFeedForward(units::turn_t position, units::turns_per_second_t feedforwardVelocity, int slot=0) override
Send the motor to a specific position with a velocity feedforward.
Definition PhoenixController.h:143
Container to hold PID and feed forward values for the motor controller.
Definition PIDF.h:22
double I
Integral control of the feedback term.
Definition PIDF.h:42
units::turn_t error
Minimum error threshold.
Definition PIDF.h:54
double D
Derivative control of the feedback term.
Definition PIDF.h:44
double P
Proportion control of the feedback term.
Definition PIDF.h:40
units::turns_per_second_cubed_t maxJerk
Max jerk: revolutions per 1s^3.
Definition PIDF.h:52
units::turns_per_second_squared_t maxAcceleration
Max acceleration: revolutions per 1s^2.
Definition PIDF.h:50
units::turns_per_second_t maxVelocity
Max velocity: revolutions per 1s.
Definition PIDF.h:48