AC_PosControl: compensate for velocity FF when initializing controller#32758
AC_PosControl: compensate for velocity FF when initializing controller#32758lthall wants to merge 1 commit into
Conversation
315801c to
39ed4ac
Compare
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adjusts AC_PosControl controller initialization to avoid a first-cycle transient by compensating velocity feed-forward during NE/D init, and simplifies integrator initialization accordingly.
Changes:
- Compute initial
_vel_target_*so velocity PID P and FF terms cancel on the first cycle (NE and D init paths). - Simplify NE velocity PID integrator initialization to use
_accel_target_*directly (removing FF-related compensation).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Set velocity target so that P and FF sum to zero on the first cycle: | ||
| // 0 = (target - measurement) * kP + target * kFF | ||
| // target = measurement * kP / (kP + kFF) | ||
| _vel_target_ned_ms.xy() = _vel_estimate_ned_ms.xy() * (_pid_vel_ne_m.kP() / (_pid_vel_ne_m.kP() + MAX(_pid_vel_ne_m.ff(), 0))); |
| // Set velocity target so that P and FF sum to zero on the first cycle: | ||
| // 0 = (target - measurement) * kP + target * kFF | ||
| // target = measurement * kP / (kP + kFF) | ||
| _vel_target_ned_ms.z = _vel_estimate_ned_ms.z * (_pid_vel_d_m.kP() / (_pid_vel_d_m.kP() + MAX(_pid_vel_d_m.ff(), 0))); |
| // Set velocity target so that P and FF sum to zero on the first cycle: | ||
| // 0 = (target - measurement) * kP + target * kFF | ||
| // target = measurement * kP / (kP + kFF) | ||
| _vel_target_ned_ms.xy() = _vel_estimate_ned_ms.xy() * (_pid_vel_ne_m.kP() / (_pid_vel_ne_m.kP() + MAX(_pid_vel_ne_m.ff(), 0))); |
| // Set velocity target so that P and FF sum to zero on the first cycle: | ||
| // 0 = (target - measurement) * kP + target * kFF | ||
| // target = measurement * kP / (kP + kFF) | ||
| _vel_target_ned_ms.z = _vel_estimate_ned_ms.z * (_pid_vel_d_m.kP() / (_pid_vel_d_m.kP() + MAX(_pid_vel_d_m.ff(), 0))); |
| // Set velocity target so that P and FF sum to zero on the first cycle: | ||
| // 0 = (target - measurement) * kP + target * kFF | ||
| // target = measurement * kP / (kP + kFF) | ||
| _vel_target_ned_ms.xy() = _vel_estimate_ned_ms.xy() * (_pid_vel_ne_m.kP() / (_pid_vel_ne_m.kP() + MAX(_pid_vel_ne_m.ff(), 0))); |
There was a problem hiding this comment.
We should probably ensure we don't get a divide-by-zero if (_pid_vel_ne_m.kP() + MAX(_pid_vel_ne_m.ff())) is zero
|
This looks fine to me once we add a check to avoid divide-by-zero. It would be nice to see some testing results showing the jump/no-jump before vs after. I've added @bnsgeyer in case he has a moment to review this. |
Summary
Adjust the velocity target during NE and D controller initialization so the velocity PID P and FF terms sum to zero on the first cycle, eliminating a transient bump on controller init
Simplify integrator initialization by removing the now-unnecessary FF compensation from set_integrator
Classification & Testing (check all that apply and add your own)
SITL only.
Description
When a position controller is initialized (e.g. on mode switch), the velocity target was set directly to the current velocity estimate. With a non-zero feed-forward gain, this caused a net output on the first cycle because P * (target - measurement) is zero but FF * target is not, producing a transient.
The fix sets the velocity target to:
target = measurement * kP / (kP + kFF)so that P * (target - measurement) + FF * target = 0 on the first cycle. This is applied to both the NE (horizontal) and D (vertical) init paths.
Since the velocity target now correctly accounts for FF, the integrator init simplifies to just the current acceleration target without needing to subtract the FF term.