ADRC APIs

ADRC

The adrc component provides active disturbance rejection controllers for first-order and second-order plants, including both linear ADRC and Han-style nonlinear ADRC variants.

At a high level, ADRC treats plant uncertainty and external disturbances as an extra state that can be estimated online by an extended state observer (ESO). Instead of relying on a very accurate plant model, you provide an approximate input gain b0 and let the observer estimate the remaining dynamics. That usually makes ADRC attractive for embedded robotics systems where friction, battery sag, payload changes, cable drag, and contact disturbances are present but hard to model precisely.

This component includes:

  • HanTrackingDifferentiator for setpoint shaping and reference-rate estimation

  • LinearAdrcFirstOrder for approximately first-order plants

  • LinearAdrcSecondOrder for approximately second-order plants

  • HanAdrcFirstOrder for nonlinear first-order ADRC

  • HanAdrcSecondOrder for nonlinear second-order ADRC

Choosing a controller

For many motor-control applications, a good starting point is:

  1. use a faster inner current or torque loop if one already exists,

  2. place first-order ADRC around motor speed, or second-order ADRC around position,

  3. move to the Han nonlinear variants only if the linear controllers do not give the disturbance rejection or large-error behavior you want.

Robotics motor-control usage

In a practical robotics stack, ADRC is usually most effective in the outer mechanical loops rather than as a replacement for every low-level regulator. For example:

  • a BLDC or DC motor drive might keep its inner current loop and use ADRC for wheel speed,

  • a servo joint might use second-order ADRC on position with the motor driver handling the faster electrical dynamics,

  • a mobile robot heading controller can use second-order ADRC to compensate for uneven traction, slope changes, or payload variation.

The observer states are often useful for debugging and tuning:

  • z2 in the first-order controllers is the estimated lumped disturbance,

  • z3 in the second-order controllers is the estimated lumped disturbance,

  • large persistent disturbance estimates usually indicate load torque, friction, bias, or a poor b0 estimate.

Those estimated disturbance states are especially valuable in robotics, because they can explain why a loop feels “mysteriously” different under battery sag, ground contact, or changing payload.

Tuning for motors and actuators

ADRC is often tuned by bandwidth rather than by directly shaping multiple independent gains. A practical workflow for motor-control applications is:

  1. Pick the structure first. Use first-order ADRC for speed-like loops and second-order ADRC for position-like loops.

  2. Estimate ``b0``. The sign must be correct. The magnitude only needs to be approximate, but if it is very wrong the controller will feel badly scaled and may saturate too early.

  3. Start with conservative bandwidth. Increase the controller bandwidth until the loop is responsive but not noisy or oscillatory.

  4. Make the observer faster than the controller. A faster ESO helps reject disturbances early, but too much observer bandwidth will amplify encoder or velocity-estimate noise.

  5. Use the tracking differentiator to soften steps. This is especially helpful for position commands that would otherwise demand unrealistic acceleration from the motor.

  6. Watch saturation. If the command rails against output_min / output_max, back off the bandwidth or revisit b0 and trajectory shaping before increasing gains further.

Some practical heuristics:

  • for the linear variants, choose observer_bandwidth several times larger than controller_bandwidth and increase gradually;

  • if encoder noise or quantization dominates, reduce observer bandwidth before blaming the control law;

  • if large setpoint steps cause overshoot or chatter, enable the tracking differentiator or lower its aggressiveness;

  • for the Han nonlinear variants, increase fal_delta if the loop is too sharp around zero error, and adjust the alpha parameters only after the main gain and bandwidth choices are in the right range.

Note

The most important ADRC tuning parameter is usually not a fancy nonlinear exponent, but the combination of loop rate, actuator saturation, sensor quality, and a reasonable b0 estimate. If the sample period is too slow or the actuator is saturating constantly, ADRC will not rescue the loop.

References

Useful starting points for ADRC background and tuning include:

  • Jingqing Han, From PID to Active Disturbance Rejection Control, IEEE Transactions on Industrial Electronics, 2009.

  • Zhiqiang Gao, Active Disturbance Rejection Control: A Paradigm Shift in Feedback Control System Design, Proceedings of the American Control Conference, 2006.

  • Zhiqiang Gao, Scaling and Bandwidth-Parameterization Based Controller Tuning, Proceedings of the American Control Conference, 2002.

  • Gernot Herbst, Practical Active Disturbance Rejection Control: Tuning Methods and Guidelines for Continuous-Time Systems, IFAC-PapersOnLine, 2015.

Code examples for the ADRC API are provided in the components/adrc/example folder.

API Reference

Header File

Classes

class HanTrackingDifferentiator : public espp::BaseComponent

Han tracking differentiator for smoothing a reference trajectory and estimating its rate.

The tracking differentiator (TD) is commonly used at the front of an ADRC loop to turn a stepped reference into a smooth internal command. That reduces jerk, limits observer shock, and provides an internally consistent reference derivative for second-order loops.

For robotics motor control, the TD is useful when a planner or user interface produces abrupt setpoint changes but the plant should accelerate smoothly. In a position loop, for example, the TD can convert a position step into a smoother position and velocity command pair.

Tuning

`tracking_bandwidth` controls how aggressively the TD chases the target. Larger values follow the reference more tightly but can reintroduce abrupt motion. `filter_factor` scales the internal `h0` term and can be increased to further soften the commanded trajectory.

Public Functions

inline explicit HanTrackingDifferentiator(const Config &config)

Construct the tracking differentiator.

Parameters:

config – Tracking differentiator configuration.

inline void set_config(const Config &config, bool reset_state = true)

Change the tracking differentiator configuration.

Parameters:
  • config – New configuration.

  • reset_state – If true, reset the differentiator state after applying the config.

inline void clear(float value = 0.0f)

Reset the differentiator state.

Parameters:

value – Initial position value after reset.

inline State update(float target, float dt)

Advance the tracking differentiator.

Parameters:
  • target – Target reference value to follow.

  • dt – Timestep in seconds.

Returns:

Updated tracking differentiator state.

inline State operator()(float target, float dt)

Advance the tracking differentiator.

Parameters:
  • target – Target reference value to follow.

  • dt – Timestep in seconds.

Returns:

Updated tracking differentiator state.

inline State get_state() const

Get the most recently computed state.

Returns:

Current differentiator state.

inline Config get_config() const

Get the active differentiator configuration.

Returns:

Current configuration.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct Config

Configuration for the Han tracking differentiator.

Public Members

float tracking_bandwidth

Tracking aggressiveness parameter, commonly noted `r`.

float filter_factor = {5.0f}

Multiplier applied to `dt` for the Han `h0` term.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity for the internal logger.

struct State

State of the tracking differentiator.

Public Members

float position = {0.0f}

Smoothed reference value.

float rate = {0.0f}

Estimated first derivative of the smoothed reference.

class LinearAdrcFirstOrder : public espp::BaseComponent

Linear first-order active disturbance rejection controller.

This controller targets plants that can be approximated as a first-order system in the controlled variable, such as a velocity loop whose command acts roughly like torque or acceleration after inner current regulation.

The linear ESO estimates:

  • `z1`: the controlled output, and

  • `z2`: the lumped disturbance and modeling error.

The resulting control law is simple to tune with bandwidth parameters and is often a good first ADRC choice when migrating from PI or PID speed control.

Typical robotics use

Use this class for wheel speed control, conveyor speed control, or other actuator loops where the measured state is dominated by a single pole. In a cascaded servo, this is commonly the outer speed loop above a faster current controller.

Tuning

Start with a conservative `controller_bandwidth`, then choose `observer_bandwidth` several times higher so the ESO settles faster than the controller. `b0` should match the sign of the actuator path and roughly scale command to output-rate change. If the controller saturates immediately, reduce the bandwidth or improve the `b0` estimate before increasing gains.

Linear First-Order ADRC Example

    espp::LinearAdrcFirstOrder controller({
        .b0 = 1.0f,
        .controller_bandwidth = 8.0f,
        .observer_bandwidth = 24.0f,
        .output_min = -4.0f,
        .output_max = 4.0f,
    });
    FirstOrderPlant plant;
    for (int i = 0; i < num_steps; ++i) {
      auto time = i * dt;
      auto reference = time >= 0.25f ? 1.0f : 0.0f;
      plant.disturbance = time >= 2.5f ? -0.8f : 0.0f;
      auto control = controller.update(reference, plant.y, dt);
      auto output = plant.update(control, dt);
      if (i % print_interval == 0 || i == num_steps - 1) {
        auto state = controller.get_state();
        fmt::print("t={:0.2f}s ref={:0.2f} y={:0.3f} u={:0.3f} z2={:0.3f}\n", time, reference,
                   output, state.output, state.z2);
      }
    }

Public Functions

inline explicit LinearAdrcFirstOrder(const Config &config)

Construct the first-order linear ADRC controller.

Parameters:

configController configuration.

inline void set_config(const Config &config, bool reset_state = true)

Change the controller configuration.

Parameters:
  • config – New controller configuration.

  • reset_state – If true, reset the observer and control state after applying the config.

inline void clear(float measurement = 0.0f)

Reset the observer and controller state.

Parameters:

measurement – Initial measured output for the observer state.

inline float update(float reference, float measurement, float dt)

Update the controller.

Parameters:
  • reference – Desired output reference.

  • measurement – Measured output.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float operator()(float reference, float measurement, float dt)

Update the controller.

Parameters:
  • reference – Desired output reference.

  • measurement – Measured output.

  • dt – Timestep in seconds.

Returns:

Control output.

inline State get_state() const

Get the current controller state.

Returns:

Current state.

inline Config get_config() const

Get the active configuration.

Returns:

Current configuration.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct Config

Configuration for the first-order linear ADRC controller.

Public Members

float b0

Estimated control gain of the plant.

float controller_bandwidth

Closed-loop bandwidth used for the state error feedback gain.

float observer_bandwidth

Observer bandwidth used for the linear extended state observer.

float output_min

Minimum output command.

float output_max

Maximum output command.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity for the internal logger.

struct State

Observer and controller state.

Public Members

float measurement = {0.0f}

Most recent measured output.

float reference = {0.0f}

Most recent reference input.

float z1 = {0.0f}

Estimated plant output state.

float z2 = {0.0f}

Estimated total disturbance state.

float output = {0.0f}

Most recent controller output.

class LinearAdrcSecondOrder : public espp::BaseComponent

Linear second-order active disturbance rejection controller.

This controller targets plants whose commanded input primarily affects the second derivative of the controlled variable. Typical examples include motor position loops, gimbal angle loops, and heading loops where the command acts like torque or angular acceleration.

The linear ESO estimates:

  • `z1`: position,

  • `z2`: rate, and

  • `z3`: the lumped disturbance and unmodeled dynamics.

Typical robotics use

Use this class when your feedback variable is position but your actuator fundamentally produces acceleration. In a mobile robot or arm joint, this is usually the position controller that sits above a faster velocity or torque loop.

Tuning

`controller_bandwidth` shapes the closed-loop stiffness and damping. The observer is usually tuned faster than the controller so `z3` converges before it dominates the motion. If a trajectory generator already provides a velocity feedforward term, pass it through the four-argument `update()` overload as `reference_rate`.

Linear Second-Order ADRC Example

    espp::LinearAdrcSecondOrder controller({
        .b0 = 1.0f,
        .controller_bandwidth = 10.0f,
        .observer_bandwidth = 36.0f,
        .output_min = -8.0f,
        .output_max = 8.0f,
    });
    SecondOrderPlant plant;
    for (int i = 0; i < num_steps; ++i) {
      auto time = i * dt;
      auto reference = time >= 0.25f ? 1.0f : 0.0f;
      plant.disturbance = time >= 2.5f ? 1.2f : 0.0f;
      auto control = controller.update(reference, plant.x1, dt);
      auto output = plant.update(control, dt);
      if (i % print_interval == 0 || i == num_steps - 1) {
        auto state = controller.get_state();
        fmt::print("t={:0.2f}s ref={:0.2f} y={:0.3f} u={:0.3f} z3={:0.3f}\n", time, reference,
                   output, state.output, state.z3);
      }
    }

Public Functions

inline explicit LinearAdrcSecondOrder(const Config &config)

Construct the second-order linear ADRC controller.

Parameters:

configController configuration.

inline void set_config(const Config &config, bool reset_state = true)

Change the controller configuration.

Parameters:
  • config – New controller configuration.

  • reset_state – If true, reset the observer and control state after applying the config.

inline void clear(float measurement = 0.0f, float measurement_rate = 0.0f)

Reset the observer and controller state.

Parameters:
  • measurement – Initial measured position for the observer state.

  • measurement_rate – Initial measured rate estimate for the observer state.

inline float update(float reference, float reference_rate, float measurement, float dt)

Update the controller using an explicit reference rate.

Parameters:
  • reference – Desired position reference.

  • reference_rate – Desired reference rate.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float update(float reference, float measurement, float dt)

Update the controller assuming a zero reference-rate command.

Parameters:
  • reference – Desired position reference.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float operator()(float reference, float measurement, float dt)

Update the controller assuming a zero reference-rate command.

Parameters:
  • reference – Desired position reference.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline State get_state() const

Get the current controller state.

Returns:

Current state.

inline Config get_config() const

Get the active configuration.

Returns:

Current configuration.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct Config

Configuration for the second-order linear ADRC controller.

Public Members

float b0

Estimated control gain of the plant.

float controller_bandwidth

Closed-loop bandwidth used to derive the feedback gains.

float observer_bandwidth

Observer bandwidth used for the linear extended state observer.

float output_min

Minimum output command.

float output_max

Maximum output command.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity for the internal logger.

struct State

Observer and controller state.

Public Members

float measurement = {0.0f}

Most recent measured position.

float reference = {0.0f}

Most recent reference position.

float reference_rate = {0.0f}

Most recent reference rate.

float z1 = {0.0f}

Estimated plant position state.

float z2 = {0.0f}

Estimated plant rate state.

float z3 = {0.0f}

Estimated total disturbance state.

float output = {0.0f}

Most recent controller output.

class HanAdrcFirstOrder : public espp::BaseComponent

Han-style nonlinear first-order active disturbance rejection controller.

This variant replaces the linear error terms with Han’s nonlinear `fal()` functions and can optionally prefilter the reference with a tracking differentiator. The nonlinear feedback is often attractive when you want high authority on large errors without making the loop excessively sharp around the operating point.

Typical robotics use

Use this class for speed loops that must reject large step disturbances but remain well-behaved near zero speed, for example wheel or propeller speed control with friction, stiction, or battery-voltage variation.

Tuning

`controller_gain` is the main feedback gain. `observer_bandwidth` sets the speed of the nonlinear ESO. `controller_alpha`, `observer_alpha`, and `fal_delta` determine how strongly the loop transitions between linear small-error behavior and nonlinear large-error behavior. Increase `fal_delta` if the loop is too twitchy around the setpoint, especially with noisy sensors.

Han First-Order ADRC Example

    espp::HanAdrcFirstOrder controller({
        .b0 = 1.0f,
        .controller_gain = 7.0f,
        .observer_bandwidth = 22.0f,
        .observer_alpha = 0.5f,
        .controller_alpha = 0.8f,
        .fal_delta = 0.01f,
        .use_tracking_differentiator = true,
        .tracking_config =
            {
                .tracking_bandwidth = 45.0f,
                .filter_factor = 5.0f,
            },
        .output_min = -4.0f,
        .output_max = 4.0f,
    });
    FirstOrderPlant plant;
    for (int i = 0; i < num_steps; ++i) {
      auto time = i * dt;
      auto reference = time >= 0.25f ? 1.0f : 0.0f;
      plant.disturbance = time >= 2.5f ? -0.8f : 0.0f;
      auto control = controller.update(reference, plant.y, dt);
      auto output = plant.update(control, dt);
      if (i % print_interval == 0 || i == num_steps - 1) {
        auto state = controller.get_state();
        fmt::print("t={:0.2f}s ref={:0.2f} td={:0.3f} y={:0.3f} u={:0.3f}\n", time, reference,
                   state.td_reference, output, state.output);
      }
    }

Public Functions

inline explicit HanAdrcFirstOrder(const Config &config)

Construct the first-order nonlinear ADRC controller.

Parameters:

configController configuration.

inline void set_config(const Config &config, bool reset_state = true)

Change the controller configuration.

Parameters:
  • config – New controller configuration.

  • reset_state – If true, reset the observer and control state after applying the config.

inline void clear(float measurement = 0.0f)

Reset the observer, controller, and tracking differentiator state.

Parameters:

measurement – Initial measured output for the observer state.

inline float update(float reference, float measurement, float dt)

Update the controller.

Parameters:
  • reference – Desired output reference.

  • measurement – Measured output.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float operator()(float reference, float measurement, float dt)

Update the controller.

Parameters:
  • reference – Desired output reference.

  • measurement – Measured output.

  • dt – Timestep in seconds.

Returns:

Control output.

inline State get_state() const

Get the current controller state.

Returns:

Current state.

inline Config get_config() const

Get the active configuration.

Returns:

Current configuration.

inline HanTrackingDifferentiator::State get_tracking_state() const

Get the current tracking differentiator state.

Returns:

Tracking differentiator state.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct Config

Configuration for the first-order nonlinear ADRC controller.

Public Members

float b0

Estimated control gain of the plant.

float controller_gain

Nonlinear state error feedback gain.

float observer_bandwidth

Observer bandwidth used to derive nonlinear ESO gains.

float observer_alpha = {0.5f}

`fal()` exponent used by the observer disturbance state.

float controller_alpha = {0.8f}

`fal()` exponent used by the nonlinear feedback term.

float fal_delta = {0.01f}

Small-signal linear region width used by `fal()`.

bool use_tracking_differentiator = {true}

Enable reference smoothing through the Han TD.

HanTrackingDifferentiator::Config tracking_config  {.tracking_bandwidth = 40.0f,.filter_factor = 5.0f}

Tracking differentiator configuration.

float output_min

Minimum output command.

float output_max

Maximum output command.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity for the internal logger.

struct State

Observer and controller state.

Public Members

float measurement = {0.0f}

Most recent measured output.

float reference = {0.0f}

Most recent reference input.

float td_reference = {0.0f}

Smoothed reference from the tracking differentiator.

float z1 = {0.0f}

Estimated plant output state.

float z2 = {0.0f}

Estimated total disturbance state.

float output = {0.0f}

Most recent controller output.

class HanAdrcSecondOrder : public espp::BaseComponent

Han-style nonlinear second-order active disturbance rejection controller.

This controller combines a second-order ESO with nonlinear state error feedback and an optional Han tracking differentiator. It is the most flexible controller in this component and is useful when a position-like loop must reject significant disturbances while still handling large setpoint changes gracefully.

Typical robotics use

Use this class for position control of motor-driven joints, pan/tilt systems, steering axes, or mobile-robot heading loops when load variation and unmodeled friction are too large for simple PD or PID tuning to stay robust.

Tuning

`position_gain` and `rate_gain` play roles similar to proportional and derivative action in the nonlinear feedback law. `observer_bandwidth` should usually be faster than the desired closed-loop motion, but if encoder noise is significant you may need to back it off and rely more on the tracking differentiator to keep the reference smooth. `controller_alpha1` and `controller_alpha2` change how strongly large position and rate errors are amplified relative to small errors.

Han Second-Order ADRC Example

    espp::HanAdrcSecondOrder controller({
        .b0 = 1.0f,
        .position_gain = 30.0f,
        .rate_gain = 6.0f,
        .observer_bandwidth = 32.0f,
        .observer_alpha1 = 0.5f,
        .observer_alpha2 = 0.25f,
        .controller_alpha1 = 0.8f,
        .controller_alpha2 = 1.5f,
        .fal_delta = 0.01f,
        .use_tracking_differentiator = true,
        .tracking_config =
            {
                .tracking_bandwidth = 60.0f,
                .filter_factor = 5.0f,
            },
        .output_min = -8.0f,
        .output_max = 8.0f,
    });
    SecondOrderPlant plant;
    for (int i = 0; i < num_steps; ++i) {
      auto time = i * dt;
      auto reference = time >= 0.25f ? 1.0f : 0.0f;
      plant.disturbance = time >= 2.5f ? 1.2f : 0.0f;
      auto control = controller.update(reference, plant.x1, dt);
      auto output = plant.update(control, dt);
      if (i % print_interval == 0 || i == num_steps - 1) {
        auto state = controller.get_state();
        fmt::print("t={:0.2f}s ref={:0.2f} td={:0.3f} y={:0.3f} u={:0.3f} z3={:0.3f}\n", time,
                   reference, state.td_reference, output, state.output, state.z3);
      }
    }

Note

When `use_tracking_differentiator` is true, the explicit `reference_rate` argument passed to the four-argument `update()` overload is ignored. In that mode the controller uses the tracking differentiator’s internally generated rate estimate instead.

Public Functions

inline explicit HanAdrcSecondOrder(const Config &config)

Construct the second-order nonlinear ADRC controller.

Parameters:

configController configuration.

inline void set_config(const Config &config, bool reset_state = true)

Change the controller configuration.

Parameters:
  • config – New controller configuration.

  • reset_state – If true, reset the observer and control state after applying the config.

inline void clear(float measurement = 0.0f, float measurement_rate = 0.0f)

Reset the observer, controller, and tracking differentiator state.

Parameters:
  • measurement – Initial measured position for the observer state.

  • measurement_rate – Initial measured rate estimate for the observer state.

inline float update(float reference, float reference_rate, float measurement, float dt)

Update the controller using an externally supplied reference rate when the tracking differentiator is disabled.

Note

If `use_tracking_differentiator` is true, `reference_rate` is ignored and the tracking differentiator’s internally estimated rate is used instead.

Parameters:
  • reference – Desired position reference.

  • reference_rate – Desired reference rate.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float update(float reference, float measurement, float dt)

Update the controller assuming a zero reference-rate command when the tracking differentiator is disabled.

Parameters:
  • reference – Desired position reference.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline float operator()(float reference, float measurement, float dt)

Update the controller assuming a zero reference-rate command when the tracking differentiator is disabled.

Parameters:
  • reference – Desired position reference.

  • measurement – Measured position.

  • dt – Timestep in seconds.

Returns:

Control output.

inline State get_state() const

Get the current controller state.

Returns:

Current state.

inline Config get_config() const

Get the active configuration.

Returns:

Current configuration.

inline HanTrackingDifferentiator::State get_tracking_state() const

Get the current tracking differentiator state.

Returns:

Tracking differentiator state.

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

struct Config

Configuration for the second-order nonlinear ADRC controller.

Public Members

float b0

Estimated control gain of the plant.

float position_gain

Nonlinear feedback gain for position error.

float rate_gain

Nonlinear feedback gain for rate error.

float observer_bandwidth

Observer bandwidth used to derive nonlinear ESO gains.

float observer_alpha1 = {0.5f}

`fal()` exponent used for the ESO middle state.

float observer_alpha2 = {0.25f}

`fal()` exponent used for the ESO disturbance state.

float controller_alpha1 = {0.8f}

`fal()` exponent used for the position error.

float controller_alpha2 = {1.5f}

`fal()` exponent used for the rate error.

float fal_delta = {0.01f}

Small-signal linear region width used by `fal()`.

bool use_tracking_differentiator = {true}

Enable reference smoothing through the Han TD.

HanTrackingDifferentiator::Config tracking_config  {.tracking_bandwidth = 60.0f,.filter_factor = 5.0f}

Tracking differentiator configuration.

float output_min

Minimum output command.

float output_max

Maximum output command.

espp::Logger::Verbosity log_level = {espp::Logger::Verbosity::WARN}

Verbosity for the internal logger.

struct State

Observer and controller state.

Public Members

float measurement = {0.0f}

Most recent measured position.

float reference = {0.0f}

Most recent reference position.

float td_reference = {0.0f}

Smoothed reference position.

float td_reference_rate = {0.0f}

Smoothed reference rate.

float z1 = {0.0f}

Estimated plant position state.

float z2 = {0.0f}

Estimated plant rate state.

float z3 = {0.0f}

Estimated total disturbance state.

float output = {0.0f}

Most recent controller output.