Automated Workflows for System Modeling and Controller Design

Suba Thomas, Wolfram Research
Sergio Vargas, Wolfram Research

(RowDefault[, (Spacer1), , (Spacer1), ])  #WolframTechConf

ABSTRACT

The Wolfram Language has been enhanced with several features to enable better integration of system modeling and to automate and streamline the
subsequent controller design and simulation. These improvements include
the capability to work directly with models created in System Modeler,
compute controllers for both regulation and tracking, automatically
assemble closed-loop systems, generate controllers for deployment to
hardware and much more. This talk will present these developments along
with a System Modeler application example demonstrating the automated
workflow.

Motivation

A regulating controller

ssm=StateSpaceModel[x'[t]==x[t]+u[t],x[t],u[t],x[t],t]

OutputResponse[{ssm,1/10},1,t]
Limit[%,t->Infinity]

u[t]==-κ x[t]+(OverBar[u])[t] 		⟹ 		 x'[t]=x[t]-κ x[t]+(OverBar[u])[t]

κ=StateFeedbackGains[ssm,{-3}]

csys=SystemsModelStateFeedbackConnect[ssm,κ]

OutputResponse[{csys,1/10},1,t]
Limit[%,t->Infinity]

A tracking controller

ssm=StateSpaceModel[x'[t]== x[t]+u[t],x[t],u[t],x[t],t]

u[t]==-(Subscript[κ, x]).x[t]-(Subscript[κ, e]).∫e[t]dt  ⟹  (({{e[t]}, {x'[t]}}))=(({{0, -1}, {-(Subscript[κ, e]), 1-(Subscript[κ, x])}})).(({{∫e[t]dt}, {x[t]}}))+(({{1}, {0}}))(Subscript[y, ref])[t]

κ=StateFeedbackGains[<|"InputModel"->ssm,"TrackedOutputs"->1|>,{-3,-5}]

SystemsModelStateFeedbackConnect[ssm,{{κ[[1,1]]}}];
SystemsModelSeriesConnect[TransferFunctionModel[-κ[[1,2]]/s,s],%];
csys=SystemsModelFeedbackConnect[%]

OutputResponse[{csys,1/10},γ,t]//Expand
Limit[%,t->Infinity]

Closed-loop system layouts

The automated workflow

A regulating controller

ssm=StateSpaceModel[x'[t]==x[t]+ u[t],x[t],u[t],x[t],t]

csys=StateFeedbackGains[ssm,{-3},"ClosedLoopSystem"]

OutputResponse[{csys,1/10},1,t]
Limit[%,t->Infinity]

A tracking controller

ssm=StateSpaceModel[x'[t]==x[t]+u[t],x[t],u[t],x[t],t]

csys=StateFeedbackGains[<|"InputModel"->ssm,"TrackedOutputs"->1|>,{-3,-5},"ClosedLoopSystem"]

OutputResponse[{csys,1/10},γ,t]//Simplify//Expand
Limit[%,t->Infinity]

Workflow

The controller data object

csys=StateFeedbackGains[(StateSpaceModelFull[1, 1, 1, 0, , ,{{x[t],0}},{{u[t],0}},Automatic,t, ]),{-3},"ClosedLoopSystem"]

κ=StateFeedbackGains[(StateSpaceModelFull[1, 1, 1, 0, , ,{{x[t],0}},{{u[t],0}},Automatic,t, ]),{-3},"FeedbackGains"]

𝒸𝒹=StateFeedbackGains[(StateSpaceModelFull[1, 1, 1, 0, , ,{{x[t],0}},{{u[t],0}},Automatic,t, ]),{-3},"Data"]

𝒸𝒹["ClosedLoopSystem"]

𝒸𝒹["FeedbackGains"]

𝒸𝒹[{"ClosedLoopSystem","FeedbackGains"}]

𝒸𝒹[{"ClosedLoopSystem",<|"Merge"->False|>}]

𝒸𝒹[{{"ClosedLoopSystem",<|"Merge"->False|>},"FeedbackGains"}]

𝒸𝒹["PropertyAssociation"]

𝒸𝒹["Dataset"]

smcd=SystemsModelControllerData[];

props=smcd["Properties"]

RandomChoice[props]
smcd[%]

Designing an output feedback tracking controller

Open-loop system

sys=(AffineStateSpaceModelFull[(Subscript[ω, 1]), ((-g l m1 Sin[(Subscript[θ, 1])]-(a^2) k Sin[(Subscript[θ, 1])-(Subscript[θ, 2])])/((l^2) m1)), (Subscript[ω, 2]), (((a^2) k Sin[(Subscript[θ, 1])-(Subscript[θ, 2])]-g l m2 Sin[(Subscript[θ, 2])])/((l^2) m2)), 0, 0, ((Cos[(Subscript[θ, 1])])/(l m1)), 0, 0, 0, 0, ((Cos[(Subscript[θ, 2])])/(l m2)), (Subscript[θ, 1]), (Subscript[ω, 1]), (Subscript[ω, 2]), 0, 0, 0, 0, 0, 0, (Subscript[θ, 1]), (Subscript[ω, 1]), (Subscript[θ, 2]), (Subscript[ω, 2]), \[Null], \[Null], \[Null], d, f, \[Null], {d,f}Automatic, ])/.{m1->1,m2->1,l->1,g->9.8, a-> 0.5, k-> 10000}

Plot[10 UnitBox[5t-0.5],{t,-1,1},Exclusions->None]

srOL=StateResponse[sys,{10 UnitBox[5t-0.5],0},{t,0,5}];

Plot[#,{t,0,5},]&/@ srOL

Tracker

sspec=<|"InputModel"->sys,"FeedbackInputs"->2,"TrackedOutputs"->1,"TrackedSignal"->Function[{r,t},r''[t]+r[t]]|>;

J=(∫_0^∞)(10^4)(((Subscript[(UnderBar[θ]), 1])[t])^2)+(10^2)(((Subscript[(UnderBar[ω]), 1])[t])^2)+10(((Subscript[(UnderBar[θ]), 2])[t])^2)+10(((Subscript[(UnderBar[ω]), 2])[t])^2)+(10^4)((e[t])^2)+10 ((e'[t])^2)+(((UnderBar[f])[t])^2)dt

wts=DiagonalMatrix/@ {{(10^2),10,(10^2),10 ,(10^4),10},{1}};
MatrixForm/@wts

𝒸𝒹T=LQRegulatorGains[sspec,wts,"Data"]

csysT=𝒸𝒹T["ClosedLoopSystem"]

(AffineStateSpaceModelFull[(Subscript[ω, 1]), -9.8` Sin[(Subscript[θ, 1])]-2500.` Sin[(Subscript[θ, 1])-(Subscript[θ, 2])], (Subscript[ω, 2]), 2500.` Sin[(Subscript[θ, 1])-(Subscript[θ, 2])]-9.8` Sin[(Subscript[θ, 2])]+Cos[(Subscript[θ, 2])] ((Subscript[\[FormalX], 1])-((260095226 (Subscript[θ, 1]))/212647729)-((442899149 (Subscript[θ, 2]))/16382757)-((43108709 (Subscript[ω, 1]))/12111564)-((51722963 (Subscript[ω, 2]))/6461891)), 0.` +(Subscript[\[FormalX], 2])-83.64958080202994` (Subscript[θ, 1]), 0.` -(Subscript[\[FormalX], 1])-54.888501816317834` (Subscript[θ, 1]), 0, 0, Cos[(Subscript[θ, 1])], 0, 0, 0, 0, 0, 0, 83.64958080202994`, 0, 54.888501816317834`, (Subscript[θ, 1]), (Subscript[ω, 1]), (Subscript[ω, 2]), 0, 0, 0, 0, 0, 0, (Subscript[θ, 1]), (Subscript[ω, 1]), (Subscript[θ, 2]), (Subscript[ω, 2]), (Subscript[\[FormalX], 1]), (Subscript[\[FormalX], 2]), \[Null], \[Null], \[Null], d, " ", \[Null], {d,(Subscript[\[FormalU], 1])}Automatic, ])

srT=StateResponse[csysT,{5 UnitBox[5t-0.5],10 ° Sin[t]},{t,0,25}];
srTPlots=Plot[#,{t,0,25},]&/@ srT

Legended[Show[srTPlots[[1]],Plot[10 ° Sin[t],{t,0,25},PlotStyle->ColorData[97,2]]],LineLegend[ColorData[97,#]&/@{1,2},{"actual","ref."}]]

cmT=𝒸𝒹T["ControllerModel"]

ceT=OutputResponse[cmT,Join[{10 ° Sin[t],srT[[1]]},srT],{t,0,25}];
Plot[ceT,{t,0,25},]

Tracker with estimator

𝒸𝒹T["ClosedLoopPoles"]

ePoles={-8+I,-8-I,-10,-11};

ℓ=EstimatorGains[{sys,{2,3}},ePoles]

𝒸𝒹ET=EstimatorRegulator[<|"InputModel"->sys,"FeedbackInputs"->2,"TrackedOutputs"->1,"MeasuredOutputs"->{2,3}|>,{ℓ,𝒸𝒹T},"Data"]

SortBy[𝒸𝒹ET["ClosedLoopPoles"]//Chop,Re]
SortBy[Join[ePoles,𝒸𝒹T["ClosedLoopPoles"]],Re]
ContainsExactly[%,%%,SameTest->(Chop[#1 - #2, (10^(-4))] == 0. &)]

csysET=𝒸𝒹ET["ClosedLoopSystem"]//N//Chop

srET=StateResponse[csysET,{5 UnitBox[5t-0.5],10° Sin[t]},{t,0,50}];
srETPlots=Plot[#,{t,0,20},]&/@ srET

Legended[Show[srETPlots[[1]],Plot[10 ° Sin[t],{t,0,20},PlotStyle->ColorData[97,2]]],LineLegend[ColorData[97,#]&/@{1,2},{"actual","ref."}]]

cmET=𝒸𝒹ET["ControllerModel"]

OutputResponse[cmET,Join[{10° Sin[t]},srET[[{2,4}]]],{t,0,20}];
Plot[%,{t,0,20},]

Noisy measurements

csysNET=𝒸𝒹ET[{"ClosedLoopSystem",<|"NoisyOutputs"->{2,3} |>}]

sNoise=Table[Interpolation[Thread[{Range[0,50],RandomReal[NormalDistribution[0,0.001],{51}]}],t],{2}];

Plot[sNoise,{t,0,50},]

srNET=StateResponse[csysNET,Join[{5 UnitBox[5t-0.5],10° Sin[t]},sNoise],{t,0,50}];
Plot[#,{t,0,20},]&/@ srNET

OutputResponse[cmET,Join[{5 UnitBox[5t-0.5]},srNET[[{2,4}]]+sNoise],{t,0,50}];
Plot[%,{t,0,20},]

Working with System Modeler

SystemModeler[]

SystemModel

modelDC=SystemModel["Modeling.InvertedPendulum.Components.DCMotor"]

SystemModelLinearize[modelDC]

SystemModelPlot[modelDC,{"inertia.w"}]

PIDTune[modelDC]

Workflows: Regulator for an electrical motor + gear + pendulum

Creating a closed loop model

modelMGP=;

SystemModeler[modelMGP,"Modeling"]

linMGP=SystemModelLinearize[modelMGP]

sspecMGP=<|"InputModel"->linMGP,"FeedbackInputs"->{"V"}|>;
ctrlMGP=StateFeedbackGains[sspecMGP,{-2784.4,-4.6,-4.1,-1.2,-0.2},"Data"]

csysMGP=ConnectSystemModelController[modelMGP,ctrlMGP]

Perturbing the system

simMGP=SystemModelSimulate[csysMGP,40,<|"Inputs"->{"d"->Function[t,(E^(-(1/2) (((-5+t))^2)))+(E^(-(1/2) (((-15+t))^2)))],"V"->Function[t,0]}|>];

SystemModelPlot[simMGP,{"syse.sys.d"},PlotLegends->{"Disturbance"},PlotRange->All]

SystemModelPlot[simMGP,{"syse.sys.pendulumDamper.phi_rel"},PlotLegends->{"Pendulum angle"},PlotRange->All]

SystemModelPlot[simMGP,{"syse.sys.voltageSource.v"},PlotLegends->{"Motor input voltage"},PlotRange->All]

Workflows: Regulator for a continuous stirred-tank reactor

Creating a closed loop model

modelCSTR=;

linCSTR=SystemModelLinearize[modelCSTR]

ctrlCSTR=LQGRegulator[linCSTR,{{{}},(({{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}))},{(({{1, 0, 0}, {0, 1, 0}, {0, 0, 1}})),(({{1}}))},"Data"]

csysCSTR=ConnectSystemModelController[modelCSTR,ctrlCSTR]

Perturbing the system

simCsysCSTR=SystemModelSimulate[csysCSTR,(Quantity[8, "h", hours, "Hours"]),<|"InitialValues"->{"sys.x1"->-1000}|>];

SystemModelPlot[simCsysCSTR,{"deltaCA"},PlotRange->All,TargetUnits->{"Hours",None}]

Workflows: Tracker for a ball on top of a beam

Creating a closed loop model

modelBB=;

linBB=SystemModelLinearize[modelBB]

sspecBB=<|"InputModel"->linBB,"TrackedOutputs"->{"rs"}|>;
ctrlBB=LQGRegulator[sspecBB,{{{}},(({{(10^(-5)), 0}, {0, (10^(-5))}}))},{(10^4)(({{1, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, 0, 1, 0, 0}, {0, 0, 0, 1, 0}, {0, 0, 0, 0, 1}})),(({{1}}))},"Data"]

csysBB=ConnectSystemModelController[modelBB,ctrlBB]

Providing a reference

simBB=SystemModelSimulate[csysBB,100,<|"Inputs"->{"ref_rs"->Function[t,(1/2)(E^(-(1/8) (((-10+t))^2)))-(1/2)(E^(-(1/8) (((-30+t))^2)))],"noise_rs"->Function[t,0],"noise_thetab"->Function[t,0]}|>];

SystemModelPlot[simBB,{"ref_rs","rs"},PlotRange->All]

SystemModelPlot[simBB,{"sys.T"},PlotRange->All]

Animating a ball and beam party

nSystems=6;
pVals=Flatten@Table[{"bb"<>ToString[k]<>".sys.rotation.r"->{0,0,0.5 k},"bb"<>ToString[k]<>".sys.rotationBeam.r"->{0,0,0.5k},"bb"<>ToString[k]<>".sys.rotation.animation"->False,"bb"<>ToString[k]<>".sys.rotationBeam.animation"->False},{k,nSystems}];
eqs=Flatten@Table[{("bb"<>ToString[k]<>".ref_rs")[t]==(1/2)(E^(-(1/8) (((-10+t+k))^2)))-(1/2)(E^(-(1/8) (((-30+t+k))^2))),("bb"<>ToString[k]<>".noise_rs")[t]==0,("bb"<>ToString[k]<>".noise_thetab")[t]==0},{k,nSystems}];
cNames=Table["bb"<>ToString[k],{k,nSystems}];

multiCsys=CreateSystemModel["BallsAndBeams",eqs,t,{cNames∈csysBB},<|"ParameterValues"->pVals,"SimulationSettings"->{"StopTime"->40}|>]

SystemModeler[multiCsys,"Animation"]

Workflows: Tracker for the depth in a submarine

Creating a closed loop model

modelSub=;

linSub=SystemModelLinearize[modelSub]

sspecSub=<|"InputModel"->linSub,"FeedbackInputs"->{"deltarho"},"TrackedOutputs"->{"y"}|>;
ctrlSub=StateFeedbackGains[sspecSub,{-2+I,-2-I,-2},"Data"]

csysSub=ConnectSystemModelController[modelSub,ctrlSub]

Providing a reference

simSub=SystemModelSimulate[csysSub,60,<|"Inputs"->{"ref_y"->Function[t,-15Tanh[t/10]],"f"->RandomFunction[WienerProcess[0,(10^6)],{0,60,0.1}]}|>];

SystemModelPlot[simSub,{"ref_y","y"},PlotRange->All]

SystemModelPlot[simSub,{"syse.sys.deltarho"},PlotRange->All]

Animating the system

subWithRef=CreateSystemModel["SubWithRef",{"csysSub.ref_y"[t]==-15Tanh[t/10],"csysSub.f"[t]==0},t,{"csysSub"∈csysSub},<|"SimulationSettings"->{"StopTime"->20}|>]

SystemModeler[subWithRef,"Animation"]

Workflows: Discrete controller for a camera stabilizer

Creating a closed loop model

modelCS=;

linCS=SystemModelLinearize[modelCS]

sspecCS=<|"InputModel"->linCS,"FeedbackInputs"->{"fa"}|>;
ctrlCS=DiscreteLQRegulatorGains[sspecCS,{(({{1, 0, 0, 0, 0, 0}, {0, (10^6), 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0}, {0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, (10^6), 0}, {0, 0, 0, 0, 0, 1}})),(({{1}}))},0.05,"Data"]

csysCS=ConnectSystemModelController[modelCS,ctrlCS]

Perturbing the system

bumpInputs=<|"Inputs"->{"fa"->Function[t,0],"fw"->Function[t,2000(UnitStep[t]-UnitStep[t-0.25])]}|>;
simModelCS=SystemModelSimulate[modelCS,10,bumpInputs];

SystemModelPlot[simModelCS,{"xce","xbe","xwe"},5,PlotRange->All]

simCsysCS=SystemModelSimulate[csysCS,10,bumpInputs];

SystemModelPlot[simCsysCS,{"syse.sys.xce","syse.sys.xbe","syse.sys.xwe"},PlotRange->All]

Show[SystemModelPlot[simModelCS,{"xce"},10,PlotRange->All,PlotLegends->{"Uncontrolled"}],SystemModelPlot[simCsysCS,{"syse.sys.xce"},PlotRange->All,PlotStyle->Red,PlotLegends->{"Controlled"}]]

SystemModelPlot[simCsysCS,{"syse.sys.fa"},2,PlotRange->All]

Animating the system

withBump=ConnectSystemModelComponents["CSWithBump",{"cs"∈csysCS,"bump"∈"Modelica.Blocks.Sources.Pulse","cons"∈"Modelica.Blocks.Sources.Constant"},{"bump.y"\[UndirectedEdge]"cs.fw","cons.y"\[UndirectedEdge]"cs.fa"},<|"ParameterValues"->{"bump.amplitude"->2000,"bump.startTime"->1,"bump.period"->0.5,"bump.width"->50,"bump.nperiod"->1,"cons.k"->0}|>]

simWB=SystemModelSimulate[withBump,10];

Row[SystemModelPlot[simWB,{#},PlotRange->All,ImageSize->300,PlotLabel->#]&/@{"bump.y","cs.syse.sys.xce","cs.syse.sys.fa"}]

SystemModeler[withBump,"Animation"]

Conclusions

New support for tracking controllers and action over SystemModel objects

Controller data objects enable streamlined workflows for control design