Skip to content

Commit 4c312f7

Browse files
committed
add BodyShape component
1 parent b1feacc commit 4c312f7

File tree

4 files changed

+142
-75
lines changed

4 files changed

+142
-75
lines changed

ext/Render.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,8 @@ function render!(scene, ::typeof(P.Body), sys, sol, t)
719719
end
720720

721721

722-
function render!(scene, ::typeof(P.FixedTranslation), sys, sol, t)
722+
function render!(scene, ::Union{typeof(P.FixedTranslation), typeof(P.BodyShape)}, sys, sol, t)
723+
sol(sol.t[1], idxs=sys.render)==true || return true # yes, == true
723724
r_0a = get_fun(sol, [sys.frame_a.x, sys.frame_a.y])
724725
r_0b = get_fun(sol, [sys.frame_b.x, sys.frame_b.y])
725726
color = get_color(sys, sol, :purple)
@@ -728,14 +729,15 @@ function render!(scene, ::typeof(P.FixedTranslation), sys, sol, t)
728729
r2 = Point3f(r_0b($t)..., 0)
729730
origin = r1#(r1+r2) ./ 2
730731
extremity = r2#-r1 # Double pendulum is a good test for this
731-
radius = 0.1f0#Float32(sol($t, idxs=sys.radius))
732+
radius = Float32(sol($t, idxs=sys.radius))
732733
Makie.GeometryBasics.Cylinder(origin, extremity, radius)
733734
end
734735
mesh!(scene, thing; color, specular = Vec3f(1.5), shininess=20f0, diffuse=Vec3f(1))
735736
true
736737
end
737738

738739
function render!(scene, ::typeof(P.Revolute), sys, sol, t)
740+
sol(sol.t[1], idxs=sys.render)==true || return true # yes, == true
739741
r_0 = get_fun(sol, [sys.frame_a.x, sys.frame_a.y])
740742
n = [0,0,1]
741743
color = get_color(sys, sol, :red)
@@ -764,6 +766,7 @@ function render!(scene, ::typeof(P.Revolute), sys, sol, t)
764766
end
765767

766768
function render!(scene, ::Union{typeof(P.Spring), typeof(P.SpringDamper)}, sys, sol, t)
769+
sol(sol.t[1], idxs=sys.render)==true || return true # yes, == true
767770
r_0a = get_fun(sol, [sys.frame_a.x, sys.frame_a.y])
768771
r_0b = get_fun(sol, [sys.frame_b.x, sys.frame_b.y])
769772
color = get_color(sys, sol, :blue)

src/PlanarMechanics/components.jl

Lines changed: 99 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
purple = Multibody.purple
12
"""
23
Fixed(; name, r = (0.0, 0.0), phi = 0.0)
34
@@ -13,7 +14,6 @@ Frame fixed in the planar world frame at a given position and orientation
1314
1415
- `frame: 2-dim. Coordinate system
1516
16-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/Fixed.mo
1717
"""
1818
@mtkmodel Fixed begin
1919
@parameters begin
@@ -34,44 +34,40 @@ https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b70839146
3434
end
3535

3636
"""
37-
Body(; name, m, j, r, g = nothing)
37+
Body(; name, m=1, I=0.1, r=0, gy=-9.807, radius=0.1, render=true, color=Multibody.purple)
3838
3939
Body component with mass and inertia
4040
4141
# Parameters:
42-
43-
- `m`: [kg] mass of the body
44-
- `j`: [kg.m²] inertia of the body with respect to the origin of `frame` along the z-axis of `frame`
45-
- `r`: [m, m] (optional) Translational position x,y-position
46-
- `gy`: [m/s²] (optional) gravity field acting on the mass in the y-direction, positive value acts in the positive direction defaults to -9.807
47-
48-
# States:
49-
50-
- `rx`: [m] x position
51-
- `ry`: [m] y position
52-
- `vx`: [m/s] x velocity
53-
- `vy`: [m/s] y velocity
54-
- `ax`: [m/s²] x acceleration
55-
- `ay`: [m/s²] y acceleration
42+
- `m`: [kg] mass of the body
43+
- `j`: [kg.m²] inertia of the body with respect to the origin of `frame` along the z-axis of `frame`
44+
- `r`: [m, m] Translational position x,y-position
45+
- `gy`: [m/s²] gravity field acting on the mass in the y-direction, positive value acts in the positive direction defaults to -9.807
46+
- `radius`: [m] Radius of the body in animations
47+
- `render`: [Bool] Render the body in animations
48+
- `color`: [Array{Float64,1}] Color of the body in animations
49+
50+
# Variables:
51+
- `r`: [m, m] x,y position
52+
- `v`: [m/s, m/s] x,y velocity
53+
- `a`: [m/s², m/s²] x,y acceleration
5654
- `phi`: [rad] rotation angle (counterclockwise)
5755
- `ω`: [rad/s] angular velocity
5856
- `α`: [rad/s²] angular acceleration
5957
6058
# Connectors:
61-
6259
- `frame`: 2-dim. Coordinate system
63-
64-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/Body.mo
6560
"""
6661
@component function Body(; name, m, I, r = zeros(2), phi = 0, gy = -9.807, radius=0.1, render=true, color=Multibody.purple)
6762
@named frame = Frame()
6863
pars = @parameters begin
69-
m = m
70-
I = I
71-
gy = gy
64+
m = m, [description = "Mass of the body"]
65+
I = I, [description = "Inertia of the body with respect to the origin of frame_a along the z-axis of frame_a"]
66+
gy = gy, [description = "Gravity field acting on the mass in the y-direction, positive value acts in the positive direction"]
7267
radius = radius, [description = "Radius of the body in animations"]
7368
render = render, [description = "Render the body in animations"]
7469
color[1:4] = color, [description = "Color of the body in animations"]
70+
z = 0, [description = "Fixed z-position"]
7571
end
7672

7773
vars = @variables begin
@@ -95,30 +91,68 @@ https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b70839146
9591
α .~ D.(ω)
9692
# newton's law
9793
f .~ [frame.fx, frame.fy]
98-
f .~ m*a + m*[0, -gy]#ifelse(gy !== nothing, fy / m + gy, fy / m),
94+
f + [0, m*gy] .~ m*a#ifelse(gy !== nothing, fy / m + gy, fy / m),
9995
I * α ~ frame.j
10096
]
10197

10298
return compose(ODESystem(eqs, t, vars, pars; name),
10399
frame)
104100
end
105101

102+
"""
103+
BodyShape(; name, r = [1,0], r_cm = 0.5*r, gy = -9.807)
104+
105+
The `BodyShape` component is similar to a [`Body`](@ref), but it has two frames and a vector `r` that describes the translation between them, while the body has a single frame only.
106+
107+
# Parameters
108+
- `r`: (Structural) Vector from `frame_a` to `frame_b` resolved in `frame_a`
109+
- `r_cm`: (Structural) Vector from `frame_a` to the center of mass resolved in `frame_a`
110+
"""
111+
@mtkmodel BodyShape begin
112+
@structural_parameters begin
113+
r = [1,0]
114+
r_cm = 0.5*r
115+
gy = -9.807
116+
end
117+
@parameters begin
118+
# r[1:2] = [1,0], [description = "Fixed x,y-length of the rod resolved w.r.t to body frame_a at phi = 0"]
119+
# r_cm[1:2] = 0.5*r, [description = "Vector from frame_a to center of mass, resolved in frame_a"]
120+
m = 1, [description = "mass of the body"]
121+
I = 0.1, [description = "inertia of the body with respect to the center of mass"]
122+
radius = 0.1, [description = "Radius of the body in animations"]
123+
render = true, [description = "Render the body in animations"]
124+
(color[1:4] = purple), [description = "Color of the body in animations"]
125+
end
126+
@components begin
127+
translation = FixedTranslation(; r)
128+
translation_cm = FixedTranslation(; r=r_cm)
129+
body = Body(; r=r_cm, I, m, gy)
130+
frame_a = Frame()
131+
frame_b = Frame()
132+
end
133+
@equations begin
134+
connect(frame_a, translation.frame_a, translation_cm.frame_a)
135+
connect(frame_b, translation.frame_b)
136+
connect(translation_cm.frame_b, body.frame)
137+
end
138+
end
139+
106140
"""
107141
FixedTranslation(; name, r::AbstractArray, l)
108142
109143
A fixed translation between two components (rigid rod)
110144
111145
# Parameters:
112-
113-
- `rx`: [m] Fixed x-length of the rod resolved w.r.t to body frame_a at phi = 0
114-
- `ry`: [m] Fixed y-length of the rod resolved w.r.t to body frame_a at phi = 0
146+
- `rx`: [m] Fixed x-length of the rod resolved w.r.t to body frame_a at phi = 0
147+
- `ry`: [m] Fixed y-length of the rod resolved w.r.t to body frame_a at phi = 0
148+
- `radius`: [m] Radius of the rod in animations
149+
- `render`: [Bool] Render the rod in animations
115150
116151
# Connectors:
117-
118-
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
119-
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
120152
121-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/FixedTranslation.mo
153+
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
154+
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
155+
122156
"""
123157
@mtkmodel FixedTranslation begin
124158
@extend frame_a, frame_b = partial_frames = PartialTwoFrames()
@@ -159,22 +193,23 @@ end
159193
Linear 2D translational spring
160194
161195
# Parameters:
162-
163-
- `c_x`: [N/m] Spring constant in x dir
164-
- `c_y`: [N/m] Spring constant in y dir
165-
- `c_phi`: [N.m/rad] Spring constant in phi dir
166-
- `s_relx0`: [m] Unstretched spring length
167-
- `s_rely0`: [m] Unstretched spring length
168-
- `phi_rel0`: [rad] Unstretched spring angle
169-
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
170-
196+
- `c_x`: [N/m] Spring constant in x dir
197+
- `c_y`: [N/m] Spring constant in y dir
198+
- `c_phi`: [N.m/rad] Spring constant in phi dir
199+
- `s_relx0`: [m] Unstretched spring length
200+
- `s_rely0`: [m] Unstretched spring length
201+
- `phi_rel0`: [rad] Unstretched spring angle
202+
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
203+
- `num_windings`: [Int] Number of windings of the coil when rendered
204+
- `color = [0,0,1,1]` Color of the spring in animations
205+
- `render = true` Render the spring in animations
206+
- `radius = 0.1` Radius of spring when rendered
207+
- `N = 200` Number of points in mesh when rendered
171208
172209
# Connectors:
210+
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
211+
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
173212
174-
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
175-
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
176-
177-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/Spring.mo
178213
"""
179214
@mtkmodel Spring begin
180215
@extend frame_a, frame_b = partial_frames = PartialTwoFrames()
@@ -232,18 +267,13 @@ end
232267
Linear (velocity dependent) damper
233268
234269
# Parameters:
235-
236-
- `d`: [N.s/m] Damoing constant
237-
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
270+
- `d`: [N.s/m] Damping constant
271+
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
238272
239273
240274
# Connectors:
241-
242-
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
243-
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
244-
245-
246-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/Damper.mo
275+
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
276+
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
247277
"""
248278
@mtkmodel Damper begin
249279
@extend frame_a, frame_b = partial_frames = PartialTwoFrames()
@@ -298,25 +328,26 @@ end
298328
Linear 2D translational spring damper model
299329
300330
# Parameters:
301-
302-
- `c_x`: [N/m] Spring constant in x dir
303-
- `c_y`: [N/m] Spring constant in y dir
304-
- `c_phi`: [N.m/rad] Spring constant in phi dir
305-
- `d_x`: [N.s/m] Damping constant in x dir
306-
- `d_y`: [N.s/m] Damping constant in y dir
307-
- `d_phi`: [N.m.s/rad] Damping constant in phi dir
308-
- `s_relx0`: [m] Unstretched spring length
309-
- `s_rely0`: [m] Unstretched spring length
310-
- `phi_rel0`: [rad] Unstretched spring angle
311-
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
312-
331+
- `c_x`: [N/m] Spring constant in x dir
332+
- `c_y`: [N/m] Spring constant in y dir
333+
- `c_phi`: [N.m/rad] Spring constant in phi dir
334+
- `d_x`: [N.s/m] Damping constant in x dir
335+
- `d_y`: [N.s/m] Damping constant in y dir
336+
- `d_phi`: [N.m.s/rad] Damping constant in phi dir
337+
- `s_relx0`: [m] Unstretched spring length
338+
- `s_rely0`: [m] Unstretched spring length
339+
- `phi_rel0`: [rad] Unstretched spring angle
340+
- `s_small`: [m] Prevent zero-division if distance between frame_a and frame_b is zero
341+
- `num_windings`: [Int] Number of windings of the coil when rendered
342+
- `color = [0,0,1,1]` Color of the spring in animations
343+
- `render = true` Render the spring in animations
344+
- `radius = 0.1` Radius of spring when rendered
345+
- `N = 200` Number of points in mesh when rendered
313346
314347
# Connectors:
348+
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
349+
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
315350
316-
- `frame_a` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
317-
- `frame_b` [Frame](@ref) Coordinate system fixed to the component with one cut-force and cut-torque
318-
319-
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Parts/SpringDamper.mo
320351
"""
321352
@mtkmodel SpringDamper begin
322353
@extend frame_a, frame_b = partial_frames = PartialTwoFrames()

src/PlanarMechanics/joints.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b70839146
2424
"""
2525
@component function Revolute(;
2626
name,
27-
use_flange = false)
27+
use_flange = false, render = true, radius = 0.1, color = [1.0, 0.0, 0.0, 1.0])
2828
@named partial_frames = PartialTwoFrames()
2929
@unpack frame_a, frame_b = partial_frames
3030
systems = [frame_a, frame_b]
@@ -36,6 +36,12 @@ https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b70839146
3636
j(t)
3737
end
3838

39+
pars = @parameters begin
40+
render = render, [description = "Render the joint in animations"]
41+
radius = radius, [description = "Radius of the body in animations"]
42+
color[1:4] = color, [description = "Color of the body in animations"]
43+
end
44+
3945
eqs = [
4046
ω ~ D(phi),
4147
α ~ D(ω),
@@ -64,7 +70,6 @@ https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b70839146
6470
push!(eqs, j ~ 0)
6571
end
6672

67-
pars = []
6873

6974
return compose(ODESystem(eqs, t, vars, pars; name = name),
7075
systems...)

test/test_PlanarMechanics.jl

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,35 @@ end
4747

4848
@named model = ODESystem(connections,
4949
t,
50-
[],
51-
[],
5250
systems = [body, revolute, rod, ceiling])
51+
model = complete(model)
52+
ssys = structural_simplify(IRSystem(model))
53+
54+
@test length(unknowns(ssys)) == 2
55+
unset_vars = setdiff(unknowns(ssys), keys(ModelingToolkit.defaults(ssys)))
56+
prob = ODEProblem(ssys, unset_vars .=> 0.0, tspan)
57+
58+
sol = solve(prob, Rodas5P())
59+
@test SciMLBase.successful_retcode(sol)
60+
@test sol(1, idxs=model.rod.frame_a.phi) -2.881383661312169 atol=1e-2
61+
@test sol(2, idxs=model.rod.frame_a.phi) -1 atol=1e-2
62+
end
63+
64+
@testset "Pendulum with body shape" begin
65+
# https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Examples/Pendulum.mo
66+
@named ceiling = Pl.Fixed()
67+
@named rod = Pl.BodyShape(r = [1.0, 0.0], m=1, I=0.1)
68+
@named revolute = Pl.Revolute()
69+
70+
connections = [
71+
connect(ceiling.frame, revolute.frame_a),
72+
connect(revolute.frame_b, rod.frame_a),
73+
]
74+
75+
@named model = ODESystem(connections,
76+
t,
77+
systems = [revolute, rod, ceiling])
78+
model = complete(model)
5379
ssys = structural_simplify(IRSystem(model))
5480

5581
@test length(unknowns(ssys)) == 2
@@ -58,6 +84,8 @@ end
5884

5985
sol = solve(prob, Rodas5P())
6086
@test SciMLBase.successful_retcode(sol)
87+
@test sol(1, idxs=model.rod.frame_a.phi) -pi atol=1e-2
88+
@test sol(2, idxs=model.rod.frame_a.phi) 0 atol=1e-2
6189
end
6290

6391
@testset "Prismatic" begin
@@ -302,7 +330,7 @@ end
302330
c_x = 5,
303331
d_x = 1,
304332
c_phi = 0)
305-
@named body = Pl.Body(; I = 0.1, m = 0.5, rx = 1, ry = 1, color=[0,1,0,1])
333+
@named body = Pl.Body(; I = 0.1, m = 0.5, r = [1,1], color=[0,1,0,1])
306334
@named fixed = Pl.Fixed()
307335
@named fixed_translation = Pl.FixedTranslation(; r = [-1, 0])
308336

0 commit comments

Comments
 (0)