Repository navigation
Root locus: relative step-size control, improper systems, default gain and plot limits - #1079
Conversation
The adaptive step-size control of `getpoles` accepted a step if the absolute distance moved by the poles was below `tol`. The number of steps was thus proportional to the distance the poles travel, which for systems with poles of large magnitude, or with a time unit that is small in relation to the dynamics, led to tens of millions of steps and exhausted the memory. The distance is now measured relative to the magnitude of each pole, bounded from below by a fraction of the smallest nonzero magnitude among the open-loop poles, the zeros and the poles at the final gain. The number of steps thus grows logarithmically with the distance travelled and is independent of the time unit. The pole matrices are now formed with `reduce(hcat, ...)` instead of splatting, and transposed with `transpose` instead of the adjoint, which conjugated all returned poles. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
For an improper transfer function `P/Q`, the closed-loop system has `degree(P)` poles for k > 0 while the open-loop system has `degree(Q)` poles. The pole matrices were sized from the denominator degree, which caused a `DimensionMismatch`. They are now sized from the larger of the two degrees. The adaptive method starts the locus at a gain k0 > 0, determined from Rouché's theorem, at which the additional poles have a magnitude larger than ten times that of the open-loop poles and zeros and the remaining poles are close to the open-loop poles. The plot recipes mark the open-loop poles of the system rather than the first row of the root matrix, which for an improper system also contains the additional poles. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
This is an automated message.
|
|
This is an automated message.
|
… poles and zeros The default maximum gain `K = 500` and the plot limits of ±50 depended on the time unit and on the gain of the system. The default gain is now the smallest gain at which, according to Rouché's theorem, the poles that tend to infinity have a magnitude larger than ten times the largest magnitude ω0 among the open-loop poles and zeros, and the remaining poles are within one percent of the zeros. The default plot limits contain the open-loop poles and zeros, the origin, and all points of the locus with a magnitude of at most 2ω0. For discrete-time systems, ω0 is at least 1, the limits contain the unit circle, and the unit circle is drawn. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
This is an automated message.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1079 +/- ##
==========================================
+ Coverage 91.70% 91.73% +0.02%
==========================================
Files 42 42
Lines 5796 5865 +69
==========================================
+ Hits 5315 5380 +65
- Misses 481 485 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Claude-Session: https://claude.ai/code/session_01HigNnun4aJKMoWmY7wyULn Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
















Problem
rlocuson the following system did not terminate in reasonable time and exhausted the memory:The adaptive step-size control in
getpolesaccepted a step if the sum of the absolute distances moved by the poles was belowtol(1e-2per pole). The number of steps was thus proportional to the distance the poles travel. For this system the open-loop poles have magnitude ≈ 500 and at the defaultK = 500the closed-loop poles are located at ≈ -438 ± 3.4e5im, which requires on the order of 3e7 steps (1.4e6 steps already forK = 1). Each step allocated a vector, and the result was finally assembled withhcat(poleout_list...), splatting all steps as arguments.Change
1e-3times the smallest nonzero magnitude among the open-loop poles, the zeros and the closed-loop poles at the final gain (magnitudes belowsqrt(eps)times the largest are regarded as zero, since a root at the origin is computed with a rounding error). The number of steps then grows logarithmically with the distance travelled and is independent of the time unit of the system. This applies to both the scalar-gain and the matrix-gain variants ofgetpoles.reduce(hcat, ...)instead of splatting.transposeinstead of'. The adjoint conjugated all returned poles; this was not visible in plots since the locus is symmetric about the real axis.tolandinitial_stepsizeis documented in the docstring ofrlocus.Effect
K = 500tf([1/5,2],[1,1,1]),K = 200K = 200tf(1, [1,0,0]),K = 200tf(1, [1,1001,1000]),K = 200double_mass_model, state feedbackThe double-mass model requires more steps than before since it has a pole at the origin, which is now resolved on a relative scale as it departs from the origin.
Tests are added for the system above, including a check that the number of steps and the (scaled) poles are unchanged when the time unit is changed from s to ms.
Improper transfer functions
For an improper transfer function
P/Q, the closed-loop system hasdegree(P)poles fork > 0while the open-loop system hasdegree(Q)poles. Both SISO methods ofgetpolessized the pole matrices from the denominator degree and failed with aDimensionMismatch. They are now sized from the larger of the two degrees.The adaptive method starts the locus at a gain
k0 > 0(K[1] = k0) instead of approximatingk = 0byk = eps(), which placed the additional poles at a magnitude of ≈ 1e16.k0follows from Rouché's theorem: ifk|P(s)| < |Q(s)|on a closed contour,Q + kPhas as many roots inside the contour asQ. The contour consists of a circle of radius ten times the largest magnitude among the open-loop poles and zeros and of circles of radius0.01|p|around each open-loop polep, so that atk0the additional poles are outside the large circle and the remaining poles are close to the open-loop poles. The method with a vector of gains retains the approximationk = eps()fork = 0.The Plots and Makie recipes mark the open-loop poles by
poles(sys)rather than by the first row of the root matrix, which for an improper system also contains the additional poles.Default gain and plot limits
The default maximum gain
K = 500and the plot limits of ±50 in the Plots recipe depended on the time unit and on the gain of the system. For the system above, the locus was entirely outside the plotted region.rlocus(P),rlocusplot(P),CSMakie.rlocusplot(P),K = nothing): with ω0 the largest magnitude among the open-loop poles and zeros (at least 1 in discrete time), the default gain is the maximum of|Q(s)/P(s)|over a circle of radius 10ω0 (ifdegree(Q) > degree(P)) and over circles of radius0.01max(|z|, 0.01ω0)around each zeroz. By Rouché's theorem, at this gain the poles that tend to infinity are located outside the large circle and the remaining poles inside the small circles. The default gain is invariant to a change of the time unit and inversely proportional to the gain of the system. This changes the output ofrlocus(P)without a gain argument.ControlSystemsBase.rlocus_limits, used by both the Plots and the Makie recipe): the limits contain the open-loop poles and zeros, the origin, the closed-loop poles at the final gain if the gain is a matrix, and all points of the locus with a magnitude of at most 2ω0. They are symmetric about the real axis and enlarged by 10 %. For discrete-time systems, the limits contain the unit circle, which is drawn as a dashed line.Automatic limits are not a substitute for these limits: since the default gain places the poles that tend to infinity at a magnitude of at least 10ω0, automatic limits are determined by these branches rather than by the open-loop poles and zeros.
tf([1/5,2],[1,1,1])1/(s(s+1)(s+2))(-s+1)/(s²+s+1)zpk([-0.5], [1, 0.6], 0.1, 0.1)(discrete)Host: demeter6, session 6ab4068b-a603-44bd-8c0f-82dee93c0177, working directory
~/.julia/dev/ControlSystems/.claude/worktrees/generic-pondering-babbage🤖 Generated with Claude Code