|
FhSim
3.1.0
Marine systems simulation
|
| ID | 0035 |
| Class | DOCUMENTATION |
| Severity | 2 |
| Status | ready |
| Models | Trawl/PIDController |
| Found | 2026-09-22 |
src/trawl/PidController.cpp:55-60 forms the error the wrong way round:
The usual convention is reference - measurement. Read on its own, that line says the gains of any model wired to this class must be negated.
sfh::filters::PID::CalcOutputWithoutTimeshift then negates every one of the three parts (fhlib/src/filters/PID.cpp:56-64):
The two inversions cancel exactly, so the composition of the two classes is the standard convention
and a gain therefore takes the sign of the plant it drives, not the opposite of it.
Neither class says so. PidController.h's parameter table documents ProportionalGain as "Proportional gain" and stops, sfh::filters::PID has no comment on the sign at all, and the only worked example in the tree, SimplePIDController.xml, uses ProportionalGain = "1" with IntegratorGain and DerivativeGain at zero on a plant whose sign the file does not state, so it settles nothing.
Confirmed by experiment in tests/AuvClosedLoop_Test.cpp, Auv_ClosedLoopSignsFollowThePlant: with the plant signs Auv establishes — a positive RudderYaw raises the heading, a positive RudderDive lowers the pitch, a positive ThrustPower raises the speed — the three working gain sets are positive, negative and positive respectively. A uniformly negative set, which PidController.cpp:58 on its own implies, diverges on two of the three loops.
Anyone reading PidController.cpp and not following CalcOutput into fhlib concludes that the gains must be negated, and writes an XML in which two thirds of the loops run away. The AUV plan for this repository reached exactly that conclusion and specified negative gains for all three of its loops before the fixture was built.
The existing trawl models are unaffected, because their gains were found by experiment against the assembled behaviour rather than derived from the source.
The inverse error is equally available: a reader who finds the negation in fhlib first and does not notice the subtraction in PidController also lands on the wrong sign.
Documentation only. Do not change PidController.cpp:58 and do not change sfh::filters::PID: either edit silently flips the meaning of every gain in every existing trawl model, and the composition is already correct.
PidController Doxygen block, as the formula above, together with the sentence that a gain takes the sign of the plant.ProportionalGain, IntegratorGain and DerivativeGain rows of the parameter table to say that a plant whose output falls when the controller output rises needs negative gains.PidController.cpp:58 naming the second inversion in sfh::filters::PID and saying that the pair is deliberate, so the line is not "corrected" by a later reader.sfh::filters::PID::Initialize deserves the same one-line note. That part is not in this repository.Auv_ClosedLoopSignsFollowThePlant already asserts the composed sign directly: it runs a fixture whose three errors have known signs at the start and asserts the sign of each controller's first output. A pure-unit version belongs next to PIDController_Test.cpp, which today is a single regression comparison and asserts nothing about the sign: drive a Trawl/PIDController with a constant reference above a constant measurement and assert that the output of a positive-gain controller is positive.
None from documenting it. The risk is in the fix that looks obvious and is not: changing either subtraction would invert every Trawl/PIDController in every existing trawl model at once, and the trawl regressions would catch it only as an unexplained baseline diff.