All pages
Powered by GitBook
1 of 10

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Closed Loop Control

Closed-Loop Control Basics

A Closed-Loop Control System in its most basic form is a process that uses feedback to improve the accuracy of its outputs. Closed-Loop Control Systems, sometimes referred to as Feedback Controllers, are frequently used when maintaining or reaching a steady output is important or if the system may have outside influences that could affect the system's output.

A simple example using this type of Control is an automatic coffee maker. In its Closed-Loop Control System, the output is hot coffee and the process we are getting feedback on is the heating of the water. If the coffee maker receives feedback that the water is cold, it will start to heat the pot. When the water is almost hot enough to brew the coffee, the control algorithm will continue to heat the water until the correct goal temperature has been reached. Once the water reaches it's goal temperature, or if it gets too hot, the system will stop heating the water and wait until it receives feedback that the heater needs to begin again.

Closed-Loop Control with SPARK Motor Controllers

Closed-Loop Control is a staple of complex FRC mechanism programming. WPILib offers several libraries to allow teams to run PID loops on the roboRIO, but they require manual setup in your team's code, need additional configuration to run at high frequencies, and may require specifically-configured feedback devices for fast responses.

With a PID loop onboard a SPARK Motor Controller, the setup is simple, doesn't clutter your code, and the loop is updated every 1ms, increasing the responsiveness and precision of the controller. Even when using a more complex control algorithm on the roboRIO, it's still recommended to put as much processing on the motor controller as possible. The PID controller onboard the SPARK can also be configured and tuned with the REV Hardware Client, allowing for a much faster tuning process that doesn't rely on your other subsystems.

Configuring SPARK PID with REVLib can be done in a couple of lines and fits right into the configuration of the motor controller.

SparkFlexConfig config = new SparkFlexConfig()
    .closedLoop.pid(0.01, 0, 0.001);
spark.configure(config, ResetMode.kNoResetSafeParameters, PersistMode.kNoPersistParameters);

Setting a setpoint for the PID is just as easy, whether you want to set a position or velocity or even use a motion profile.

SparkClosedLoopController closedLoopController = spark.getClosedLoopController();
closedLoopController.setSetpoint(10, ControlType.kVelocity); // 10 RPM

Both the SPARK MAX and SPARK Flex can operate in several closed-loop control modes, using sensor input to tightly control the motor velocity, position, or current. The internal control loop follows a standard PID algorithm and incorporates several feedforward terms to account for known system dynamics. This allows the motor to follow precise and repeatable motions, useful for complex mechanisms.

Additionally, an arbitrary feedforward signal is added to the output of the control loop after all calculations are done. The units for this signal can be selected as either voltage or duty cycle. This feature allows more advanced feedforward calculations to be performed by the controller. This can be useful for systems with more complex dynamics than can be represented by the SPARK feedforward.


Current Control Mode

Current Control uses a PID controller to run the motor at a consistent current, providing a consistent torque. The PID controller is run using the setpoint, in Amps, and the internally measured current draw.

It is called as shown below:

API Docs:

API Reference:

Current Control mode will turn your mechanism continuously and will speed up to maximum velocity if unloaded. Use caution when running this mode and avoid using it on mechanisms with a limited range of motion.

setSetpoint
SetSetpoint
m_controller.setSetpoint(setPoint, ControlType.kCurrent);
using namespace rev::spark;

m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kCurrent);

Closed Loop Control Getting Started

Setting up Closed-Loop Control

Closed-loop control in REVLib is accessed through the SPARK's closed loop controller object. This object is specific to each motor and contains all the methods needed to control your motor with closed-loop control. It can be accessed as shown below:

// Initialize the motor (Flex/MAX are setup the same way)
SparkFlex m_motor = 

API Docs: SparkFlex, SparkClosedLoopController

using namespace rev::spark;

API Docs: SparkMax, SparkClosedLoopController

To drive your motor in a closed-loop control mode, address the closed loop controller object and give it a set point (a target in whatever units are required by your control mode: position, velocity, or current) and a control mode as shown below:

API Docs: ,

API Docs: ,

The provided example above runs the motor in position control mode, which is just a conventional PID loop reading the motor's current position from the configured encoder and taking a setpoint in rotations.

To run a PID loop, several constants are required. More advanced controllers require additional parameters to be set and tuned.

A PID controller has 3 core parameters or gains. For more information on these gains and how to tune them, see .

These gains can be configured on the with the closedLoop member of a SparkFlexConfigor SparkMaxConfig object as seen below:

API Docs:

API Docs:

There are several Feedforward parameters that can be used to model your system and help support the PID controller, resulting in more precise and consistent motions. These are explained on the .

API Docs:

API Docs:

MAXMotion has parameters that allow you to configure and tune the motion profiles generated by MAXMotion. The parameters can be set through the maxMotion member of the closedLoop config.

API Docs:

API Docs:

The SPARK MAX and SPARK Flex each have 4 closed-loop slots, each with their own set of constants. These slots are numbered 0-3. You can pass the desired as an argument to each of the applicable configurations.

API Docs:

API Docs:

When applying the setpoint, pass the slot number and the motor controller will switch to the appropriate config.

API Docs: ,

API Docs: ,

Getting Started with PID Tuning

For a detailed technical and mathematical description of each term and its effect, the WPILib docs page on PID is a good resource.

FRC Usage

In FRC, PID loops are used in many types of mechanisms, from flywheel shooters to vertical arms. These need to be tuned to different constants, depending on the units they use and the physical design of the mechanism, however the process to find these constants is roughly the same.

Most teams find success using controllers tuned primarily with P and D, using a Feedforward to account for steady-state error.

P, the proportional gain, is the primary factor of the control loop. This is multiplied by the error and that gain is added to the output. This does the heavy lifting of the motion, pushing the motor in the direction it needs to go.

I, the integral gain, is not often recommended in FRC. It is useful for eliminating steady-state error, or error that the other gains leave behind and cannot address. It accumulates the error over time and multiplies it by the I gain, gradually increasing the power it supplies until that has evened out. If it is needed, it's recommended to use a limited to prevent I windup. For FRC purposes, Feedforward gains are recommended to eliminate steady-state error instead.

The derivative gain, D, is used to tune out oscillation and dampen the motion. It resists motion, decreasing power when the mechanism is moving. A good balance of P and D is needed to make a smooth motion with no oscillation.

Several guides for PID tuning are available, such as . It may be useful to consult multiple, especially those available that reference your specific mechanism.

Any method for PID tuning will start with the same concept, however, regardless of mechanism. Before you can tune your mechanism, you should setup a graph of the setpoint and that measured value, either through the or a similar utility. This will allow you to analyze each test and properly evaluate the changes to make.

To then tune a basic PID loop, follow the steps below:

  1. Set all constants (P, I, D, etc) to 0

  2. Ensure the mechanism is safe to actuate. This process will spin the motor, potentially at unexpected speeds and in unexpected directions

  3. Check the direction of the motor, and invert it if needed so that positive output is in the desired direction

Velocity Control Mode

Velocity Control uses the PID controller to run the motor at a set speed in RPM (or configured conversion factor units).

Want to control the acceleration of your velocity controller? See for an improved version of Velocity Control with more features and control.

It is called in the same way as Position Control:

m_controller.setSetpoint(setPoint, ControlType.

API Docs: setSetpoint

using namespace rev::spark;

API Reference: SetSetpoint

Velocity Control mode will turn your motor continuously. Be sure your mechanism does not have any hard limits for rotation.

Velocity Loop constants are often of a very low magnitude, so if your mechanism isn't behaving as expected, try decreasing your gains.

MAXMotion Velocity Control

MAXMotion Velocity Control utilizes the MAXMotion parameters to improve upon velocity control. Honoring the maximum acceleration, MAXMotion Velocity Control will speed up your flywheel or rotary mechanism in a controlled way, reducing power draw and increasing consistency.

MAXMotion Velocity Control utilizes an internal velocity closed-loop controller, so transitioning from Velocity Control mode to MAXMotion Velocity Control is as simple as setting a maximum acceleration and changing the setSetpoint call.

It is called as seen below:

m_controller.setSetpoint(setPoint, ControlType.

API Docs: setSetpoint

using namespace rev::spark;

API Reference: SetSetpoint

MAXMotion Velocity Control will turn your motor continuously. Be sure your mechanism does not have any hard limits for rotation.

Tips for Smooth Motions

  • The Static, Velocity, and Acceleration constants are super helpful in making your motion smooth and consistent. You should be able to get decent performance with only kV/kA and no PID at all

  • If your motion seems jittery, try reducing your PID constants, especially P. If the underlying velocity PID outruns the acceleration target, the motion may seem jittery and the velocity will not increase smoothly.

  • Make sure your units are correct: maximum velocity is set in RPM by default and maximum acceleration is set in RPM per second by default.

  • At low speeds, the acceleration may seem wobbly or inconsistent if the loop has been tuned for higher speeds or vice versa. If both are needed, try tuning separate PIDs and switching between slots when needed. This may be easier than finding those perfect constants that work beautifully across the board.

Position Control Mode

Position Control is used to point the motor in a specific direction. It takes a setpoint in rotations (or whatever unit your selected encoder's position conversion factor is in) and uses the PID loop to move to that position. The Position control mode pipes directly into a PID controller with the configured encoder.

A properly tuned Position control loop should respond quickly and accurately to a setpoint change and should not oscillate around the target.

To run the motor in Position control mode, set the PID Controller setpoint as shown below.

API Docs:

API Reference:

Setup and tune any relevant feedforwards
  • Set P to a very small number, relative to the units you are working in

  • Set a target for the motor to move to. Ensure this is within the range of your mechanism.

  • Gradually increase P until you see movement, by small increments

  • Once you see motion, increase P by small increments until it reaches the target at the desired speed

  • If you see oscillation, decrease P or begin to increment D by a small amount. A precisely tuned P gain is better than a D gain, but a D gain may be needed to counteract the dynamics of the system

  • Continue to adjust these parameters until the motion is quick, precise, and repeatable

  • The Constants

    P - Proportional Gain

    I - Integral Gain

    D - Derivative Gain

    Tuning

    this technical one on the WPILib docs
    REV Hardware Client
    kMAXMotionVelocityControl
    );
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kMAXMotionVelocityControl);
    feed forward
    new
    SparkFlex
    (
    deviceID
    ,
    MotorType
    .
    kBrushless
    );
    // Initialize the closed loop controller
    SparkClosedLoopController m_controller = m_motor.getClosedLoopController();
    // Initialize the motor (Flex/MAX are setup the same way)
    SparkMax m_motor{deviceID, SparkMax::MotorType::kBrushless};
    // Initialize the closed loop controller
    SparkClosedLoopController m_controller = m_motor.GetClosedLoopController();

    This will run your motor in the provided mode, but it won't move until you've configured the PID constants.

    Use caution when running motors in closed-loop modes, as they may move very quickly and unexpectedly if improperly tuned.

    PID Constants and Configuration

    To read more about configuration, see this page on general configuration. For more information about SPARK specific configuration, see this page.

    PID Parameters

    Feedforward Parameters

    MAXMotion Parameters

    The MAXMotion Cruise Velocity parameter only applies to MAXMotion Position Control Mode, while MAXMotion Velocity Control Mode does not honor it in order to ensure any setpoint is reachable. This means any top-speed clamping you want to do must be done before you send the setpoint to the Motor Controller.

    Cruise Velocity is in units of Revolutions per Minute (RPM) by default

    Maximum Acceleration is in units of RPM per Second (RPM/s) by default

    Slots

    setSetpoint
    ControlType
    SetSetpoint
    ControlType
    Getting Started with PID Tuning
    ClosedLoopConfig
    ClosedLoopConfig
    Feed Forward Control page
    ClosedLoopConfig
    ClosedLoopConfig
    MAXMotionConfig
    MAXMotionConfig
    ClosedLoopConfig
    ClosedLoopConfig
    setSetpoint
    ControlType
    SetSetpoint
    ControlType
    // Set the setpoint of the PID controller in raw position mode
    m_controller.setSetpoint(setPoint, ControlType.kPosition);
    // Set the setpoint of the PID controller in raw position mode
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kPosition);
    SparkFlexConfig config = new SparkFlexConfig();
    
    // Set PID gains
    config.closedLoop
        .p(kP)
        .i(kI)
        .d(kD)
        .outputRange(kMinOutput, kMaxOutput);
    using namespace rev::spark;
    
    SparkFlexConfig config;
    
    // Set PID gains
    config.closedLoop
        .P(kP)
        .I(kI)
        .D(kD)
        .OutputRange(kMinOutput, kMaxOutput);
    SparkFlexConfig config = new SparkFlexConfig();
    
    // Set PID gains
    config.closedLoop.feedForward
        .kS(s)
        .kV(v)
        .kA(a)
        .kG(g) // kG is a linear gravity feedforward, for an elevator
        .kCos(g) // kCos is a cosine gravity feedforward, for an arm
        .kCosRatio(cosRatio); // kCosRatio relates the encoder position to absolute position
    using namespace rev::spark;
    
    SparkFlexConfig config;
    
    // Set PID gains
    config.closedLoop.feedForward
        .kS(s)
        .kV(v)
        .kA(a)
        .kG(g) // kG is a linear gravity feedforward, for an elevator
        .kCos(g) // kCos is a cosine gravity feedforward, for an arm
        .kCosRatio(cosRatio); // kCosRatio relates the encoder position to absolute position
    SparkMaxConfig config = new SparkMaxConfig();
    
    // Set MAXMotion parameters
    config.closedloop.maxMotion
        .cruiseVelocity(cruiseVel)
        .maxAcceleration(maxAccel)
        .allowedProfileError(allowedErr);
    using namespace rev::spark;
    
    SparkMaxConfig config;
    
    // Set MAXMotion parameters
    config.closedloop.maxMotion
        .CruiseVelocity(cruiseVel)
        .MaxAcceleration(maxAccel)
        .AllowedProfileError(allowedErr);
    SparkFlexConfig config = new SparkFlexConfig();
    
    config.closedLoop
        // Set PID gains for position control in slot 0.
        // We don't have to pass a slot number since the default is slot 0.
        .p(kP)
        .i(kI)
        .d(kD)
        .outputRange(kMinOutput, kMaxOutput)
        // Set PID gains for velocity control in slot 1
        .p(kP1, ClosedLoopSlot.kSlot1)
        .i(kI1, ClosedLoopSlot.kSlot1)
        .p(kD1, ClosedLoopSlot.kSlot1);
    using namespace rev::spark;
    
    SparkFlexConfig config;
    
    config.closedLoop
        // Set PID gains for position control in slot 0.
        // We don't have to pass a slot number since the default is slot 0.
        .P(kP)
        .I(kI)
        .D(kD)
        .OutputRange(kMinOutput, kMaxOutput)
        // Set PID gains for velocity control in slot 1
        .P(kP1, ClosedLoopSlot::kSlot1)
        .I(kI1, ClosedLoopSlot::kSlot1)
        .D(kD1, ClosedLoopSlot::kSlot1);
    // Use the PID gains in slot 0 for position control
    m_controller.setSetpoint(setPoint, ControlType.kPosition, ClosedLoopSlot.kSlot0);
    
    // Use the PID gains in slot 1 for velocity control
    m_controller.setSetpoint(setPoint, ControlType.kVelocity, ClosedLoopSlot.kSlot1);
    using namespace rev::spark;
    
    // Use the PID gains in slot 0 for position control
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kPosition, ClosedLoopSlot::kSlot0);
    
    // Use the PID gains in slot 1 for velocity control
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kVelocity, ClosedLoopSlot::kSlot1);
    kVelocity
    );
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kVelocity);
    MAXMotion Velocity Control

    For more complex mechanisms or motions where closer control over acceleration and velocity are needed, see MAXMotion Position Control

    setSetpoint
    SetSetpoint
    This loop was tuned to show the curve, an ideal controller would move much quicker. In a perfect world, this motion would be nearly instantaneous.
    m_controller.setSetpoint(setPoint, ControlType.kPosition);
    using namespace rev::spark;
    
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kPosition);

    Units

    Default Units

    Quantity
    Default Units
    Affected by

    Setpoint

    Rotations

    Position Conversion Factor

    Encoder Position

    There are two configurable Conversion Factors on each Encoder type that can be used to account for gear ratios and unit conversions in the motor control logic. These are applied independently and the velocity factor does not rely on the position factor, so different units can be used for each.

    Positions read from the feedback encoder are multiplied by the Position Conversion Factor before being processed by the closed-loop controller.

    Description
    Factor

    Velocities read from the feedback encoder are multiplied by the Velocity Conversion Factor before being processed by the closed-loop controller.

    The velocity conversion factor is completely independent of the position conversion factor, so both need to be set to change both units.

    All accelerations on the SPARK controllers are in terms of velocity per second, where the velocity is in units specified by the Velocity Conversion Factor.

    Description
    Factor

    10:1 Gearbox, Rotations at output

    1/10 (0.1)

    Distance in inches traveled with a 6in diameter wheel

    6π (18.8495559215)

    Degrees per Second

    360/60 (6)

    Radians per Minute

    2π (6.28318530718)

    Radians per Second

    2π/60 (0.10471975512)

    Rotations

    Position Conversion Factor

    Encoder Velocity

    RPM

    Velocity Conversion Factor

    Applied Output

    Duty Cycle

    kP

    Duty cycle per rotation

    Position Conversion Factor

    kI

    Duty cycle per (rotation*ms)

    Position Conversion Factor

    kD

    (Duty cycle*ms) per rotation

    Position Conversion Factor

    kS

    Volts

    kV

    Volts per RPM

    Velocity Conversion Factor

    kA

    Volts per RPM/s

    Velocity Conversion Factor

    kG

    Volts

    kCos

    Volts per Rotation

    MAXMotion Cruise Velocity

    RPM

    Velocity Conversion Factor

    MAXMotion Maximum Acceleration

    RPM/s

    Velocity Conversion Factor

    MAXMotion Allowed Profile Error

    Rotations

    Position Conversion Factor

    Default (Revolutions)

    1

    Degrees

    360

    Radians

    2π (6.28318530718)

    Default (RPM)

    1

    Revolutions per Second

    1/60 (0.01666666666)

    Degrees per Minute

    360

    Conversion Factors

    Position Conversion Factor

    Common Position Conversion Factors

    Velocity Conversion Factor

    Common Velocity Conversion Factors

    MAXMotion Position Control

    MAXMotion Position Control is a second-degree closed loop controller, allowing for smooth and consistent motions from one position to another by limiting both the velocity and acceleration of the motor. These can be configured via the MAXMotion Parameters, setting a target acceleration and a "cruise" velocity. The motor will spin up, honoring the acceleration target, hold speed at the cruise velocity, and then slow down honoring the acceleration target to arrive at the setpoint. MAXMotion updates its motion profile every 10ms and the underlying PID controller every 1ms, which makes it extremely fast and responsive.

    How it Works

    MAXMotion generates a profile containing all the key transition points between the current position and the setpoint and uses that to calculate intermediate positions for the PID controller to follow.

    Each point along the profile is a target for the PID controller at the point in time it corresponds to. If, at some point in time, the actual measured position is more than the configured Allowed Profile Error away from the profile, the profile will be regenerated from the current position and velocity. While the mechanism is within that margin, it will continue to track the same profile. This makes tuning easy and makes motions consistent and accurate.

    Configuring MAXMotion

    Feedforwards

    The SPARK Feedforward system was designed with MAXMotion in mind, and MAXMotion can take advantage of all of its features.

    The first step of setting up MAXMotion is to configure the PID feedforwards, as explained on . The kV and kA values from a calculator, converted to appropriate units, or from a tool like SysID are perfect starting points for tuning.

    There are 3 primary constants to configure for MAXMotion:

    • Cruise Velocity: this is the speed you want the motion to hold through the middle of its path

    • Maximum Acceleration: this is the acceleration you want to use to speed up and slow down the motion

    • Allowed Profile Error: this is the amount of position deviation from the profile that is allowed before the profile is regenerated

    Constant
    Associated behavior
    1. Ensure the mechanism is free to move and note any mechanical limits

    2. Set up the for the mechanism. Note that these may not provide expected results until other values are setup

    3. Set P to a very small number, relative to your position units. For the default units, kP = 0.01 is a good starting point. Keep in mind that kP will be multiplied by your position error and then become duty cycle percent output, so pick a "small" value relative to what your position error is expected to be

    A well-tuned MAXMotion controller will:

    • Track position, velocity, and acceleration closely and accurately

    • Respond quickly to a change in setpoint

    • Not stutter or reset

    • Move smoothly and in a controllable way

    After tuning your constants, calling MAXMotion is as simple as passing in the setpoint to the controller.

    API Docs:

    API Reference:

    Smart Motion used a different method for smooth second-degree motion control, but MAXMotion can be applied anywhere Smart Motion was previously. Maximum velocity and acceleration constants may be transferable, but should be tested with caution. All other constants will need re-tuned from scratch, including all PIDs.

    MAXMotion has several improvements over Smart Motion, and should offer better consistency, a better tuning experience, better position retention, and an all-around better user experience. It is highly recommended to migrate all systems using Smart Motion to MAXMotion.

    kP

    This is the position-tracking gain. This represents how much voltage is applied proportionally to the position error. Increasing it will make the mechanism move toward the position target more quickly, but increasing it too much will cause overshooting and stuttering.

    kI

    This is the integral gain, which is not often recommended for FRC use.

    kD

    This is the derivative gain, which helps track velocity within the position controller. For better velocity tracking, kV is a better choice.

    kS

    This is the static gain, which helps overcome a constant resistance like friction in a gearbox. It should be set to the maximum voltage in either direction that doesn't make the mechanism move at all, where any more causes motion. Increasing it will improve precision and make motions in different directions more consistent, but increasing it too much will cause jitter.

    kV

    This is the velocity-tracking gain. Increasing it will increase the voltage output proportionally to the velocity target, and will help track velocity more closely. Increasing it too much will cause overshooting on velocity or general instability.

    kA

    This is the acceleration-tracking gain. Increasing it will help track acceleration more closely, but increasing it too much will cause instability. It will make a noticeable difference in velocity tracking during acceleration and deceleration.

    kG and kCos

    These are gravity feedforwards, that help hold position against gravity and remove the gravity factor from the position and velocity tracking of the other constants. For more information on these gains, see

    Set the Cruise Velocity and Max Acceleration to small numbers, relative to your velocity units and gear ratio. For a directly-driven mechanism with default units, 30 RPM and 10 RPM/s respectively are good starting points to see the effects of MAXMotion and clearly see the impacts of each parameter, but these are very slow and will need increased

  • Set the Allowed Profile Error to a high number relative to your units and the distance to your setpoint. For most motions at the default units, an Allowed Profile Error of 1 Rotation is enough to get started. If the results are confusing, especially if the acceleration targets appear to be too low, increase this. Increasing this value will let the motion continue for a longer period before regenerating the profile, which may uncover the root of a tuning issue

  • Set up a method to retrieve relevant info

    1. If running a robot program, use NetworkTables to post this information

      1. Several of these values can be fetched from the SparkClosedLoopController object

      2. A dashboard like Glass or AdvantageScope can help graph and record these values

    2. In the REV Hardware Client, use the Telemetry tab to enable this information

    3. Good information to watch while tuning:

      1. Position

      2. Velocity

      3. Applied Output

    4. Note that fetching all these values may require modifying the Status Frame Periods for certain parameters as the CAN bus or the SPARK device reaches its limit. If the device stops responding and many Status Frames are timing out, power cycle all devices on the bus to clear the errors and try reducing traffic by increasing status frame periods

    5. Graphing all the positions and all the velocities on 2 graphs will help this process

  • Tuning for your mechanism using Simulation is a safe, good way to start, but will probably still need further tweaking to make the actual mechanism's motion perfect

  • Be very careful, as this tuning process could cause your mechanism to move in unpredictable or unexpected ways if the constants are off, particularly if your units don't match up

  • Repeat the following process until the results are satisfactory

    1. Run and record a motion to a known setpoint

    2. If the motion is jittery or shaky, reduce kP. If your Allowed Profile Error is small, increase it to a large number while diagnosing issues

    3. If the motion shoots to a high, uncontrollable velocity immediately, increase P to a larger number. With too small of a P value, the feedforwards are sensitive to tiny inaccuracies, but by increasing P it will increase this tolerance. If this persists, reduce your feedforward values, specifically kV

    4. If the MAXMotion Position Setpoint jumps or spikes and resets more than a few times, increase the Allowed Profile Error. This won't contribute to fixing the issue, but will let you better observe the behavior and identify other factors.

    5. If the motion is asymmetrical up vs down, recalculate kG and kS or try determining them experimentally

    6. If the velocity lags behind the target velocity, increase kV

    7. If the velocity overshoots the target velocity, decrease kV

    8. If the position lags behind the target, increase kP slightly

    9. If the mechanism overshoots the setpoint, reduce kV (or, if that doesn't fix it, kS)

    10. If the motion is stable, smooth, and consistent, the the position and velocity targets are reached consistently, the mechanism doesn't overshoot the setpoint, and the MAXMotion Position Setpoint doesn't seem to "jump", reduce the Allowed Profile Error, increase the Cruise Velocity, or increase the Max Acceleration slightly

    11. Repeat this process until you have the speed, smoothness, and accuracy that you want

  • Move quickly

  • Not overshoot the setpoint

  • The acceleration will be set as high as is smooth without hitting the current limit

  • The velocity will be set as high as is achievable and smooth

  • The allowed profile error will be as small as possible

  • Cruise Velocity

    This is the top of the trapezoid, the velocity that is sustained through the center stage of the motion. Increasing it beyond what is achievable will result in a triangular "trapezoid" on you Velocity graph.

    Maximum Acceleration

    This is how quickly the mechanism accelerates. Increasing it too much will draw a lot of current, and may hit the current limits or stall the motors.

    Allowed Profile Error

    This is how "loose" the profile is, and how far your mechanism can get from the profile before the profile is regenerated. For tuning, it's helpful to set this to a large value so you can see the behavior without the profile resetting, but the end goal for your motion should be to minimize this margin.

    m_controller.setSetpoint(setPoint, SparkBase.ControlType.kMAXMotionPositionControl);
    using namespace rev::spark;
    
    m_controller.SetSetpoint(setPoint, SparkBase::ControlType::kMAXMotionPositionControl);

    MAXMotion Constants

    If changes to quantities aren't showing the expected results, the Current Limits may be engaging. This will limit the acceleration of the system and can be remedied by increasing the Current Limit (within reason) or increasing the Gear Ratio.

    What do the constants do?

    Tuning for MAXMotion Position Control

    What does "good tuning" look like?

    Using MAXMotion

    Migrating from Smart Motion

    As Smart Motion and MAXMotion Position Control use different underlying control methods, all PID constants will need to be re-tuned from scratch.

    the feedforward page
    Feedforwards
    setSetpoint
    SetSetpoint

    MAXMotion Position Setpoint

  • MAXMotion Velocity Setpoint

  • Setpoint

  • Current Draw

  • Feed Forward Control

    Feed Forward Control

    Closed loop PID control and MAXMotion motion profiled control are excellent tools for precisely and reactively controlling mechanisms on your robot, but the effectiveness of these tools can be increased further with the introduction of Feed Forward terms. A feed forward (or feedforward) controller is an additional calculation that helps factor system dynamics like gravity and resistance into your closed loop movements, which can be especially helpful on heavy systems.

    WPILib offers classes for Feedforward control that behave similarly to the SPARK motor controllers internal calculations and also have an explanation of the math behind DC motor feedforward control. The SPARK feed forward system is designed to drop-in to many of the use cases of these utilities, so much of the information on them is transferable, though you may need to watch your units.

    The SPARK feed forward system has the added benefits of directly integrating with MAXMotion, being able to use high feedback frequencies without increased CAN bus traffic or additional configuration, being easy to setup and use, and conserving processing resources on your robot controller.

    Feed Forward Constant Quick Reference

    For more information on these terms, see their descriptions below

    Term
    Units
    Usage Notes

    The SPARK Feed Forward system includes 5 terms and one additional constant, each of which apply to some control modes but not others. The compatibility of these is listed in the chart below:

    Term
    MAXMotion Position Control Mode
    MAXMotion Velocity Control Mode
    Position Control Mode
    Velocity Control Mode

    Each term can be set per closed loop slot in the config, as seen below.

    API Docs:

    API Docs:

    The Static Gain is used to counteract any resistance in your motor or mechanism, and is applied in the direction of desired velocity.

    To find this value, find the smallest output that causes the mechanism to move slightly, then decrease it slightly so that it doesn't move on it's own, but has no resistance in that direction. See for how to experimentally find this value for an elevator or for the equivalent on an arm. Note that kS is input in Volts.

    This should allow the motor/mechanism to move as soon as any other output is applied, eliminating any "dead zone" of output because of resistance. This can be measured using SysID.

    The Velocity Gain is used to help your motor and mechanism maintain the desired velocity, and is multiplied by the velocity setpoint. The units are Volts per velocity as measured by the feedback sensor, after the conversion factor. By default, the units are Volts per RPM, prior to any gear ratio.

    Many calculators will estimate this in terms of the mechanism's movement, so be sure to account for gear ratios or velocity unit conversions. This can be estimated with a tool like (note the units) or measured with SysID.

    The Acceleration Gain is used to accelerate your motor to the desired acceleration, and is multiplied by the acceleration setpoint. The units are Volts per velocity unit per second, with the same caveats on the velocity units as the velocity gain. By default, the units are Volts per RPM per second, prior to any gear ratio.

    This can be estimated with a tool like (note the units) or measured with SysID.

    The Static Gravity Gain, for elevators and mass moving straight up and down, is simply added to the output and serves to hold the mechanism's position against gravity. The units are Volts.

    kG can be can be estimated with a tool like , or it can be measured with SysID.

    As kG and kCos are both different types of gravity feedforward gains, they shouldn't be used together. If your mechanism is an elevator, use kG. If your mechanism is an arm, use kCos.

    The Cosine Gravity Gain, for arms and mechanisms that fight gravity in a rotary way, is the most complicated but also the most useful of the feed forward gains. It is multiplied by the cosine of the absolute position of your mechanism, which means it pushes the most when the mechanism is horizontal and the least when it's vertical.

    This can be easily accomplished by setting up an absolute encoder or limit switch to reset the position of the arm and then using an initialization or homing sequence to zero the position correctly. Once the zero position is set, make sure to also set up the kCosRatio constant to ensure the calculations are done correctly.

    The Units are Volts and kCosRatio needs to be set to convert position to absolute mechanism rotations.

    This gain can be estimated with a tool like or measured with SysID, but is referred to as kG in these systems and may need unit conversions.

    As kCos and kG are both different types of gravity feedforward gains, they shouldn't be used together. If your mechanism is an arm, use kCos. If your mechanism is an elevator, use kG.

    Once your arm is zeroed correctly as explained above in the kCos section, the kCosRatio also needs to be configured so that your mechanism's absolute position can be calculated correctly. This ratio should convert from the units of your setpoint (selected feedback sensor's conversion factor) to absolute rotations of your mechanism, and is multiplied by the selected sensor's read position (in units set by your position conversion factor).

    This must convert your motor's selected feedback sensor's position into Rotations of the mechanism for the calculation to work.

    If your conversion factor is 1 (default), this should simply be any gear reduction between your motor and the actual motion of the arm. If your conversion factor is set, it'll need to be factored into this ratio to properly determine the absolute position of your arm.

    For more complex feedforward models, there is also a means of applying an arbitrary voltage which can be calculated in your team code and passed to the API.

    It can be applied with the setpoint as seen below:

    API Docs: ,

    API Docs: ,

    Elevator/linear mechanism gravity feedforward

    kCos

    Volts

    Arm/rotary mechanism gravity feedforward.

    Feedback sensor must be configured to 0 = horizontal

    kCosRatio

    Ratio

    Converts feedback sensor readings to mechanism rotations

    kV

    kA

    kG*

    kCos*

    kCosRatio

    kS

    Volts

    kV

    Volts per velocity

    Volts per motor RPM by default

    kA

    Volts per velocity/s

    Volts per motor RPM/s by default

    kG

    kS

    Feed Forward Constant Terms

    *kG and kCos are both gravity feedforwards, and only one can be used at a time. Many calculators refer to both as "kG", but arms will need to use kCos instead.

    kS - Static Gain

    kV - Velocity Gain

    kA - Acceleration Gain

    kG - Static (Elevator) Gravity Gain

    Manually finding kG and kS for an elevator
    1. Ensure your elevator is free to move up and down and note any physical limits

    2. Set up a Voltage output to the motors driving the elevator, via REVLib or REV Hardware Client

    3. Increase the output slowly until the elevator begins to rise

    4. Decrease the output slowly until the elevator stops and stays where it is

    5. Increase the output slightly until any more makes the elevator rise

    6. Note the Voltage output as V1

    7. Decrease the output slowly until the elevator begins to fall

    8. Increase the output slowly until any less makes the elevator fall

    9. Note the Voltage output as V2

    You now have two Voltage values, V1 and V2, that define the edges of the region of output where the elevator holds its position. Any more than V1 and the elevator will rise, and any less than V2 and the elevator will fall.

    Use the equations below to find kS and kG:

    • kS = (V1 - V2) / 2

    • kG = V2 + kS

    kG is right in the middle of this region, where it will keep the elevator right where it is.

    kS is the distance to the edges of this region, where kG + kS is the maximum output without upward movement and kG - kS is the minimum output without downward movement. This allows the PID controller to overcome resistance in either direction.

    kCos - Cosine (Arm) Gravity Gain

    To use this gain properly, the motor on your arm needs to be configured such that when the arm (the radius to the center of mass of the arm) is perfectly horizontal the selected sensor's position is zero.

    Manually finding kCos and kS for an arm
    1. Ensure your arm is free to move up and down and note any physical limits

    2. Set up a Voltage output to the motors driving the arm, via REVLib or REV Hardware Client

    3. Set a current limit to avoid damaging the motors

    4. Hold the arm horizontally

    5. Increase the output slowly until the arm begins to rise

    6. Decrease the output slowly until the arm stops and stays perfectly horizontal under its own power

    7. Don't let the arm hang under its own power horizontally longer than it needs to, or you risk damaging the motor as it heats up

    8. Increase the output slightly until any more makes the arm rise

    9. Note the Voltage output as V1

    10. Pause, disable, and power off the motor for a few minutes

    11. Hold the arm horizontally again, set the voltage at or just below V1

    12. Decrease the output slowly until the arm begins to fall

    13. Increase the output slowly until any less makes the arm fall but the arm stays perfectly horizontal under its own power

    14. Note the Voltage output as V2

    You now have two Voltage values, V1 and V2, that define the edges of the region of output where the arm holds its position horizontally. Any more than V1 and the arm will rise, and any less than V2 and the arm will fall.

    Use the equations below to find kS and kG:

    • kS = (V1 - V2) / 2

    • kG = V2 + kS

    kG is right in the middle of this region, where it will keep the arm perfectly horizontal.

    kS is the distance to the edges of this region, where kG + kS is the maximum output without upward movement and kG - kS is the minimum output without downward movement. This allows the PID controller to overcome resistance in either direction.

    kCosRatio - Ratio Constant for use with kCos

    Arbitrary Feed Forward

    WPILib offers several basic feed forward calculation classes that work great with arbFF

    ClosedLoopConfig
    ClosedLoopConfig
    kG
    kCos
    ReCalc
    ReCalc
    ReCalc
    ReCalc
    SparkClosedLoopController
    setSetpoint
    SparkClosedLoopController
    SetSetpoint

    Volts

    SparkFlexConfig config = new SparkFlexConfig();
    
    // Set PID gains
    config
        .closedLoop
            .pid(0, 0, 0) // slot 0
            .pid(0, 0, 0, ClosedLoopSlot.kSlot1) // slot 1
            .feedForward
                .kS(s) // slot 0 by default
                .kV(v, ClosedLoopSlot.kSlot0) // slot 0 explicitly
                .kA(a)
                .kG(g) // Only use one of kG and kCos
                .kCos(g)
                .kCosRatio(cosRatio)
                
                .sva(s, v, a, ClosedLoopSlot.kSlot1); // slot 1
    using namespace rev::spark;
    
    SparkFlexConfig config;
    
    // Set PID gains
    config
        .closedLoop
            .pid(0, 0, 0) // slot 0
            .pid(0, 0, 0, ClosedLoopSlot::kSlot1) // slot 1
            .feedForward
                .kS(s) // slot 0 by default
                .kV(v, ClosedLoopSlot::kSlot0) // slot 0 explicitly
                .kA(a)
                .kG(g) // Only use one of kG and kCos
                .kCos(g)
                .kCosRatio(cosRatio)
                
                .sva(s, v, a, ClosedLoopSlot::kSlot1); // slot 1
    // Set the setpoint of the controller in raw position mode, with a feedforward
    m_controller.setSetpoint(
        setPoint, 
        ControlType.kPosition,
        0, // setpoint position
        arbFeedForward
    );
    using namespace rev::spark;
    
    // Set the setpoint of the controller in raw position mode, with a feedforward
    m_controller.SetSetpoint(
        setPoint, 
        SparkBase::ControlType::kPosition,
        0, // setpoint position
        feedForward
    );