LCOV - code coverage report
Current view: top level - PID - PID.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 27 58 46.6 %
Date: 2026-09-09 11:02:08 Functions: 3 5 60.0 %

          Line data    Source code
       1             : /// @file       PID.cpp
       2             : /// @brief      Generic PID algorithm
       3             : 
       4             : #include <cmath>
       5             : 
       6             : #include "PID.h"
       7             : #include <AP_HAL/AP_HAL.h>
       8             : #include <AP_Math/AP_Math.h>
       9             : 
      10             : extern const AP_HAL::HAL& hal;
      11             : 
      12             : const AP_Param::GroupInfo PID::var_info[] = {
      13             : 
      14             :     // @Param: P
      15             :     // @DisplayName: PID Proportional Gain
      16             :     // @Description: P Gain which produces an output value that is proportional to the current error value
      17             :     AP_GROUPINFO_FLAGS_DEFAULT_POINTER("P",    0, PID, _kp, default_kp),
      18             : 
      19             :     // @Param: I
      20             :     // @DisplayName: PID Integral Gain
      21             :     // @Description: I Gain which produces an output that is proportional to both the magnitude and the duration of the error
      22             :     AP_GROUPINFO_FLAGS_DEFAULT_POINTER("I",    1, PID, _ki, default_ki),
      23             : 
      24             :     // @Param: D
      25             :     // @DisplayName: PID Derivative Gain
      26             :     // @Description: D Gain which produces an output that is proportional to the rate of change of the error
      27             :     AP_GROUPINFO_FLAGS_DEFAULT_POINTER("D",    2, PID, _kd, default_kd),
      28             : 
      29             :     // @Param: IMAX
      30             :     // @DisplayName: PID Integral Maximum
      31             :     // @Description: The maximum/minimum value that the I term can output
      32             :     AP_GROUPINFO_FLAGS_DEFAULT_POINTER("IMAX", 3, PID, _imax, default_kimax),
      33             : 
      34             :     AP_GROUPEND
      35             : };
      36             : 
      37         778 : float PID::get_pid(float error, float scaler)
      38             : {
      39         778 :     uint32_t tnow = AP_HAL::millis();
      40         778 :     uint32_t dt = tnow - _last_t;
      41         778 :     float output            = 0;
      42             :     float delta_time;
      43             : 
      44         778 :     if (_last_t == 0 || dt > 1000) {
      45           1 :         dt = 0;
      46             : 
      47             :                 // if this PID hasn't been used for a full second then zero
      48             :                 // the intergator term. This prevents I buildup from a
      49             :                 // previous fight mode from causing a massive return before
      50             :                 // the integrator gets a chance to correct itself
      51           1 :                 reset_I();
      52             :     }
      53         778 :     _last_t = tnow;
      54             : 
      55         778 :     delta_time = (float)dt * 0.001f;
      56             : 
      57             :     // Compute proportional component
      58         778 :     _pid_info.P = error * _kp;
      59         778 :     output += _pid_info.P;
      60             : 
      61             :     // Compute derivative component if time has elapsed
      62         778 :     if ((fabsf(_kd) > 0) && (dt > 0)) {
      63             :         float derivative;
      64             : 
      65           0 :                 if (isnan(_last_derivative)) {
      66             :                         // we've just done a reset, suppress the first derivative
      67             :                         // term as we don't want a sudden change in input to cause
      68             :                         // a large D output change                      
      69           0 :                         derivative = 0;
      70           0 :                         _last_derivative = 0;
      71             :                 } else {
      72           0 :                         derivative = (error - _last_error) / delta_time;
      73             :                 }
      74             : 
      75             :         // discrete low pass filter, cuts out the
      76             :         // high frequency noise that can drive the controller crazy
      77           0 :         float RC = 1/(2*M_PI*_fCut);
      78           0 :         derivative = _last_derivative +
      79           0 :                      ((delta_time / (RC + delta_time)) *
      80           0 :                       (derivative - _last_derivative));
      81             : 
      82             :         // update state
      83           0 :         _last_error             = error;
      84           0 :         _last_derivative    = derivative;
      85             : 
      86             :         // add in derivative component
      87           0 :         _pid_info.D = _kd * derivative;
      88           0 :         output                          += _pid_info.D;
      89             :     }
      90             : 
      91             :     // scale the P and D components
      92         778 :     output *= scaler;
      93         778 :     _pid_info.D *= scaler;
      94         778 :     _pid_info.P *= scaler;
      95             : 
      96             :     // Compute integral component if time has elapsed
      97         778 :     if ((fabsf(_ki) > 0) && (dt > 0)) {
      98           0 :         _integrator             += (error * _ki) * scaler * delta_time;
      99           0 :         if (_integrator < -_imax) {
     100           0 :             _integrator = -_imax;
     101           0 :         } else if (_integrator > _imax) {
     102           0 :             _integrator = _imax;
     103             :         }
     104           0 :         _pid_info.I = _integrator;
     105           0 :         output                          += _integrator;
     106             :     }
     107             : 
     108         778 :     _pid_info.target = output;
     109         778 :     return output;
     110             : }
     111             : 
     112             : void
     113           2 : PID::reset_I()
     114             : {
     115           2 :     _integrator = 0;
     116             :         // we use NAN (Not A Number) to indicate that the last 
     117             :         // derivative value is not valid
     118           2 :     _last_derivative = NAN;
     119           2 :     _pid_info.I = 0;
     120           2 : }
     121             : 
     122           1 : void PID::reset() {
     123           1 :     memset(&_pid_info, 0, sizeof(_pid_info));
     124           1 :     reset_I();
     125           1 : }
     126             : 
     127             : void
     128           0 : PID::load_gains()
     129             : {
     130           0 :     _kp.load();
     131           0 :     _ki.load();
     132           0 :     _kd.load();
     133           0 :     _imax.load();
     134           0 : }
     135             : 
     136             : void
     137           0 : PID::save_gains()
     138             : {
     139           0 :     _kp.save();
     140           0 :     _ki.save();
     141           0 :     _kd.save();
     142           0 :     _imax.save();
     143           0 : }

Generated by: LCOV version 1.14