Skip to content

Commit df2a14f

Browse files
committed
add planar wheel examples to docs
1 parent c7953e9 commit df2a14f

File tree

5 files changed

+123
-18
lines changed

5 files changed

+123
-18
lines changed

docs/src/examples/wheel.md

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The wheel-related components available are
1010
- [`RollingWheelSet`](@ref): a set of two wheels connected by an axis. One of the wheels cannot slip, while the other one slips as required to allow the wheel set to turn (no differential is modeled). No wheel can leave the ground.
1111
- [`RollingWheelSetJoint`](@ref): A lower-level component used in `RollingWheelSet` to model the kinematics of the wheel set, without inertial or mass properties.
1212
- [`RollingConstraintVerticalWheel`](@ref): A low-level constraint that is used to enforce a perfectly rolling wheel that is always vertical, i.e., it can only roll forward and not fall down.
13+
- [`PlanarMechanics.SimpleWheel`](@ref): A 2D wheel component with a simple, linear lateral slip model.
14+
- [`PlanarMechanics.SlipBasedWheelJoint`](@ref): A more advanced 2D wheel component with slip-dependent friction characteristics.
1315

1416
All wheel components are limited to rolling on the ``xz`` plane, i.e., the gravity direction must be the default `[0, -1, 0]`.
1517

@@ -170,4 +172,97 @@ render(model, sol, framerate=30, filename="car.gif", x=6, z=6, y=5)
170172
nothing # hide
171173
```
172174

173-
![car animation](car.gif)
175+
![car animation](car.gif)
176+
177+
## Simple planar wheel
178+
This example demonstrates how we can model a simple single-track vehicle with planar (2D or 3DOF) components.
179+
180+
We will use the component [`PlanarMechanics.SimpleWheel`](@ref), together with a [`PlanarMechanics.Revolute`](@ref) joint to connect the front wheel to the [`PlanarMechanics.BodyShape`](@ref) representing the vehicle body. The revolute joint is used for steering.
181+
182+
```@example WHEEL
183+
import Multibody.PlanarMechanics as Pl
184+
185+
@mtkmodel TestWheel begin
186+
@components begin
187+
body = Pl.BodyShape(r = [1.0, 0.0], m=1, I=0.1, gy=0)
188+
revolute = Pl.Revolute()
189+
wheel1 = Pl.SimpleWheel(color=tire_black)
190+
wheel2 = Pl.SimpleWheel(color=tire_black, μ=.5)
191+
thrust_input1 = Blocks.Constant(k=1)
192+
thrust_input2 = Blocks.Constant(k=0)
193+
end
194+
@equations begin
195+
connect(body.frame_a, revolute.frame_a)
196+
connect(revolute.frame_b, wheel1.frame_a)
197+
connect(thrust_input1.output, wheel1.thrust)
198+
connect(thrust_input2.output, wheel2.thrust)
199+
revolute.phi ~ deg2rad(50)*sin(2pi*0.2*t)
200+
201+
connect(wheel2.frame_a, body.frame_b)
202+
end
203+
end
204+
@named model = TestWheel()
205+
model = complete(model)
206+
ssys = structural_simplify(IRSystem(model))
207+
defs = Dict(unknowns(ssys) .=> 0)
208+
prob = ODEProblem(ssys, defs, (0.0, 10.0))
209+
sol = solve(prob, Rodas5P())
210+
@test SciMLBase.successful_retcode(sol)
211+
render(model, sol, show_axis=true, x=1, y=-1.8, z=5, lookat=[1,-1.8,0], traces=[model.wheel1.frame_a, model.wheel2.frame_a], filename="drifting.gif")
212+
```
213+
214+
![drifting animation](drifting.gif)
215+
216+
## Slip-based planar wheel
217+
This example demonstrates use of the [`PlanarMechanics.SlipBasedWheelJoint`](@ref) component, which is a more advanced 2D wheel component with slip-dependent friction characteristics. The wheel is being driven by a constant torque, and is connected through a [`PlanarMechanics.Prismatic`](@ref) joint to a [`PlanarMechanics.Revolute`](@ref) joint. This forces the wheel to move in a circular arc around the revolute pivot point, and spiral outwards due to slip. A [`PlanarMechanics.Body](@ref) attached to the end of the prismatic joint is used to add inertial properties.
218+
219+
```@example WHEEL
220+
@mtkmodel TestSlipBasedWheel begin
221+
@components begin
222+
slipBasedWheelJoint = Pl.SlipBasedWheelJoint(
223+
radius = 0.3,
224+
r = [1,0], # Driving direction at angle phi = 0
225+
mu_A = 0.8, # Friction coefficient at adhesion
226+
mu_S = 0.4, # Friction coefficient at sliding
227+
N = 100, # Base normal load
228+
sAdhesion = 0.04, # Adhesion slippage
229+
sSlide = 0.12, # Sliding slippage
230+
vAdhesion_min = 0.05, # Minimum adhesion velocity
231+
vSlide_min = 0.15, # Minimum sliding velocity
232+
color = tire_black,
233+
)
234+
prismatic = Pl.Prismatic(r = [0,1], s = 1, v = 0)
235+
revolute = Pl.Revolute(phi = 0, w = 0)
236+
fixed = Pl.Fixed()
237+
engineTorque = Rotational.ConstantTorque(tau_constant = 2)
238+
body = Pl.Body(m = 10, I = 1, gy=0)
239+
inertia = Rotational.Inertia(J = 1, phi = 0, w = 0)
240+
constant = Blocks.Constant(k = 0)
241+
end
242+
@equations begin
243+
connect(fixed.frame, revolute.frame_a)
244+
connect(revolute.frame_b, prismatic.frame_a)
245+
connect(prismatic.frame_b, body.frame_a)
246+
connect(prismatic.frame_b, slipBasedWheelJoint.frame_a)
247+
connect(slipBasedWheelJoint.flange_a, inertia.flange_b)
248+
connect(constant.output, slipBasedWheelJoint.dynamicLoad)
249+
connect(engineTorque.flange, inertia.flange_a)
250+
end
251+
end
252+
253+
@named model = TestSlipBasedWheel()
254+
model = complete(model)
255+
ssys = structural_simplify(IRSystem(model))
256+
display(unknowns(ssys))
257+
defs = ModelingToolkit.defaults(model)
258+
prob = ODEProblem(ssys, [
259+
model.inertia.w => 1e-10, # This is important, at zero velocity, the friction is ill-defined
260+
model.revolute.frame_b.phi => 0,
261+
D(model.revolute.frame_b.phi) => 0,
262+
D(model.prismatic.r0[2]) => 0,
263+
], (0.0, 15.0))
264+
sol = solve(prob, Rodas5Pr())
265+
render(model, sol, show_axis=false, x=0, y=0, z=4, traces=[model.slipBasedWheelJoint.frame_a], filename="slipwheel.gif")
266+
```
267+
268+
![slipwheel animation](slipwheel.gif)

src/PlanarMechanics/PlanarMechanics.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import ..Multibody
1616
export Frame, FrameResolve, PartialTwoFrames, ZeroPosition, ori_2d
1717
include("utils.jl")
1818

19-
export Fixed, Body, FixedTranslation, Spring, Damper, SpringDamper
19+
export Fixed, Body, BodyShape, FixedTranslation, Spring, Damper, SpringDamper
2020
export SlipBasedWheelJoint, SimpleWheel
2121
include("components.jl")
2222

src/PlanarMechanics/components.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ The `BodyShape` component is similar to a [`Body`](@ref), but it has two frames
107107
# Parameters
108108
- `r`: (Structural) Vector from `frame_a` to `frame_b` resolved in `frame_a`
109109
- `r_cm`: (Structural) Vector from `frame_a` to the center of mass resolved in `frame_a`
110+
111+
# Subsystems
112+
- `translation`: [FixedTranslation](@ref) Fixed translation between `frame_a` and `frame_b`
113+
- `translation_cm`: [FixedTranslation](@ref) Fixed translation between `frame_a` and the center of mass
114+
- `body`: [Body](@ref) Body component placed at center of mass. This component holds the inertial properties
115+
116+
# Connectors
117+
- `frame_a`
118+
- `frame_b`
110119
"""
111120
@mtkmodel BodyShape begin
112121
@structural_parameters begin

src/PlanarMechanics/joints.jl

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22
Revolute(; name, phi = 0.0, tau = 0.0, axisflange = false)
33
A revolute joint
44
5-
# parameters
6-
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
7-
- `phi`: [rad] Initial angular position for the flange
8-
- `tau`: [N.m] Initial Cut torque in the flange
5+
# Parameters:
6+
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
7+
- `phi`: [rad] Initial angular position for the flange
8+
- `tau`: [Nm] Initial Cut torque in the flange
99
10-
# states
11-
- `phi(t)`: [rad] angular position
12-
- `ω(t)`: [rad/s] angular velocity
13-
- `α(t)`: [rad/s²] angular acceleration
14-
- `j(t)`: [N.m] torque
10+
# Variables:
11+
- `phi(t)`: [rad] angular position
12+
- `w(t)`: [rad/s] angular velocity
13+
- `α(t)`: [rad/s²] angular acceleration
14+
- `tau(t)`: [Nm] torque
1515
1616
# Connectors
17-
- `frame_a` [Frame](@ref)
18-
- `frame_b` [Frame](@ref)
19-
- `fixed` [Fixed](@ref) if `axisflange == false`
20-
- `flange_a` [Flange](@ref) if `axisflange == true`
21-
- `support` [Support](@ref) if `axisflange == true`
17+
- `frame_a` [Frame](@ref)
18+
- `frame_b` [Frame](@ref)
19+
- `fixed` [Fixed](@ref) if `axisflange == false`
20+
- `flange_a` [Flange](@ref) if `axisflange == true`
21+
- `support` [Support](@ref) if `axisflange == true`
2222
2323
https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Joints/Revolute.mo
2424
"""

test/test_PlanarMechanics.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,14 @@ end
403403
body = Pl.BodyShape(r = [1.0, 0.0], m=1, I=0.1, gy=0)
404404
revolute = Pl.Revolute()
405405
wheel1 = Pl.SimpleWheel(color=gray)
406-
wheel2 = Pl.SimpleWheel(color=gray, μ=.1)
406+
wheel2 = Pl.SimpleWheel(color=gray, μ=.5)
407407
input = Blocks.Constant(k=1)
408408
end
409409
@equations begin
410410
connect(body.frame_a, revolute.frame_a)
411411
connect(revolute.frame_b, wheel1.frame_a)
412412
connect(input.output, wheel1.thrust)
413-
revolute.phi ~ deg2rad(60)
413+
revolute.phi ~ deg2rad(50)sin(2pi*0.2*t)
414414
wheel2.thrust.u ~ 0
415415

416416
connect(wheel2.frame_a, body.frame_b)
@@ -423,6 +423,7 @@ end
423423
prob = ODEProblem(ssys, defs, (0.0, 10.0))
424424
sol = solve(prob, Rodas5P(), initializealg = BrownFullBasicInit())
425425
@test SciMLBase.successful_retcode(sol)
426+
# Multibody.render(model, sol, show_axis=true, x=1, y=-1.8, z=5, lookat=[1,-1.8,0], traces=[model.wheel1.frame_a, model.wheel2.frame_a], filename="drifting.gif")
426427
end
427428

428429

0 commit comments

Comments
 (0)