Foundations
Output families, gain slots, feedforward, profiles, and the general tuning workflow.
1.1 The Phoenix 6 Output Families
Every closed-loop request in Phoenix 6 belongs to one of three output families. The family sets the units of every gain you tune.
| Family | Example Requests | Output Units | When to Use |
|---|---|---|---|
| DutyCycle | VelocityDutyCycle, PositionDutyCycle, MotionMagicDutyCycle | Fraction of supply, [-1, 1] | Early bring-up only. Battery sag changes behavior. |
| Voltage | VelocityVoltage, PositionVoltage, MotionMagicVoltage | Volts | Default for nearly everything. Compensates for battery sag. |
| TorqueCurrent (FOC) | VelocityTorqueCurrentFOC, PositionTorqueCurrentFOC, MotionMagicTorqueCurrentFOC | Amps (torque-producing stator current) | Needs Phoenix Pro (Kraken X60/X44). Controls torque directly. |
Gains do not transfer between families. The units and the physics differ, so moving a mechanism from voltage to torque-current means re-tuning it from scratch.
1.2 The Gain Slot Model
Each motor has three gain slots (Slot0–Slot2), and the request picks which one to use. Every slot holds the same set of gains, which combine like this:
Where:
- is the controller output (Volts or Amps depending on family)
- is the tracking error
- , are the reference velocity and acceleration (from a profile, or passed in the request)
- is the gravity term: with
GravityType = Arm_Cosine, withElevator_Static(the default). Mechanisms without gravity load simply use kG = 0 - is the sign applied to kS. Where it comes from depends on the control mode and the
StaticFeedforwardSignconfig (see §1.2.1)
| Gain | Meaning | Unit (Voltage Mode) | Unit (Torque Mode) |
|---|---|---|---|
| kP | Output per unit error | V / (mechanism unit) | A / (mechanism unit) |
| kI | Output per unit accumulated error | V / (mechanism unit · s) | A / (mechanism unit · s) |
| kD | Output per unit error rate | V / (mechanism unit / s) | A / (mechanism unit / s) |
| kS | Static friction compensation | V | A |
| kV | Velocity feedforward | V / (rotation/s) | A / (rotation/s) — typically 0 |
| kA | Acceleration feedforward | V / (rotation/s²) | A / (rotation/s²), set by inertia (see §3.0.3) |
| kG | Gravity feedforward | V | A |
1.2.1 The StaticFeedforwardSign Config
kS pushes against static friction, so its sign has to match the direction the mechanism is trying to move. The StaticFeedforwardSign field in each gain slot (Slot0Configs.StaticFeedforwardSign, etc.) picks where that sign comes from:
| Value | comes from | Use it for |
|---|---|---|
UseVelocitySign (default) | , the sign of the velocity reference | Motion Magic, and any position request where you pass a velocity. |
UseClosedLoopSign | , the sign of the position error | Plain position requests (PositionVoltage, PositionTorqueCurrentFOC) with no velocity. |
The setting only affects position control. Velocity modes (VelocityVoltage, VelocityTorqueCurrentFOC) always use the sign of the velocity reference.
Why it matters. A plain position request has . With the default UseVelocitySign, that makes , so kS is never applied. kP alone then has to overcome static friction, which only happens once the error is large enough, leaving a small steady-state offset. UseClosedLoopSign applies kS in the direction of the error and removes the offset.
The cost is chatter. Near the setpoint, noise pushes the error back and forth across zero, and kS flips sign every loop. That is audible and heats the motor. Keep kS just large enough to break stiction when you use UseClosedLoopSign.
Which setting for which request:
| Request | Setting |
|---|---|
VelocityVoltage, VelocityTorqueCurrentFOC | Ignored. Always . |
Any MotionMagic* position request | UseVelocitySign (default). The profile supplies , so kS acts during motion and drops to zero once the mechanism stops. |
PositionVoltage / PositionTorqueCurrentFOC, no Velocity argument | UseClosedLoopSign. Otherwise kS never helps with small corrections. |
PositionVoltage / PositionTorqueCurrentFOC with a Velocity argument (your own motion profile) | UseVelocitySign (default), since you are supplying a real velocity reference. |
The mechanism procedures below refer back to this table.
1.3 Feedforward First
Feedforwards do the heavy lifting. PID corrects what's left.
If kS, kV, kA, and kG are right, kP only has to clean up disturbances and modeling error. Raising kP to make up for missing feedforward gets you oscillation, overshoot, and a mechanism that misbehaves whenever something changes (a stretched chain, a fresh battery, a heavier game piece). Put the physics in the feedforward, then let feedback handle what's left.
1.4 Profiled vs. Simple Control
Position control comes in three flavors:
Simple PID — PositionVoltage / PositionTorqueCurrentFOC. The setpoint is a step, and the controller drives the error to zero as fast as the gains allow. Nothing shapes the path in between, so a large step asks for near-infinite acceleration: current spikes, mechanical stress, and overshoot.
Motion Magic (trapezoidal) — MotionMagicVoltage / MotionMagicTorqueCurrentFOC. Builds a trapezoidal velocity profile from a configured cruise velocity (), acceleration (), and optional jerk (, which rounds the corners into an S-curve). The PID chases a moving target it can actually keep up with.
Each loop, the profile hands the controller a position, velocity, and acceleration reference:
With good feedforward, the mechanism follows these closely.
Motion Magic Expo — MotionMagicExpoVoltage / MotionMagicExpoTorqueCurrentFOC. Builds an exponential profile from a model of the motor instead of from limits you pick. See §1.4.1.
When to use which:
- Simple PID — short moves where the path doesn't matter, or early bring-up before the mechanism is characterized.
- Motion Magic (trapezoidal) — when you need to enforce a hard limit (e.g., "this arm must never accelerate faster than X or the robot tips"), or want the same profile shape every time.
- Motion Magic Expo — the default choice for arms, elevators, and turrets. The profile stays within what the motor can deliver, so it is always achievable.
1.5 General Tuning Workflow
- Set units. Set
SensorToMechanismRatioinFeedbackConfigsso positions are in mechanism rotations (the arm, turret, or drum turns once per unit) rather than rotor rotations. Every gain below is per mechanism unit. - Set
StaticFeedforwardSignfor the request you'll use (§1.2.1): the defaultUseVelocitySignfor velocity and Motion Magic,UseClosedLoopSignfor plainPositionVoltage/PositionTorqueCurrentFOC. - Characterize. Get kS, kV, kA, and (for arms and elevators) kG in your output family's units. For voltage mode, SysId is the standard tool. For torque-current mode, compute kG and kA from physics and measure kS by hand (§3.0.4 explains why).
- Feedforward only. Enter the feedforwards with all PID gains at zero. The mechanism should roughly follow slow setpoints.
- Add kP until disturbances are rejected well, stopping short of sustained oscillation.
- Add kD if position moves oscillate. Start around kP/100 in voltage mode and kP/30 to kP/10 in torque mode, which lacks back-EMF damping (§3.0.3).
- Add kI only as a last resort, for steady-state error the model doesn't explain.
- Tune the profile last (cruise velocity and acceleration, or Expo_kV and Expo_kA), once PID plus feedforward handles arbitrary setpoints.