1+ using DyadBotComponents
2+ using ModelingToolkit
3+ using ModelingToolkit: t_nounits as t, D_nounits as D
4+ using ModelingToolkitInputs
5+ using ModelingToolkitParameters
6+ using OrdinaryDiffEq
7+ using SciMLBase
8+
9+ # --------------------------------------------------------------
10+ # Precompiled Constants
11+ # --------------------------------------------------------------
12+ @named bot = DyadBotComponents. CascadeControlledFlatDyadBot ()
13+ bot_ns = ModelingToolkit. toggle_namespacing (bot, false )
14+ inputs = [bot_ns. v_ref]
15+ sys = mtkcompile (bot; inputs)
16+ sys, input_functions = ModelingToolkitInputs. build_input_functions (sys, inputs)
17+ bot_params = DyadBotComponents. CascadeControlledFlatDyadBotParams ()
18+ prob = ODEProblem (sys, sys => bot_params, (0 , Inf ))
19+ bot_setters = cache (sys, DyadBotComponents. CascadeControlledFlatDyadBotParams);
20+
21+
22+
23+ # --------------------------------------------------------------
24+ # APP
25+ # --------------------------------------------------------------
26+ using ShoelaceWidgets
27+ using Bonito
28+ using WGLMakie
29+
30+ @kwdef struct AppState
31+ integrator = Ref {SciMLBase.DEIntegrator} ()
32+
33+ v_ref = Ref (0.0 )
34+
35+ setup = SLButton (" setup" )
36+ stop = SLButton (" stop" ; disabled= true )
37+
38+ loop = Ref (true )
39+ loop_thread = Ref {Task} ()
40+
41+ left1 = SLButton (" <" )
42+ left2 = SLButton (" <<" )
43+ left3 = SLButton (" <<<" )
44+ pause = SLButton (" ||" )
45+ right1 = SLButton (" >" )
46+ right2 = SLButton (" >>" )
47+ right3 = SLButton (" >>>" )
48+
49+ L = SLInput (0.5 ; label= " length [m]" )
50+ R = SLInput (0.1 ; label= " wheel radius [m]" )
51+
52+ kpo = SLInput (0.54 ; label= " outer P" )
53+ kio = SLInput (2.48 ; label= " outer i" )
54+ kdo = SLInput (0.0 ; label= " outer i" )
55+
56+ kpi = SLInput (15.6 ; label= " inner P" )
57+ kii = SLInput (1e6 ; label= " inner i" ) # TODO : put to Inf
58+ kdi = SLInput (0.16 ; label= " inner i" )
59+
60+
61+ f = Figure ()
62+ ax = Axis (f[1 ,1 ])
63+
64+ bot_params = DyadBotComponents. CascadeControlledFlatDyadBotParams ()
65+
66+ base_transform = Ref {Makie.Transformation} ()
67+ spoke_transform = Ref {Makie.Transformation} ()
68+ body_transform = Ref {Makie.Transformation} ()
69+
70+ end
71+
72+ left1 (app:: AppState , x) = app. v_ref[] = - 1
73+ left2 (app:: AppState , x) = app. v_ref[] = - 2
74+ left3 (app:: AppState , x) = app. v_ref[] = - 3
75+
76+ pause (app:: AppState , x) = app. v_ref[] = 0
77+
78+ right1 (app:: AppState , x) = app. v_ref[] = + 1
79+ right2 (app:: AppState , x) = app. v_ref[] = + 2
80+ right3 (app:: AppState , x) = app. v_ref[] = + 3
81+
82+ function stop (app:: AppState , x)
83+
84+ app. loop[] = false
85+ wait (app. loop_thread[])
86+ app. setup. disabled[] = false
87+ app. stop. disabled[] = true
88+
89+ end
90+
91+
92+ function build_fig! (app:: AppState )
93+
94+ empty! (app. ax)
95+
96+ r = app. bot_params. plant. R # wheel radius
97+ y1 = r
98+
99+ L = app. bot_params. plant. L # body length
100+ y2 = y1 + L
101+
102+ w1 = r* 0.5
103+ w2 = r* 2
104+
105+ # wheel
106+ app. base_transform[] = Transformation (origin = Vec3d (0.0 , y1, 0 ))
107+ app. spoke_transform[] = Transformation (app. base_transform[]; origin = Vec3d (0.0 , y1, 0 ))
108+ linesegments! (app. ax, [- r,+ r], [y1,y1]; transformation= app. spoke_transform[], color= :blue )
109+ linesegments! (app. ax, [0.0 ,0.0 ], [y1- r,y1+ r]; transformation= app. spoke_transform[], color= :blue )
110+ poly! (app. ax, Circle (Point2f (0.0 ,y1), r); color= :transparent , strokecolor= :blue , strokewidth= 2 , transformation= app. spoke_transform[])
111+
112+ # body
113+ app. body_transform[] = Transformation (app. base_transform[]; origin = Vec3d (0.0 , y1, 0 ))
114+ poly! (app. ax, Rect (0.0 - w1/ 2 , y1, w1, y2- y1); color= :transparent , strokecolor= :red , strokewidth= 2 , transformation= app. body_transform[])
115+ poly! (app. ax, Rect (0.0 - w2/ 2 , y2, w2, w2); color= :transparent , strokecolor= :green , strokewidth= 2 , transformation= app. body_transform[])
116+
117+ # frame
118+ poly! (app. ax, Rect (- 3 / 2 , 0 , 3 , 3 ); color= :transparent )
119+ end
120+
121+
122+ function setup (app:: AppState , x)
123+
124+ app. bot_params. plant. R = app. R. value[]
125+ app. bot_params. plant. L = app. L. value[]
126+
127+ app. bot_params. outer_controller. k = app. kpo. value[]
128+ app. bot_params. outer_controller. Ti = app. kio. value[]
129+ app. bot_params. outer_controller. Td = app. kdo. value[]
130+
131+ app. bot_params. inner_controller. k = app. kpi. value[]
132+ app. bot_params. inner_controller. Ti = app. kii. value[]
133+ app. bot_params. inner_controller. Td = app. kdi. value[]
134+
135+ build_fig! (app)
136+
137+
138+ # Update Model Parameters
139+ # -- precompiled constants: prob, bot_setters, sys
140+ prob_ = remake (prob, bot_setters, sys => app. bot_params)
141+
142+ # Initialize the integrator
143+ app. integrator[] = init (prob_, Rodas5P ())
144+
145+ step_size = 0.1
146+ app. loop[] = true
147+
148+ r = app. bot_params. plant. R
149+
150+ app. loop_thread[] = Threads. @spawn while app. loop[]
151+ t0 = Base. time ()
152+
153+ # input
154+ set_input! (input_functions, app. integrator[], sys. v_ref, app. v_ref[])
155+
156+ # step
157+ step! (app. integrator[], 0.1 , true )
158+
159+ # update plot
160+ x = app. integrator[][sys. plant. x]
161+ theta = app. integrator[][sys. plant. theta]
162+
163+ translate! (app. base_transform[], x)
164+ rotate! (app. body_transform[], - theta + π)
165+ rotate! (app. spoke_transform[], - x/ r)
166+
167+ # real time pause
168+ compute_time = Base. time () - t0
169+ sleep_time = max (step_size - compute_time, 0 )
170+ sleep (sleep_time)
171+ end
172+
173+ app. setup. disabled[] = true
174+ app. stop. disabled[] = false
175+
176+ end
177+
178+
179+ function build_app! (app:: AppState )
180+
181+ on (Base. Fix1 (left1, app), app. left1. value)
182+ on (Base. Fix1 (left2, app), app. left2. value)
183+ on (Base. Fix1 (left3, app), app. left3. value)
184+ on (Base. Fix1 (pause, app), app. pause. value)
185+ on (Base. Fix1 (right1, app), app. right1. value)
186+ on (Base. Fix1 (right2, app), app. right2. value)
187+ on (Base. Fix1 (right3, app), app. right3. value)
188+
189+ on (Base. Fix1 (setup, app), app. setup. value)
190+ on (Base. Fix1 (stop, app), app. stop. value)
191+
192+ end
193+
194+ function initialize ()
195+ app = AppState ()
196+ build_app! (app)
197+ return app
198+ end
199+
200+ function get_head ()
201+ DOM. head (
202+ DOM. title (" JuliaHub | DyadBot" ),
203+ DOM. head (
204+ get_shoelace ()...
205+ )
206+ )
207+ end
208+
209+
210+
211+ function get_body (session, app:: AppState )
212+ DOM. body (
213+
214+ DOM. div (
215+ app. R,
216+ app. L,
217+ DOM. hr (), # ---------------
218+ app. kpo,
219+ app. kio,
220+ app. kdo,
221+ app. kpi,
222+ app. kii,
223+ app. kdi,
224+ DOM. hr (), # ---------------
225+ DOM. div (app. setup, app. stop),
226+ DOM. hr (), # ---------------
227+ DOM. div (app. left3, app. left2, app. left1, app. pause, app. right1, app. right2, app. right3),
228+ DOM. hr (), # ---------------
229+ app. f
230+ )
231+
232+
233+ )
234+ end
235+
236+ app = initialize ()
237+
238+
239+ App () do session
240+ DOM. html (
241+ get_head (),
242+ get_body (session, app)
243+ )
244+ end
0 commit comments