Skip to content

Commit 4e5ee3a

Browse files
committed
Merge branch 'main' of github.com:JuliaComputing/Multibody.jl
2 parents db0fc16 + 52b80d5 commit 4e5ee3a

File tree

4 files changed

+114
-26
lines changed

4 files changed

+114
-26
lines changed

docs/src/examples/wheel.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ This example demonstrates use of the [`PlanarMechanics.SlipBasedWheelJoint`](@re
235235
revolute = Pl.Revolute(phi = 0, w = 0)
236236
fixed = Pl.Fixed()
237237
engineTorque = Rotational.ConstantTorque(tau_constant = 2)
238-
body = Pl.Body(m = 10, I = 1, gy=0)
238+
body = Pl.Body(m = 10, I = 1, gy=0, phi=0, w=0)
239239
inertia = Rotational.Inertia(J = 1, phi = 0, w = 0)
240240
constant = Blocks.Constant(k = 0)
241241
end
@@ -264,6 +264,7 @@ prob = ODEProblem(ssys, [
264264
], (0.0, 15.0))
265265
sol = solve(prob, Rodas5Pr())
266266
render(model, sol, show_axis=false, x=0, y=0, z=4, traces=[model.slipBasedWheelJoint.frame_a], filename="slipwheel.gif")
267+
nothing # hide
267268
```
268269

269270
![slipwheel animation](slipwheel.gif)
@@ -275,8 +276,8 @@ A more elaborate example with 4 wheels.
275276
@mtkmodel TwoTrackWithDifferentialGear begin
276277
@components begin
277278
body = Pl.Body(m = 100, I = 1, gy = 0)
278-
body1 = Pl.Body(m = 300, I = 0.1, r = [1, 1], v = [0, 0], phi = 0, w = 0, gy = 0)
279-
body2 = Pl.Body(m = 100, I = 1, gy = 0)
279+
body1 = Pl.Body(m = 300, I = 0.1, r = [1, 1], v = [0, 0], phi = 0, w = 0, gy = 0,)
280+
body2 = Pl.Body(m = 100, I = 1, gy = 0,)
280281
wheelJoint1 = Pl.SlipBasedWheelJoint(
281282
radius = 0.25,
282283
r = [0, 1],
@@ -383,4 +384,8 @@ prob = ODEProblem(ssys, defs, (0.0, 5.0))
383384
sol = solve(prob, Rodas5P(autodiff=false))
384385
@test SciMLBase.successful_retcode(sol)
385386
Multibody.render(model, sol, show_axis=false, x=0, y=0, z=5, filename="twotrack.gif")
386-
```
387+
nothing # hide
388+
```
389+
390+
![twotrack animation](twotrack.gif)
391+
```

src/PlanarMechanics/components.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Body component with mass and inertia
5858
# Connectors:
5959
- `frame`: 2-dim. Coordinate system
6060
"""
61-
@component function Body(; name, m, I, r = zeros(2), v=nothing, phi = 0, w=nothing, gy = -9.807, radius=0.1, render=true, color=Multibody.purple, state_priority=2)
61+
@component function Body(; name, m, I, r = zeros(2), v=nothing, phi = nothing, w=nothing, gy = -9.807, radius=0.1, render=true, color=Multibody.purple, state_priority=2)
6262
@named frame_a = Frame()
6363
pars = @parameters begin
6464
m = m, [description = "Mass of the body"]
@@ -626,13 +626,18 @@ The force depends with friction characteristics on the slip. The slip is split i
626626
- lateral slip: the lateral velocity divided by the rolling velocity.
627627
- longitudinal slip: the longitudinal slip velocity divided by the rolling velocity.
628628
629-
For low rolling velocity this definition become ill-conditioned. Hence a dry-friction model is used for low rolling velocities. For zero rolling velocity, the intitialization might fail if automatic differentiation is used. Either start with a non-zero (but tiny) rolling velocity or pass `autodiff=false` to the solver.
629+
For low rolling velocity this definition become ill-conditioned. Hence a dry-friction model is used for low rolling velocities. For **zero rolling velocity**, the intitialization might fail if automatic differentiation is used. Either start with a non-zero (but tiny) rolling velocity or pass `autodiff=false` to the solver.
630630
631631
The radius of the wheel can be specified by the parameter `radius`. The driving direction (for `phi = 0`) can be specified by the parameter `r`. The normal load is set by `N`.
632632
633633
The wheel contains a 2D connector `frame_a` for the steering on the plane. The rolling motion of the wheel can be actuated by the 1D connector `flange_a`.
634634
635635
In addition there is an input `dynamicLoad` for a dynamic component of the normal load.
636+
637+
# Connectors:
638+
- `frame_a` (Frame) Coordinate system fixed to the component with one cut-force and cut-torque
639+
- `flange_a` (Rotational.Flange) Flange for the rolling motion
640+
- `dynamicLoad` (Blocks.RealInput) Input for the dynamic component of the normal load (must be connected)
636641
"""
637642
@component function SlipBasedWheelJoint(;
638643
name,

src/PlanarMechanics/joints.jl

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ A revolute joint
2222
"""
2323
@component function Revolute(;
2424
name,
25-
axisflange = false, render = true, radius = 0.1, color = [1.0, 0.0, 0.0, 1.0], phi=0, w=0,
25+
axisflange = false, render = true, radius = 0.1, color = [1.0, 0.0, 0.0, 1.0], phi=nothing, w=nothing,
26+
iscut = false,
2627
state_priority = 10)
2728
@named partial_frames = PartialTwoFrames()
2829
@unpack frame_a, frame_b = partial_frames
@@ -31,7 +32,7 @@ A revolute joint
3132
vars = @variables begin
3233
(phi(t) = phi), [state_priority=state_priority]
3334
(w(t) = w), [state_priority=state_priority]
34-
α(t)
35+
α(t), [state_priority=state_priority]
3536
tau(t)
3637
end
3738

@@ -47,14 +48,16 @@ A revolute joint
4748
# rigidly connect positions
4849
frame_a.x ~ frame_b.x,
4950
frame_a.y ~ frame_b.y,
50-
frame_a.phi + phi ~ frame_b.phi,
5151
# balance forces
5252
frame_a.fx + frame_b.fx ~ 0,
5353
frame_a.fy + frame_b.fy ~ 0,
5454
# balance torques
5555
frame_a.tau + frame_b.tau ~ 0,
5656
frame_a.tau ~ tau
5757
]
58+
if !iscut
59+
push!(eqs, frame_a.phi + phi ~ frame_b.phi)
60+
end
5861

5962
if axisflange
6063
@named fixed = Rotational.Fixed()
@@ -83,10 +86,11 @@ end
8386
A prismatic joint
8487
8588
# Parameters
86-
- `r`: [m, m] x,y-direction of the rod wrt. body system at phi=0
87-
- `constant_f`: [N] Constant force in direction of elongation
88-
- `constant_s`: [m] Constant elongation of the joint"
89-
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
89+
- `r`: [m, m] x,y-direction of the rod wrt. body system at phi=0
90+
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
91+
- `render`: Render the joint in animations
92+
- `radius`: Radius of the body in animations
93+
- `color`: Color of the body in animations
9094
9195
# Variables
9296
- `s(t)`: [m] Elongation of the joint
@@ -104,27 +108,28 @@ A prismatic joint
104108
@component function Prismatic(;
105109
name,
106110
r = [0,0],
107-
s = 0,
108-
v = 0,
109-
constant_f = 0,
110-
constant_s = 0,
111+
s = nothing,
112+
v = nothing,
111113
axisflange = false,
112114
render = true,
113115
radius = 0.1,
114116
color = [0,0.8,1,1],
117+
state_priority = 2,
118+
iscut = false,
115119
)
116120
@named partial_frames = PartialTwoFrames()
121+
@unpack frame_a, frame_b = partial_frames
122+
117123
systems = @named begin
118124
fixed = TranslationalModelica.Support()
119125
end
120-
@unpack frame_a, frame_b = partial_frames
121126

122127
if axisflange
123128
more_systems = @named begin
124-
flange_a = TranslationalModelica.Flange(; f = constant_f, constant_s)
125-
support = TranslationalModelica.Support()
129+
flange_a = TranslationalModelica.Flange()
130+
support = TranslationalModelica.Flange()
126131
end
127-
systems = [systems, more_systems]
132+
systems = [systems; more_systems]
128133
end
129134

130135
pars = @parameters begin
@@ -135,8 +140,8 @@ A prismatic joint
135140
end
136141

137142
vars = @variables begin
138-
(s(t) = s), [state_priority = 2, description="Joint coordinate"]
139-
(v(t) = v), [state_priority = 2]
143+
(s(t) = s), [state_priority = state_priority, description="Joint coordinate"]
144+
(v(t) = v), [state_priority = state_priority]
140145
a(t)
141146
f(t)
142147
e0(t)[1:2]
@@ -154,15 +159,21 @@ A prismatic joint
154159
# rigidly connect positions
155160
frame_a.x + r0[1] ~ frame_b.x
156161
frame_a.y + r0[2] ~ frame_b.y
157-
frame_a.phi ~ frame_b.phi
158162
frame_a.fx + frame_b.fx ~ 0
159163
frame_a.fy + frame_b.fy ~ 0
160164
frame_a.tau + frame_b.tau + r0[1] * frame_b.fy - r0[2] * frame_b.fx ~ 0
161165
e0[1] * frame_a.fx + e0[2] * frame_a.fy ~ f
162166
]
167+
if !iscut
168+
push!(eqs, frame_a.phi ~ frame_b.phi)
169+
end
163170

164171
if axisflange
165-
push!(eqs, connect(fixed.flange, support))
172+
append!(eqs, [
173+
connect(fixed, support)
174+
flange_a.s ~ s
175+
flange_a.f ~ f
176+
])
166177
else
167178
# actutation torque
168179
push!(eqs, f ~ 0)

test/test_PlanarMechanics.jl

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ import ModelingToolkitStandardLibrary.Mechanical.Rotational
466466
revolute = Pl.Revolute(phi = 0, w = 0)
467467
fixed = Pl.Fixed()
468468
engineTorque = Rotational.ConstantTorque(tau_constant = 2)
469-
body = Pl.Body(m = 10, I = 1, gy=0)
469+
body = Pl.Body(m = 10, I = 1, gy=0, phi=0, w=0)
470470
inertia = Rotational.Inertia(J = 1, phi = 0, w = 0)
471471
constant = Blocks.Constant(k = 0)
472472
end
@@ -618,3 +618,70 @@ sol = solve(prob, Rodas5P(autodiff=false))
618618
# plot(sol)
619619

620620
end
621+
622+
623+
# ==============================================================================
624+
## Planar Kinematic loop
625+
# ==============================================================================
626+
627+
import ModelingToolkitStandardLibrary.Mechanical.TranslationalModelica as Translational
628+
# NOTE: waiting for release of ModelingToolkitStandardLibrary that includes https://github.com/SciML/ModelingToolkitStandardLibrary.jl/pull/327
629+
# @testset "Planar Kinematic loop" begin
630+
# @info "Testing Planar Kinematic loop"
631+
632+
# @mtkmodel PlanarKinematicLoop begin
633+
# @parameters begin
634+
# radius = 0.02
635+
# end
636+
# @components begin
637+
# fixed = Pl.Fixed()
638+
# fixed1D = Translational.Fixed(s0=0)
639+
# fixedTranslation1 = Pl.FixedTranslation(; r = [0, -0.5], radius)
640+
# fixedTranslation2 = Pl.FixedTranslation(; r = [0, -0.5], radius)
641+
# fixedTranslation3 = Pl.FixedTranslation(; r = [0, -0.6], radius)
642+
# revolute1 = Pl.Revolute(; phi = asin(0.4/0.5/2), state_priority=100, radius)
643+
# revolute3 = Pl.Revolute(; phi = -asin(0.4/0.5/2), radius)
644+
# revolute2 = Pl.Revolute(; radius)
645+
# revolute4 = Pl.Revolute(; phi = -0.69813170079773, w = 0, state_priority=100, radius)
646+
# prismatic1 = Pl.Prismatic(r = [1, 0], s = 0.4, v = 0, axisflange=true, state_priority=10)
647+
# springDamper1D = Translational.SpringDamper(c = 20, d = 4, s_rel0 = 0.4)
648+
# body = Pl.Body(m = 1, I = 0.1, state_priority=10)
649+
# end
650+
651+
# @equations begin
652+
# connect(fixedTranslation1.frame_a, revolute1.frame_b)
653+
# connect(fixedTranslation2.frame_a, revolute3.frame_b)
654+
# connect(revolute2.frame_a, fixedTranslation1.frame_b)
655+
# connect(revolute2.frame_b, fixedTranslation2.frame_b)
656+
# connect(fixedTranslation3.frame_a, revolute4.frame_b)
657+
# connect(revolute1.frame_a, fixed.frame)
658+
# connect(fixed1D.flange, springDamper1D.flange_a)
659+
# connect(revolute4.frame_a, fixedTranslation1.frame_b)
660+
# connect(body.frame_a, fixedTranslation3.frame_b)
661+
# connect(prismatic1.frame_a, fixed.frame)
662+
# connect(springDamper1D.flange_b, prismatic1.flange_a)
663+
# connect(prismatic1.frame_b, revolute3.frame_a)
664+
# end
665+
# end
666+
667+
# @named model = PlanarKinematicLoop()
668+
# model = complete(model)
669+
# ssys = structural_simplify(IRSystem(model))
670+
# @test length(unknowns(ssys)) <= 6 # ideally 4
671+
# display(sort(unknowns(ssys), by=string))
672+
673+
674+
# guesses = ModelingToolkit.missing_variable_defaults(model)
675+
# ps = parameters(model)
676+
# fulldefs = defaults(model)
677+
# defs = Dict(filter(p->!ModelingToolkit.isparameter(p[1]), collect(ModelingToolkit.defaults(model))))
678+
# u0 = merge(Dict(guesses), defs)
679+
# initsys = generate_initializesystem(ssys; guesses=u0)
680+
# initprob = NonlinearLeastSquaresProblem(initsys, u0, [[p => fulldefs[p] for p in ps]; t => 0.0])
681+
# u0sol = solve(initprob)
682+
683+
684+
# prob = ODEProblem(ssys, unknowns(ssys) .=> u0sol[unknowns(ssys)]*0.7, (0.0, 5.0))
685+
# sol = solve(prob, Rodas5P(), initializealg = ShampineCollocationInit())
686+
# @test SciMLBase.successful_retcode(sol)
687+
# end

0 commit comments

Comments
 (0)