@@ -111,6 +111,11 @@ void Pid::reset(bool save_i_term)
111111 d_error_ = 0.0 ;
112112 cmd_ = 0.0 ;
113113
114+ d_error_last_ = 0 ;
115+ d_term_last_ = 0 ;
116+ error_last_ = 0 ;
117+ aw_term_last_ = 0 ;
118+
114119 // Check to see if we should reset integral error here
115120 if (!save_i_term)
116121 {
@@ -121,7 +126,11 @@ void Pid::reset(bool save_i_term)
121126 gains_ = gains_box_.get ();
122127}
123128
124- void Pid::clear_saved_iterm () { i_term_ = 0.0 ; }
129+ void Pid::clear_saved_iterm ()
130+ {
131+ i_term_ = 0.0 ;
132+ i_term_last_ = 0.0 ;
133+ }
125134
126135void Pid::get_gains (
127136 double & p, double & i, double & d, double & u_max, double & u_min,
@@ -332,22 +341,16 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
332341 else if (gains_.tf_ > 0.0 && gains_.d_method_ == " trapezoidal" )
333342 {
334343 // Derivative filter is on
335- d_term = ((2 * gains_.tf_ + dt_s) * d_term_last_ +
344+ d_term = ((2 * gains_.tf_ - dt_s) * d_term_last_ +
336345 (gains_.d_gain_ * dt_s * (d_error_ + d_error_last_))) /
337346 (2 * gains_.tf_ + dt_s);
338347 }
339- else if (
340- gains_.tf_ == 0.0 &&
341- (gains_.d_method_ == " forward_euler" || gains_.d_method_ == " backward_euler" ))
348+ else if (gains_.tf_ == 0.0 )
342349 {
343- // Derivative filter is off. Since forward doesn't exist for tf=0, use backward
350+ // Derivative filter is off. Since forward doesn't exist for tf=0, use backward.
351+ // To avoid artificial states and amplify noise, trapezoidal also falls back to backward.
344352 d_term = gains_.d_gain_ * d_error_;
345353 }
346- else if (gains_.tf_ == 0.0 && gains_.d_method_ == " trapezoidal" )
347- {
348- // Derivative filter is off
349- d_term = (-dt_s * d_term_last_ + (gains_.d_gain_ * dt_s * (d_error_ + d_error_last_))) / (dt_s);
350- }
351354
352355 if (gains_.antiwindup_strat_ .type == AntiWindupStrategy::UNDEFINED)
353356 {
@@ -358,6 +361,60 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
358361 const bool is_error_in_deadband_zone =
359362 control_toolbox::is_zero (error, gains_.antiwindup_strat_ .error_deadband );
360363
364+ if (gains_.i_method_ == " forward_euler" )
365+ {
366+ i_term_ = gains_.i_gain_ * dt_s * error_last_;
367+ }
368+ else if (gains_.i_method_ == " backward_euler" )
369+ {
370+ i_term_ = gains_.i_gain_ * dt_s * error;
371+ }
372+ else if (gains_.i_method_ == " trapezoidal" )
373+ {
374+ i_term_ = gains_.i_gain_ * (dt_s * 0.5 ) * (error + error_last_);
375+ }
376+ else
377+ {
378+ throw std::runtime_error (" Pid: invalid integral method" );
379+ }
380+
381+ double i_proposed = i_term_last_ + i_term_;
382+ if (gains_.antiwindup_strat_ .type == AntiWindupStrategy::CONDITIONAL_INTEGRATION)
383+ {
384+ // testa se o incremento empurra a saturação
385+ const bool have_limits = std::isfinite (gains_.u_min_ ) || std::isfinite (gains_.u_max_ );
386+ if (have_limits)
387+ {
388+ // commando não-saturado candidato usando i_proposed
389+ const double cmd_unsat_candidate = p_term + d_term + i_proposed;
390+
391+ const bool sat_high = std::isfinite (gains_.u_max_ ) && (cmd_unsat_candidate > gains_.u_max_ );
392+ const bool sat_low = std::isfinite (gains_.u_min_ ) && (cmd_unsat_candidate < gains_.u_min_ );
393+
394+ const bool pushing_high = sat_high && (i_term_ > 0.0 );
395+ const bool pushing_low = sat_low && (i_term_ < 0.0 );
396+
397+ if (pushing_high || pushing_low)
398+ {
399+ i_term_ = i_term_last_; // congela
400+ }
401+ else
402+ {
403+ i_term_ = i_proposed; // aceita
404+ }
405+ }
406+ else
407+ {
408+ i_term_ = i_proposed; // sem limites -> aceita
409+ }
410+ }
411+ else
412+ {
413+ i_term_ = i_proposed; // sem CI -> integra normalmente (AW vem depois)
414+ }
415+
416+ i_term_ = std::clamp (i_term_, gains_.i_min_ , gains_.i_max_ );
417+
361418 // Compute the command
362419 cmd_unsat_ = p_term + i_term_ + d_term;
363420
@@ -381,10 +438,30 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
381438 {
382439 if (
383440 gains_.antiwindup_strat_ .type == AntiWindupStrategy::BACK_CALCULATION &&
384- !is_zero (gains_.i_gain_ ))
441+ !is_zero (gains_.i_gain_ ) && gains_.i_method_ == " forward_euler" )
442+ {
443+ i_term_ += dt_s * 1 / gains_.antiwindup_strat_ .tracking_time_constant * (cmd_ - cmd_unsat_);
444+ }
445+ else if (
446+ gains_.antiwindup_strat_ .type == AntiWindupStrategy::BACK_CALCULATION &&
447+ !is_zero (gains_.i_gain_ ) && gains_.i_method_ == " backward_euler" )
385448 {
386- i_term_ += dt_s * (gains_.i_gain_ * error +
387- 1 / gains_.antiwindup_strat_ .tracking_time_constant * (cmd_ - cmd_unsat_));
449+ i_term_ = i_term_ / (1 + dt_s / gains_.antiwindup_strat_ .tracking_time_constant ) +
450+ dt_s / gains_.antiwindup_strat_ .tracking_time_constant * (cmd_ - p_term - d_term) /
451+ (1 + dt_s / gains_.antiwindup_strat_ .tracking_time_constant );
452+ }
453+ else if (
454+ gains_.antiwindup_strat_ .type == AntiWindupStrategy::BACK_CALCULATION &&
455+ !is_zero (gains_.i_gain_ ) && gains_.i_method_ == " trapezoidal" )
456+ {
457+ const double num_i_last =
458+ (1.0 - dt_s / (2.0 * gains_.antiwindup_strat_ .tracking_time_constant )) * i_term_last_;
459+ const double trap_inc = gains_.i_gain_ * (dt_s * 0.5 ) * (p_error_ + error_last_);
460+ const double aw_sum = (cmd_ - (p_term + d_term)) + aw_term_last_;
461+ const double denom = (1.0 + dt_s / (2.0 * gains_.antiwindup_strat_ .tracking_time_constant ));
462+ i_term_ = (num_i_last + trap_inc +
463+ (dt_s / (2.0 * gains_.antiwindup_strat_ .tracking_time_constant )) * aw_sum) /
464+ denom;
388465 }
389466 else if (gains_.antiwindup_strat_ .type == AntiWindupStrategy::CONDITIONAL_INTEGRATION)
390467 {
@@ -393,17 +470,13 @@ double Pid::compute_command(double error, double error_dot, const double & dt_s)
393470 i_term_ += dt_s * gains_.i_gain_ * error;
394471 }
395472 }
396- else if (gains_.antiwindup_strat_ .type == AntiWindupStrategy::NONE)
397- {
398- // No anti-windup strategy, so just integrate the error
399- i_term_ += dt_s * gains_.i_gain_ * error;
400- }
401473 }
402474
403- i_term_ = std::clamp (i_term_, gains_.i_min_ , gains_.i_max_ );
404-
405475 d_term_last_ = d_term;
406476 d_error_last_ = d_error_;
477+ i_term_last_ = i_term_;
478+ aw_term_last_ = cmd_ - p_term - d_term;
479+ error_last_ = error;
407480
408481 return cmd_;
409482}
0 commit comments