Adding plots to an existing root locus plot

how can I add damping frequency lines constraints to an existing root locus plot

13 Comments

Be much more likely to get an answer if you would provide your present plot and a sketch of what you would like to add...the rlocusplot in Control System TB appears to be brand new; whether there was something else earlier I don't know, only have Signal Processing TB myself.
figure(1)
sys = tf([2 5 1],[1 2 3]);
rlp = rlocusplot(sys);
The example works; now to seee what happens if we try to add an arbitrary line on top of it???
hF=gcf;
hF.Children % what's the object just for grins...
ans =
RLocusPlot with properties: Responses: [1x1 controllib.chart.response.RootLocusResponse] TimeUnit: "seconds" FrequencyUnit: "rad/s" Visible: on Use GET to show all properties
hold on
hXL=xline(-2,'r'); % try to add a vertical line on existing plot...
hXL.Parent
ans =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
hL=plot([-2.5 0.5],[-1.5 1.5],'b:');
OK, those show what was asked for, but they don't belong to the rlocusplot object; another axes was created that by default overlaid the previous plot/figure.
Let's see what happens if we try explicitly...
figure(2) % create a new figure to not destroy previous
rlp = rlocusplot(sys); % make a new plot...we'll reuse same handle
hXL=xline(rlp,-2,'r'); % now try to add the vertical line onto the plot explicitly
hXL.Parent % check to see who really is the proud parent...
ans =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
Well, it doesn't crash and burn like some of the other closed specialty graphs, but it didn't put the line on the actual root locus plot, either, but again created a new axes on top and drew into it.
So, you may be able to make something work that looks like what you would like; you'll probably need to see if there's any chance linkaxes has been extended--according to the documentation, it hasn't, it still expects an array of axes handles...
hold on
hL=plot(rlp,[-2.5 0.5],[-1.5 1.5],'b:'); % one presumes this also will go on another axes
hL.Parent
ans =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
And, indeed, while plot is generous enough to not crash/reject the handle, it also doesn't add anything onto the existing rlp plot but continues on with the other axes. NOTA BENE: Without the hold on, the existing figure is erased and only the subsequent line shows up...one last interesting Q? would be if hold will accept and actually do something to an rlp object; given the behavior of plotting not erasing the existing one but creating the new axes, it's virtually certain it will have no effect and you won't be able to draw anything else on the plot itself but only draw on top of it as above to simulate having done.
linkaxes(hXL.Parent,rlp) % see if this goes boom...
Error using erase (line 10)
First argument must be text.

Error in linkaxes>valid_arguments (line 170)
invalidChars = erase(arg,{'x','y','z'});

Error in linkaxes (line 112)
if (~valid_arguments(option))
Ayup...so if you try to resize one, you'll have to have callback functions to handle the other to keep them overlaid, you won't be able to count on MATLAB doing it for you.
If you used some other tool to draw the plot above behavior may be totally different, of course...
Hi dpb,
To clear up some confusion in your post:
The rlocusplot() function is not new within the Control System Toolbox, but in R2024b the output was changed to create a chart object (RLocusPlot). This chart object contains an axes object where the chart data objects are placed. When you superimpose chart data onto a Control System Toolbox chart using "hold on", the chart data objects are parented to the axes object within the chart. No new axes are created. The "linkaxes" function you called fails because that is not valid syntax for that command. The first argument must be an array of axes objects, and the second must be the linking dimensions. However, I would be hesitant to access the internal chart axes object using hXL.Parent as you may run into unexpected behaviors.
Hi Andrew,
What unexpected behaviors might arise using hXL.Parent? Do the same concerns arise if acessing the internal chart axes via rlp.Children? Should the rlp internal chart axes not be accessed at all?
Andrew, Paul's comment above made me realize that was one thing I didn't explore earlier -- indeed, what TMW did expose as Children; I had made the assumption it would be unaccessible to the end user. So, if the conjecture thad 'hold on' did add to the existing axes, then, those two handles should match --
hChild(1)==hAx
ans = logical
1
hAx.Children
ans =
8x1 graphics array: ConstantLine Scatter (RootLocusPoleScatter) Scatter (RootLocusZeroScatter) Line (RootLocusLocusLines) Line (RootLocusLocusLines) ConstantLine ConstantLine Rectangle (RootLocusUnitCircle)
and, in fact, they do, even though the parent of the rlp object is converted from the original figure to a TiledLayoutObject by the route of 'hold on'. One could then also just directly plot into that axes by passing the handle in the calls ot any of the drawing routines/primitives.
The "syntax error" was deliberate owing to my having forgotten that zoom works on all axes in a figure (I haven't used it but once or twice in my 30+ yr w/ MATLAB) so that was a red herring, anyway.
This is indeed much improved behavior than the previous types of charts that have arisen in the forum asking for further modifications--TMW is learning as they go along, clearly. One would hope that eventually the early arrivals will also get similar treatment if they already haven't--for certain reasons I've not yet upgraded past R2022b so I can't easily check out some of the newest things locally.
Hi dpb,
The doc page for xline only allows for adding the line to an Axes object. Apparently sending the handle to an RLocusPlot as the first argument to xline doesn't throw an error, but it is undocumented AFAICT. Adding the xline to the Axes that is contained within the RLocusPlot gives the expected result (and is the more natural approach IMO).
sys = tf([2 5 1],[1 2 3]);
rlp = rlocusplot(sys);
The contained Axes is the first child of rlp
rlp.Children
ans =
4x1 graphics array: Axes Text Text Text
haxRLP = ans(1)
haxRLP =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
The contained Axes has seven children
haxRLP.Children
ans =
7x1 graphics array: Scatter (RootLocusPoleScatter) Scatter (RootLocusZeroScatter) Line (RootLocusLocusLines) Line (RootLocusLocusLines) ConstantLine ConstantLine Rectangle (RootLocusUnitCircle)
Add the ConstantLine to the Axes, not the rlp.
hXL = xline(haxRLP,-0.5,'r');
The added ConstantLine is a new child of the contained Axes
haxRLP.Children
ans =
8x1 graphics array: ConstantLine Scatter (RootLocusPoleScatter) Scatter (RootLocusZeroScatter) Line (RootLocusLocusLines) Line (RootLocusLocusLines) ConstantLine ConstantLine Rectangle (RootLocusUnitCircle)
The parent of the added ConstantLine
haxXL = hXL.Parent
haxXL =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
is the contained Axes.
isequal(haxRLP,haxXL)
ans = logical
1
Yes, our additional comments seem to have crossed in the night; I had just noted that your previous comment made me realize I had not actully checked to see about the content of rlp.Children having had a preconceived notion based on some of the earlier chart objects it would be almost completely opaque with only the few properties exposed, and certainly I didn't expect the underlying axes itself. I don't know how much my prior complaints influenced the decision, but this is clearly a quantum change and a great leap forward to allow such. I have just been having another conversation on similar visibility within the bar function where new feature/property was added but not access to the underlying text objects which the new property uses. This appears to be a case where TMW internal review prevails depending upon their assessment of how much exposing the inner object handles will engender supporting issues owing to users being able to make changes that might cause internal errors and implementation difficulties/details.
I was going to comment on your earlier question about @Andrew Ouellette's concern about using the parent handle that I think it's immaterial which way one goes at it, whether the end result ended up as the same axes as it has now been shown to be or another as I was confused into thinking by the change in parents and that I had simply expected the original axes handle to be hidden by TMW deliberately based on prior charts.
If you have a valid handle to an axes, then using it in the graphics primitives is certainly valid; if it turns out ot be the same object found by two different searches; which reference is used is immaterial; either resolves to the same end object. Hence, I think his concern is unfounded...
Regarding this statement in a previous comment:
"the parent of the rlp object is converted from the original figure to a TiledLayoutObject by the route of 'hold on'. "
sys = tf([2 5 1],[1 2 3]);
rlp is a RLocusPlot
rlp = rlocusplot(sys)
rlp =
RLocusPlot with properties: Responses: [1x1 controllib.chart.response.RootLocusResponse] TimeUnit: "seconds" FrequencyUnit: "rad/s" Visible: on Use GET to show all properties
Parent of rlp is a figure;
rlp.Parent
ans =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [1628 1512 583 437] Units: 'pixels' Use GET to show all properties
Use hold on
hold on
The parent of the rlp is still a figure, were you seeing otherwise?
rlp.Parent
ans =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [1628 1512 583 437] Units: 'pixels' Use GET to show all properties
However, there does seem to be an inconsistency.
The first child of the rlp is the Axes
haxRLP = rlp.Children(1)
haxRLP =
Axes with properties: XLim: [-2.5000 0.5000] YLim: [-1.5000 1.5000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
So the parent of the Axes should be the RLocusPlot (rlp). But it's a TiledChartLayout (which is the case even if "hold on" is not executed)
haxRLP.Parent
ans =
TiledChartLayout with properties: TileArrangement: 'fixed' GridSize: [1 1] Padding: 'loose' TileSpacing: 'compact' Use GET to show all properties
@Andrew Ouellette, how can the child of a parent have a different parent?
@Paul, My initial post did make a shortcut presumption without explicit testing that it was executing hold on that triggered the change in parent to show the TiledChartLayout object instead of Figure (1); I didn't actually do the call back without having already done hold on (since I don't have the TB locally and it's rather painful to try to do really detailed exploration in the forum environment).
It's clear that the RLocusPlot object is complex, that the top level parent of the embedded axes resolves to a tiled layout is interesting, indeed, as noted, lacking the TB locally, I presumed it was the "hold on" getting ready to do something more that caused the behavior.
I'm satisfied about the axes itself being the same one, but w/o local access it's too painful to probe even deeper/more carefully. Can you maybe then eventually resolve that the parent of the TiledLayout is the Figure at the very top of the food chain?
ADDENDUM:
Actually, can show that w/o the actual rlp object itself...
h=tiledlayout(1,1)
h =
TiledChartLayout with properties: TileArrangement: 'fixed' GridSize: [1 1] Padding: 'loose' TileSpacing: 'loose' Use GET to show all properties
h.Parent
ans =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [1628 1512 583 437] Units: 'pixels' Use GET to show all properties
gcf==h.Parent
ans = logical
1
Here's the best I have, and it's still confusing.
Create the RLocusPlot object
rlp = rlocusplot(tf(1,[1 2 1]));
The figure is a child of the root.
hfig = get(0,'children')
hfig =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [1628 1512 583 437] Units: 'pixels' Use GET to show all properties
The rlp is the only child of the figure.
hfig.Children
ans =
RLocusPlot with properties: Responses: [1x1 controllib.chart.response.RootLocusResponse] TimeUnit: "seconds" FrequencyUnit: "rad/s" Visible: on Use GET to show all properties
isequal(rlp,hfig.Children)
ans = logical
1
The rlp has four (apparently) children.
rlp.Children
ans =
4x1 graphics array: Axes Text Text Text
The axes is the first child of the rlp
hAX = rlp.Children(1)
hAX =
Axes with properties: XLim: [-1.2000 0.2000] YLim: [-0.6000 0.6000] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1401 0.7750 0.7611] Units: 'normalized' Use GET to show all properties
Even though the axes is a child of the rlp, it thinks its parent is a TiledChartLayout.
hTL = hAX.Parent
hTL =
TiledChartLayout with properties: TileArrangement: 'fixed' GridSize: [1 1] Padding: 'loose' TileSpacing: 'compact' Use GET to show all properties
That parent of the TiledChartLayout is the rlp, even though it wasn't shown above as a child of the rlp.
hTL.Parent
ans =
RLocusPlot with properties: Responses: [1x1 controllib.chart.response.RootLocusResponse] TimeUnit: "seconds" FrequencyUnit: "rad/s" Visible: on Use GET to show all properties
And the parent of the parent of the TiledChartLayout is the original figure.
hTL.Parent.Parent
ans =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [1628 1512 583 437] Units: 'pixels' Use GET to show all properties
isequal(hfig,hTL.Parent.Parent)
ans = logical
1
Maybe (likely) there are aspects of the handle graphics system that I don't understand, but this hierarchy doesn't make sense to me.
f=which('-all','rlocusplot');
M=readlines(f{:});
M(contains(M,'chart','ignorecase',1))
ans = 3x1 string array
" % Check to create stepplot using control charts" " h = controllib.chart.internal.utils.ltiplot("rlocus",hParent,..." " ax = controllib.chart.internal.utils.getEntryAxesForChart(hParent);"
M(contains(M,'tiledlayout','ignorecase',1))
ans = 0x1 empty string array
The logic involved is pretty complex and uses Mathworks internals routines that are not m-files so that one can't get into the complete internals. The above shows that the m-code doesn't directly call tiledlayout, but clearly somewhere in the bowels it was/is, since it turns out that's what the end object axes turns out to belong to in the end.
I also don't follow why, exactly, the two parents are resolved differently; that's buried somewhere in those hidden pieces.
The short answer is that the Children property of RLocusPlot is not documented, so do not expect any sort of behavior from it- it does not function like the Children property of other graphics objects like figures or axes. The closest thing that RLocusPlot has to the traditional Children property is its Responses property, which allows you to modify the data and styling for the root locus responses contained in the chart.
As for the pitfalls with interacting with the internal axes handle, you will find that many interactions work how you would expect. However, the RLocusPlot object is generally unaware of changes made to its internal axes that it itself did not make. You may find that the object stomps over some of your customizations during its updates, or you could place the chart into an invalid state (e.g. by deleting the axes handle). Additionally, you will find that your customizations will not be reflected in the Live Editor or after loading a saved figure containing a RLocusPlot object.
Fair enough. One takes one's chances when using undocumented features.
dpb
dpb on 4 Nov 2024
Edited: dpb on 4 Nov 2024
There still has to be a defineable reason for the particular behavior, undocumented or not, though, persuing which was the direction of this sidebar conversation.
It simply can't be relied upon to remain that way going forward nor even be assured the code won't do the apocryphal "start WW III" if one mucks around with those undocumented properties--trashing the figure or even crashing MATLAB at the extreme.
But, while the internals are somewhat interesting, it isn't of any real significance to the use of the object and the implementation is far improved over many of the earlier similar canned charts in exposing internals such as the axes handle even if some internal features are unsupported by making other user modfications.
I, personally, commend TMW on the decision to have done so, even though I don't have the particular TB and my days of control systems are far behind me; hopefully, the direction will carry over to new such specialty graphics as they are introduced and also make its way back into earlier ones that weren't so user friendly.

Sign in to comment.

Answers (2)

Hello,
Design requirements (constraints) for damping ratios and natural frequencies in root locus charts is only available within the Control System Designer app currently. You cannot import a root locus chart directly into the app, but you can import the same system you used to generate the root locus and design requirements at that point.
If you are simply looking to see grid lines with constant damping factors and natural frequencies, you have a few options:
1) After creating your root locus chart, you can open a context menu by right clicking on the chart axes. In that menu, you can select the Grid option to turn on the chart's grid.
2) Starting in R2024b, you can turn on the chart's grid via the chart API.
sys = rss(4);
rlp = rlocusplot(sys);
rlp.AxesStyle.GridVisible = true;
3) You can also explore the sgrid() or zgrid() functions to customize the damping factors and natural frequencies shown in the grid.

Categories

Find more on Graphics in Help Center and File Exchange

Asked:

on 30 Sep 2024

Edited:

dpb
on 4 Nov 2024

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!