AutoThrottleNG is a robust and feature-rich Arduino library designed for creating sophisticated, failsafe throttle or output control systems. It builds upon the standard Arduino PID
library to provide precise regulation, enhanced with input filtering, output smoothing, and multiple layers of safety checks.
This library is ideal for applications where smooth, stable, and reliable control of motors, actuators, or other output devices is critical, such as:
- Robotics (e.g., wheel speed control, arm joints)
- Drones and UAVs (e.g., altitude hold, motor speed regulation)
- Automated Vehicles (e.g., cruise control, steering)
- Process Control (e.g., temperature regulation, flow control)
- Any system requiring closed-loop control with added safety and refinement.
AutoThrottleNG manages the complexities of filtering noisy sensor data, preventing abrupt output changes, monitoring system stability, and handling common failure scenarios, allowing you to focus on the higher-level logic of your application.
AutoThrottleNG integrates several key control and signal processing techniques:
-
PID Control (Proportional-Integral-Derivative)
-
The library uses the standard Arduino
PID
library (requiringPID.h
) as its core control algorithm. -
It continuously calculates an error value (
e(t)
) as the difference between the desiredSetpoint
and the current (filtered)Input
. -
It computes an
Output
value aiming to minimize this error over time. -
Formula: The discrete PID algorithm can be represented as:
Error[t] = Setpoint - Input[t] P = Kp * Error[t] I = I[t-1] + Ki * Error[t] * Δt D = Kd * (Error[t] - Error[t-1]) / Δt Output[t] = P + I + D
Where:
Kp
: Proportional Gain (reacts to current error)Ki
: Integral Gain (accumulates past errors to eliminate steady-state offset)Kd
: Derivative Gain (reacts to the rate of change of the error, dampens oscillations)Δt
: Time interval between calculations (Sample Time)
-
Proportional on Measurement (P_ON_M): The underlying PID library also supports P_ON_M, where the proportional term is calculated based on the change in input rather than the change in error. This can reduce "derivative kick" when the setpoint changes suddenly. AutoThrottleNG allows configuring this via the constructor or
setTunings
.
-
-
Input Filtering (Exponential Moving Average - EMA)
-
Purpose: Sensor readings are often noisy. EMA filtering smooths the raw input signal before it's fed to the PID controller, leading to more stable control and preventing the derivative term (Kd) from overreacting to noise.
-
Formula:
FilteredValue[t] = α * RawValue[t] + (1 - α) * FilteredValue[t-1]
Where:
α
(alpha) is the smoothing factor (0.0 to 1.0), configured viasetInputFilterAlpha()
.α = 0.0
: No filtering (FilteredValue = PreviousFilteredValue) - Note: Library uses 0 to mean pass-through raw value.α ≈ 1.0
: Minimal filtering (FilteredValue ≈ RawValue)- Smaller
α
: More smoothing, but introduces more lag (delay) in the filtered signal's response to changes. α = 0.0
is treated as "filter disabled" by the library.
-
Trade-off: Increased smoothing (lower
α
) reduces noise sensitivity but makes the system slower to respond to genuine changes in the input.
-
-
Output Smoothing (Ramp Rate Limiting)
- Purpose: Prevents the final throttle/output value from changing too abruptly, even if the PID controller calculates a large change. This is crucial for systems sensitive to sudden acceleration/deceleration (e.g., preventing jerky movements, mechanical stress, current spikes).
- Mechanism: The library limits the maximum rate of change of the final output value per second.
- Logic:
- Calculate the time elapsed (
Δt
) since the lastcompute()
call. - Calculate the maximum allowable change for this interval:
MaxChange = MaxRatePerSecond * Δt / 1000.0
- Compare the raw PID output (
TargetOutput
) to the previous smoothed output (SmoothedOutput[t-1]
). - Limit the new smoothed output:
SmoothedOutput[t] = constrain(TargetOutput, SmoothedOutput[t-1] - MaxChange, SmoothedOutput[t-1] + MaxChange)
- Calculate the time elapsed (
- Configuration: Enabled via
enableSmoothing(true, maxRatePerSecond)
. - Trade-off: Increased smoothing (lower
maxRatePerSecond
) results in smoother transitions but limits how quickly the system can respond to requests for large output changes.
- Standard PID Integration: Leverages the well-established Arduino
PID
library (PID.h
) for core control logic. - Input Filtering: Built-in optional Exponential Moving Average (EMA) filter to smooth noisy sensor inputs before they reach the PID.
- Output Smoothing: Optional time-based ramp rate limiting on the final output to prevent sudden jumps or jerky behavior.
- Failsafe Mechanisms:
- Input Validity: Rejects
NaN
orInfinity
sensor readings. - Input Timeout: Triggers an error if
updateInput()
isn't called within a configurable duration. - Stability Timeout: Triggers an error if the system's error (
|Setpoint - FilteredInput|
) remains larger than a tolerance for a configurable duration. - Configurable Failsafe Output: Sets a predefined, safe output value when any error state is active.
- Clear Error Reporting: Provides methods to check the current error state.
- Manual Error Reset: Requires user code intervention (
clearErrorState()
) to resume normal operation after a failsafe.
- Input Validity: Rejects
- Status Monitoring: Provides getters for raw input, filtered input, raw PID output, smoothed throttle output, PID gains, mode, setpoint, error state, stability status, output saturation, and last update time.
- Flexible Configuration: Allows runtime adjustment of PID tunings, output limits, sample time, controller direction, filter parameters, smoothing parameters, and failsafe settings.
- Robust Design: Includes checks and constraints to handle edge cases and invalid configurations.
- Arduino PID Library: Requires the standard "PID" library by Brett Beauregard (often referred to as PID v1) to be installed in your Arduino IDE. This library provides
PID.h
. Install it via the Arduino Library Manager (Search for "PID").
- Install PID Library: If not already installed, open the Arduino IDE, go to Tools -> Manage Libraries..., search for "PID" by Brett Beauregard, and click Install.
- Download AutoThrottleNG: Download the
AutoThrottleNG
library files (e.g., as a ZIP from GitHub). - Install AutoThrottleNG: In the Arduino IDE, go to Sketch -> Include Library -> Add .ZIP Library... and select the downloaded ZIP file.
- Restart IDE: It's often good practice to restart the Arduino IDE after installing new libraries.
- Examples: Check File -> Examples -> AutoThrottleNG for example sketches.
AutoThrottleNG(double minOutput, double maxOutput,
double kp, double ki, double kd,
int POn = P_ON_E, int direction = DIRECT);
- Initializes the AutoThrottleNG controller.
- Parameters:
minOutput
(double): The minimum value the PID controller's output (_pidOutput
) and the final throttle (getThrottle()
) can have.maxOutput
(double): The maximum value the PID controller's output and final throttle can have.kp
(double): Initial Proportional tuning gain.ki
(double): Initial Integral tuning gain.kd
(double): Initial Derivative tuning gain.POn
(int): Proportional calculation mode. UseP_ON_E
(Proportional on Error, default) orP_ON_M
(Proportional on Measurement). These constants are typically defined by the underlyingPID.h
.direction
(int): Controller direction. UseDIRECT
(increase output to increase input, default) orREVERSE
(increase output to decrease input). Constants defined byPID.h
.
void setTarget(double target);
- Sets the desired target value (setpoint) for the PID controller.
- NaN or Infinity values are ignored.
- Parameters:
target
(double): The target value the controller should aim for, in the same units as the input signal.
void updateInput(double rawInputValue);
- Provides the latest raw sensor reading to the controller.
- This value is filtered (if enabled) before being used by the PID.
- Must be called regularly and more frequently than the configured Input Timeout.
- NaN or Infinity values are rejected, and the
INPUT_INVALID
error state is set. - Parameters:
rawInputValue
(double): The latest raw reading from the sensor.
double compute();
- The main workhorse method. Call this in every iteration of your
loop()
. - Checks for failsafe conditions (timeouts).
- If no failsafe is active, it triggers the internal
_pid.Compute()
method (which uses the filtered input and setpoint). - Applies output smoothing (if enabled) to the raw PID output.
- Clamps the final output to the defined limits.
- Handles setting the failsafe output value if an error state is active.
- Returns:
- (double): The final, calculated throttle value (smoothed or failsafe value), constrained within
minOutputLimit
andmaxOutputLimit
.
- (double): The final, calculated throttle value (smoothed or failsafe value), constrained within
void reset();
- Resets the internal state of the PID controller (typically clears the integral sum by cycling the mode) and resets AutoThrottleNG's failsafe timers and error state.
- Does NOT reset: PID gains (Kp, Ki, Kd), output limits, setpoint, filter settings, smoothing settings, or failsafe configuration values. Call
setTarget()
again afterreset()
if needed.
void setTunings(double kp, double ki, double kd, int POn = -1);
- Sets the PID gains and optionally the Proportional mode.
- Parameters:
kp
,ki
,kd
(double): New tuning gains.POn
(int): Optional. Set toP_ON_E
orP_ON_M
to change mode, or leave as -1 (or omit) to keep the current Proportional mode.
void setOutputLimits(double minOutput, double maxOutput);
- Sets the minimum and maximum allowed values for the raw PID output and the final smoothed throttle. Clamps the current output and failsafe value if necessary.
- Parameters:
minOutput
,maxOutput
(double): New lower and upper output bounds.minOutput
must be less thanmaxOutput
.
void setControllerDirection(int direction);
- Sets the controller direction.
- Parameters:
direction
(int):DIRECT
orREVERSE
.
void setSampleTime(int sampleTimeMillis);
- Sets how often (in milliseconds) the PID algorithm attempts to calculate a new output.
- Parameters:
sampleTimeMillis
(int): The desired interval > 0.
void setMode(int mode);
- Sets the PID operating mode. An active failsafe will override this and force
MANUAL
. - Parameters:
mode
(int):AUTOMATIC
(PID enabled) orMANUAL
(PID calculations disabled, output holds).
void setInputFilterAlpha(double alpha);
- Configures the Exponential Moving Average (EMA) input filter.
- Parameters:
alpha
(double): Smoothing factor, clamped between 0.0 and 1.0.0.0
: Filter disabled (raw input used directly).- Values closer to
0.0
: More smoothing, more lag. - Values closer to
1.0
: Less smoothing, less lag.
void enableSmoothing(bool enable, double maxRatePerSecond = 10.0);
- Enables or disables time-based ramp rate limiting on the final output.
- Parameters:
enable
(bool):true
to enable,false
to disable.maxRatePerSecond
(double): The maximum allowed change in output units per second when smoothing is enabled. Must be > 0. Default: 10.0.
void setFailsafeValue(double value);
- Sets the output value that
getThrottle()
will return when any failsafe condition is active. The value is constrained by the output limits. - Parameters:
value
(double): The desired failsafe output value.
void setInputTimeout(unsigned long durationMillis);
- Sets the maximum allowed time (in milliseconds) between successful calls to
updateInput()
. If this time is exceeded, theINPUT_TIMEOUT
error state is triggered. - Parameters:
durationMillis
(unsigned long): Timeout duration in ms. Set to0
to disable this check.
void setStabilityParams(double tolerance, unsigned long durationMillis);
- Configures the stability check failsafe.
- Parameters:
tolerance
(double): The maximum absolute difference allowed between the setpoint (_pidSetpoint
) and the filtered input (_pidInput
) for the system to be considered stable. Must be >= 0.durationMillis
(unsigned long): The maximum time (in ms) the system's error can continuously exceed thetolerance
before theSTABILITY_TIMEOUT
error state is triggered. Set to0
to disable this check.
double getThrottle() const;
- Returns the final calculated throttle output value after PID computation, smoothing (if enabled), and failsafe checks. This is the value you should apply to your actuator.
double getRawPIDOutput() const;
- Returns the raw output value calculated by the internal
_pid.Compute()
method, before output smoothing is applied. Useful for debugging PID behavior.
double getFilteredInput() const;
- Returns the input value after the EMA filter has been applied. This is the value actually used by the PID algorithm.
double getRawInput() const;
- Returns the last valid raw input value provided via
updateInput()
, before filtering.
bool isStable() const;
- Checks if the current absolute difference between the setpoint and the filtered input is within the configured
stabilityToleranceValue
. Note that this is an instantaneous check and doesn't relate directly to theSTABILITY_TIMEOUT
error state, which requires the condition to persist.
double getKp() const;
double getKi() const;
double getKd() const;
- Return the current Proportional, Integral, or Derivative gain being used by the PID controller.
int getMode() const;
- Returns the current operating mode of the PID controller (
AUTOMATIC
orMANUAL
).
double getSetpoint() const;
- Returns the current target setpoint value stored within AutoThrottleNG.
bool isSaturated() const;
- Returns
true
if the last raw PID output (_pidOutput
) was at the minimum or maximum output limit,false
otherwise. Indicates the PID is trying to command an output beyond its limits.
unsigned long getLastUpdateTime() const;
- Returns the
millis()
timestamp of whenupdateInput()
was last called with a valid (non-NaN, non-Inf) value. Useful for debugging input timeout issues.
AutoThrottleNG::Error getErrorState() const;
- Returns the current failsafe error state.
- Return Values (enum
AutoThrottleNG::Error
):NONE
: No error active.INPUT_INVALID
: Last call toupdateInput()
received NaN or Infinity.INPUT_TIMEOUT
:updateInput()
hasn't been called within the configured timeout.STABILITY_TIMEOUT
: System error has exceeded tolerance for too long.
bool isInErrorState() const;
- Returns
true
ifgetErrorState()
is anything other thanError::NONE
,false
otherwise. Convenient check before taking failsafe actions.
void clearErrorState();
- Manually resets the error state back to
Error::NONE
. - Crucially, you MUST call this function in your code once the condition causing the failsafe has been resolved (or is assumed resolved) to allow the controller to resume
AUTOMATIC
operation. The library will not automatically clear most error states.
+-------------------+ +-----------------+ +---------------------+ +------------------+
| Raw Sensor Signal | ---> | updateInput() | ---> | Input Filter (EMA) | ---> | Filtered Input | ----+
+-------------------+ | (Validity Check)| | (if alpha > 0) | | (_pidInput) | |
+-----------------+ +---------------------+ +------------------+ |
| V
+-------------------+ +-------------------+ +---------------------+ | [PID]
| setTarget() |-->| Target Setpoint | --> | PID Algorithm | <--+
+-------------------+ | (_pidSetpoint) | | (_pid.Compute()) |
+-------------------+ +-------+-------------+
| Output
V (_pidOutput)
+-------------------+ +--------------------+ +---------------------+ +------------------+
| Final Throttle | <--- | Output Smoothing | <--- | Raw PID Output | <--- | (Check Output |
| Value | | (Ramp Limit) | | | | Saturation) |
| (to Actuator) | | (if enabled) | | | +------------------+
+-------------------+ +-----+--------------+ +---------------------+
^
| [Failsafe Override]
|
+-------------------+ <-------+
| Failsafe Value |
| (if Error State!) |
+-------------------+
* Failsafe Checks (Timeouts) occur within compute() before PID calculation.
* If Error State is active, PID is bypassed, and Failsafe Value is used (potentially smoothed).
- PID Tuning is Crucial: You must tune the Kp, Ki, and Kd values for your specific system. Start with Kp and gradually add Ki and Kd. Use methods like Ziegler-Nichols or trial-and-error. Monitor the
getRawPIDOutput()
andgetFilteredInput()
to understand behavior. - Input Filtering: Use
setInputFilterAlpha()
to reduce noise. Start with a small value (e.g., 0.1-0.3) if noise is significant. Be aware this adds lag. If your sensor is clean, leave alpha at 0.0 (disabled). - Output Smoothing: Use
enableSmoothing()
if you need smooth output transitions. Adjust themaxRatePerSecond
based on your system's requirements – lower values give more smoothing but limit responsiveness. - Failsafes:
- Configure timeouts (
setInputTimeout
,setStabilityParams
) appropriate for your system's response time and sensor update rate. Don't set them too short or too long. - Set a safe
failsafeValue
(e.g., 0% throttle, hover throttle). - Your main loop must check
isInErrorState()
and potentially take further action (e.g., disarm, alert). - You must call
clearErrorState()
to recover from a failsafe once the underlying issue is resolved.
- Configure timeouts (
compute()
Frequency: Callcompute()
in everyloop()
iteration for consistent timing and responsiveness of smoothing and PID calculations (the internal PID library manages its own sample time).updateInput()
Frequency: CallupdateInput()
as often as you get new sensor readings, and definitely faster than the configuredinputTimeoutMillis
.- Units: Ensure consistency! The units for
setTarget
,updateInput
, andstabilityTolerance
must all match. The output units will be defined byminOutput
/maxOutput
. - Constants: Verify the exact names/values of
DIRECT
,REVERSE
,AUTOMATIC
,MANUAL
,P_ON_E
,P_ON_M
as defined in thePID.h
file included with your Arduino core or the installed PID library.
Contributions are welcome! Please follow standard GitHub practices:
- Fork the repository.
- Create a new branch for your feature or fix.
- Commit your changes with clear messages.
- Push your branch and submit a Pull Request.
- Provide a detailed description of your changes in the PR.
This library is released under the MIT License. See the LICENSE file for details.