@@ -41,7 +41,7 @@ The loop closed around the angle only has a zero in the origin corresponding to
4141
4242 systems = @named begin
4343 plant = PlanarMultibodybot ()
44- controller = Blocks. LimPID (k= 22.3659 , Ti= false , Td= 0.0632787 , Nd= 54.6269 )# , u_max=15)
44+ controller = Blocks. LimPID (k= 0.487401 , Ti= 0.0587352 , Td= 0.0420526 , Nd= 119.368 )# , u_max=15)
4545 ref = Blocks. Constant (k= theta_ref)
4646 end
4747
6262 ssys. plant. body. w => 0.0 # Adjust for offset
6363]
6464
65- prob = ODEProblem (ssys, x0, (0.0 , 10 .0 ))
65+ prob = ODEProblem (ssys, x0, (0.0 , 1 .0 ))
6666sol = solve (prob, Rodas5P ())
6767plot (sol, idxs= [ssys. plant. theta_output. u, ssys. plant. x_output. u, ssys. plant. tau]); hline! ([0 ], l= (:dash , :black ), primary= false )
6868
@@ -111,17 +111,17 @@ spec = JSC.PIDAutotuningAnalysisSpec(;
111111 step_input = " u" ,
112112 step_output = " y" ,
113113 Ts = 0.01 , # Sample time
114- duration = 2 .0 , # Simulation duration
114+ duration = 3 .0 , # Simulation duration
115115 Ms = 1.6 , # Sensitivity peak constraint
116116 Mt = 1.6 , # Complementary sensitivity peak constraint
117- Mks = 100 .0 , # Control sensitivity constraint
117+ Mks = 2000 .0 , # Control sensitivity constraint
118118 wl = 1e-2 , # Lower frequency bound
119119 wu = 1e3 , # Upper frequency bound
120- ki_ub = 0.0 , # Tune PD controller
120+ # ki_ub = 0.0, # Tune PD controller
121121 num_frequencies = 200 ,
122122 soft = true ,
123- # homotopy = true,
124- # auto_resolve = true ,
123+ homotopy = true ,
124+ # auto_resolve = false ,
125125)
126126
127127# Run the autotuning analysis
@@ -150,15 +150,15 @@ display(optimized_params)
150150 systems = @named begin
151151 plant = PlanarMultibodybot ()
152152 # Inner loop: angle controller
153- inner_controller = Blocks. LimPID (k= 22.3659 , Ti= false , Td= 0.0632787 , Nd= 54.6269 )
153+ inner_controller = Blocks. LimPID (k= 0.487401 , Ti= 0.0587352 , Td= 0.0420526 , Nd= 119.368 )
154154 # Outer loop: velocity controller
155- outer_controller = Blocks. LimPID (k= 0.0463128 , Ti= 3.34679 , Td= 2.23125 , Nd= 2.8527 , wd= 1 , wp= 1 , u_max= deg2rad (25.0 ))
155+ outer_controller = Blocks. LimPID (k= 0.0666576 , Ti= 5.25024 , Td= 4.81393 , Nd= 4.76616 , wd= 1 , wp= 1 , u_max= deg2rad (25.0 ))
156156 neg_gain = Blocks. Gain (k= 1 )
157157 ref = Blocks. Step (height= x_ref, start_time= 10 )
158158 # Add pi offset to inner loop reference
159159 pi_offset = Blocks. Constant (k= 0 )
160160 add_pi = Blocks. Add (k1= 1 , k2= 1 )
161- ref_filter = Blocks. FirstOrder (T= 2 )
161+ ref_filter = Blocks. FirstOrder (T= 1 )
162162 end
163163
164164 eqs = [
@@ -188,16 +188,16 @@ x0 = [
188188 cascade_ssys. plant. body. phi => deg2rad (5 )
189189 # cascade_ssys.plant.wheelJoint.color => [0,0,0,0.5]
190190 cascade_ssys. plant. wheelJoint. frame_a. render => true
191- cascade_ssys. plant. wheelJoint. frame_a. length => 0.12
191+ cascade_ssys. plant. wheelJoint. frame_a. length => 0.05
192192 cascade_ssys. plant. wheelJoint. frame_a. radius => 0.004
193193]
194194
195195cascade_prob = ODEProblem (cascade_ssys, x0, (0.0 , 20.0 ))
196- cascade_sol = solve (cascade_prob, Rodas5P ())
196+ cascade_sol = solve (cascade_prob, Rodas5P (), dt = 0.005 , adaptive = false )
197197plot (cascade_sol, idxs= [cascade_ssys. plant. theta_output. u, cascade_ssys. plant. x_output. u, cascade_ssys. outer_controller. ctr_output. u]); hline! ([0 0.15 ], l= (:dash , :black ), primary= false , ylims= (- 1 , 3.5 ), size= (800 ,1600 ))
198198
199- Multibody. render (cascade_model, cascade_sol, 0.0 , lookat= [0 ,0.4 ,0 ], x= 0 , y= 0.4 , z= - 1.0 )[1 ]
200- # Multibody.render(cascade_model, cascade_sol, lookat=[0,0.4 ,0], x=0, y=0.4 , z=-1.0 , timescale=0.5 )
199+ Multibody. render (cascade_model, cascade_sol, 0.0 , lookat= [0 ,0.2 ,0 ], x= 0 , y= 0.2 , z= - 0.5 )[1 ]
200+ # Multibody.render(cascade_model, cascade_sol, lookat=[0,0.2 ,0], x=0, y=0.2 , z=-0.5 , timescale=1 )
201201# plot(cascade_sol, idxs=[cascade_ssys.pi_offset.output.u])
202202
203203# #
@@ -260,7 +260,7 @@ bodeplot(L2)
260260
261261# # Swms = freqresp(S2, ws2)
262262
263- Si2 = get_named_sensitivity (cascade_tuning_model, [cascade_tuning_model. u])
263+ Si2 = get_named_sensitivity (cascade_tuning_model, [cascade_tuning_model. u]; Multibody . linsys ... )
264264w = exp10 .(LinRange (- 1 , 3 , 1000 ))
265265Msi2, ws2 = hinfnorm2 (Si2)
266266bodeplot (Si2, w, plotphase= false ); hline! ([Msi2], l= (:dash , :black ), label= " \$ M_S = \$ $(round (Msi2, digits= 2 )) " )
@@ -271,10 +271,111 @@ nyquistplot(Li2)
271271marginplot (Li2, w, adjust_phase_start= true )
272272
273273
274- Li22 = get_named_looptransfer (cascade_tuning_model, [cascade_tuning_model. u]) |> minreal
274+ Li22 = get_named_looptransfer (cascade_tuning_model, [cascade_tuning_model. u]; Multibody . linsys ... ) |> minreal
275275
276276dmi2 = diskmargin (Li2)
277277plot (dmi2)
278278
279279dmi22 = diskmargin (Li22)
280280plot (dmi22)
281+
282+
283+
284+
285+ # ==============================================================================
286+ # # Feedforward generation
287+ # ==============================================================================
288+
289+ T0 = ss (tf ([1 ], [0.1 , 1 ]))
290+ Tr = T0^ 4
291+
292+ Ryur = DyadControlSystems. feedforward_generator (cascade_tuning_model; # requires master branch
293+ Tr,
294+ measurement = [cascade_tuning_model. y, cascade_tuning_model. y2],
295+ controlled_output = [cascade_tuning_model. y2],
296+ control_input = [cascade_tuning_model. u],
297+ Multibody. linsys...
298+ )
299+
300+ Ryur = balance_statespace (Ryur)[1 ]
301+
302+ @component function FilteredCascadeControlledFlatDyadBot (; name)
303+ pars = @parameters begin
304+ x_ref = 0.15 # Reference velocity
305+ end
306+
307+ systems = @named begin
308+ plant = PlanarMultibodybot ()
309+ # Inner loop: angle controller
310+ inner_controller = Blocks. LimPID (k= 22.3659 , Ti= false , Td= 0.0632787 , Nd= 54.6269 )
311+ # Outer loop: velocity controller
312+ outer_controller = Blocks. LimPID (k= 0.0666576 , Ti= 5.25024 , Td= 4.81393 , Nd= 4.76616 , wd= 1 , wp= 1 , u_max= deg2rad (25.0 ))
313+ neg_gain = Blocks. Gain (k= 1 )
314+ ref = Blocks. Step (height= x_ref, start_time= 5 )
315+ # Add pi offset to inner loop reference
316+ pi_offset = Blocks. Constant (k= 0 )
317+ add_pi = Blocks. Add3 (k1= 1 , k2= 1 )
318+
319+ torque_input = Blocks. Add ()
320+ R = Blocks. StateSpace (ssdata (Ryur)... )
321+ end
322+
323+ eqs = [
324+
325+
326+ connect (ref. output, :r2 , R. input)
327+ R. output. u[1 ] ~ add_pi. input3. u
328+ R. output. u[2 ] ~ outer_controller. reference. u
329+ R. output. u[3 ] ~ torque_input. input2. u
330+
331+
332+ # Outer loop: velocity reference -> angle reference
333+ connect (plant. x_output, neg_gain. input)
334+ connect (neg_gain. output, :y2 , outer_controller. measurement)
335+
336+ # Add pi to outer controller output for inner loop reference
337+ connect (outer_controller. ctr_output, :u2 , add_pi. input1)
338+ connect (pi_offset. output, add_pi. input2)
339+
340+ # Inner loop: angle reference -> torque
341+ connect (add_pi. output, inner_controller. reference)
342+ connect (plant. theta_output, :y , inner_controller. measurement)
343+ connect (inner_controller. ctr_output, :u , torque_input. input1)
344+ connect (torque_input. output, plant. control_input)
345+ ]
346+
347+ System (eqs, t, [], pars; systems, name)
348+ end
349+
350+ @named filtered_model = FilteredCascadeControlledFlatDyadBot ()
351+ filtered_ssys = multibody (filtered_model)
352+
353+ Pcl_angle = named_ss (filtered_model, [filtered_model. r2], [filtered_model. y]; allow_input_derivatives= true , Multibody. linsys... )
354+ Pcl_pos = named_ss (filtered_model, [filtered_model. r2], [filtered_model. y2]; allow_input_derivatives= true , Multibody. linsys... )
355+
356+ Pcl_angle = minreal (Pcl_angle)
357+ Pcl_pos = minreal (Pcl_pos)
358+
359+ w = exp10 .(LinRange (- 2.5 , 1.5 , 2000 ))
360+ bodeplot ([Pcl_angle, Pcl_pos, Tr], w, legend= :bottom , plotphase= false , legendfontsize= 8 , hz= true , label= [" Gar" " Gpr" " Tr" ])
361+
362+
363+ x0 = [
364+ filtered_ssys. inner_controller. k => optimized_params[1 , :Kp_standard ];
365+ filtered_ssys. inner_controller. Ti => false ;# optimized_params[1, :Ti_standard];
366+ filtered_ssys. inner_controller. Td => optimized_params[1 , :Td_standard ];
367+ filtered_ssys. inner_controller. Nd => optimized_params[1 , :Nd ];
368+ filtered_ssys. outer_controller. k => cascade_optimized_params[1 , :Kp_standard ];
369+ filtered_ssys. outer_controller. Ti => cascade_optimized_params[1 , :Ti_standard ];
370+
371+ filtered_ssys. plant. body. phi => deg2rad (- 10 )
372+ ]
373+
374+
375+ filtered_prob = ODEProblem (filtered_ssys, x0, (0.0 , 10.0 ))
376+ filtered_sol = solve (filtered_prob, Rodas5P (), dt= 0.01 , dtmin= 0.01 , adaptive= false )
377+ plot (filtered_sol, idxs= [filtered_ssys. plant. theta_output. u, filtered_ssys. plant. x_output. u, filtered_ssys. outer_controller. ctr_output. u, filtered_ssys. plant. tau]); hline! ([0 0.15 ], l= (:dash , :black ), primary= false , ylims= (- 1 , 3.5 ), size= (800 ,1600 ), legend= :right )
378+
379+ plot (filtered_sol, idxs= [filtered_ssys. R. input. u; filtered_ssys. R. output. u; ])
380+
381+ # Multibody.render(filtered_model, filtered_sol, lookat=[0,0.2,0], x=0, y=0.2, z=-0.5, timescale=1)
0 commit comments