Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add planar wheel examples to docs #133

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion docs/src/examples/wheel.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The wheel-related components available are
- [`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.
- [`RollingWheelSetJoint`](@ref): A lower-level component used in `RollingWheelSet` to model the kinematics of the wheel set, without inertial or mass properties.
- [`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.
- [`PlanarMechanics.SimpleWheel`](@ref): A 2D wheel component with a simple, linear lateral slip model.
- [`PlanarMechanics.SlipBasedWheelJoint`](@ref): A more advanced 2D wheel component with slip-dependent friction characteristics.

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

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

![car animation](car.gif)
![car animation](car.gif)

## Simple planar wheel
This example demonstrates how we can model a simple single-track vehicle with planar (2D or 3DOF) components.

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.

```@example WHEEL
import Multibody.PlanarMechanics as Pl

@mtkmodel TestWheel begin
@components begin
body = Pl.BodyShape(r = [1.0, 0.0], m=1, I=0.1, gy=0)
revolute = Pl.Revolute()
wheel1 = Pl.SimpleWheel(color=tire_black)
wheel2 = Pl.SimpleWheel(color=tire_black, μ=.5)
thrust_input1 = Blocks.Constant(k=1)
thrust_input2 = Blocks.Constant(k=0)
end
@equations begin
connect(body.frame_a, revolute.frame_a)
connect(revolute.frame_b, wheel1.frame_a)
connect(thrust_input1.output, wheel1.thrust)
connect(thrust_input2.output, wheel2.thrust)
revolute.phi ~ deg2rad(50)*sin(2pi*0.2*t)

connect(wheel2.frame_a, body.frame_b)
end
end
@named model = TestWheel()
model = complete(model)
ssys = structural_simplify(IRSystem(model))
defs = Dict(unknowns(ssys) .=> 0)
prob = ODEProblem(ssys, defs, (0.0, 10.0))
sol = solve(prob, Rodas5P())
@test SciMLBase.successful_retcode(sol)
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")
```

![drifting animation](drifting.gif)

## Slip-based planar wheel
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.

```@example WHEEL
@mtkmodel TestSlipBasedWheel begin
@components begin
slipBasedWheelJoint = Pl.SlipBasedWheelJoint(
radius = 0.3,
r = [1,0], # Driving direction at angle phi = 0
mu_A = 0.8, # Friction coefficient at adhesion
mu_S = 0.4, # Friction coefficient at sliding
N = 100, # Base normal load
sAdhesion = 0.04, # Adhesion slippage
sSlide = 0.12, # Sliding slippage
vAdhesion_min = 0.05, # Minimum adhesion velocity
vSlide_min = 0.15, # Minimum sliding velocity
color = tire_black,
)
prismatic = Pl.Prismatic(r = [0,1], s = 1, v = 0)
revolute = Pl.Revolute(phi = 0, w = 0)
fixed = Pl.Fixed()
engineTorque = Rotational.ConstantTorque(tau_constant = 2)
body = Pl.Body(m = 10, I = 1, gy=0)
inertia = Rotational.Inertia(J = 1, phi = 0, w = 0)
constant = Blocks.Constant(k = 0)
end
@equations begin
connect(fixed.frame, revolute.frame_a)
connect(revolute.frame_b, prismatic.frame_a)
connect(prismatic.frame_b, body.frame_a)
connect(prismatic.frame_b, slipBasedWheelJoint.frame_a)
connect(slipBasedWheelJoint.flange_a, inertia.flange_b)
connect(constant.output, slipBasedWheelJoint.dynamicLoad)
connect(engineTorque.flange, inertia.flange_a)
end
end

@named model = TestSlipBasedWheel()
model = complete(model)
ssys = structural_simplify(IRSystem(model))
display(unknowns(ssys))
defs = ModelingToolkit.defaults(model)
prob = ODEProblem(ssys, [
model.inertia.w => 1e-10, # This is important, at zero velocity, the friction is ill-defined
model.revolute.frame_b.phi => 0,
D(model.revolute.frame_b.phi) => 0,
D(model.prismatic.r0[2]) => 0,
], (0.0, 15.0))
sol = solve(prob, Rodas5Pr())
render(model, sol, show_axis=false, x=0, y=0, z=4, traces=[model.slipBasedWheelJoint.frame_a], filename="slipwheel.gif")
```

![slipwheel animation](slipwheel.gif)
2 changes: 1 addition & 1 deletion src/PlanarMechanics/PlanarMechanics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import ..Multibody
export Frame, FrameResolve, PartialTwoFrames, ZeroPosition, ori_2d
include("utils.jl")

export Fixed, Body, FixedTranslation, Spring, Damper, SpringDamper
export Fixed, Body, BodyShape, FixedTranslation, Spring, Damper, SpringDamper
export SlipBasedWheelJoint, SimpleWheel
include("components.jl")

Expand Down
9 changes: 9 additions & 0 deletions src/PlanarMechanics/components.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ The `BodyShape` component is similar to a [`Body`](@ref), but it has two frames
# Parameters
- `r`: (Structural) Vector from `frame_a` to `frame_b` resolved in `frame_a`
- `r_cm`: (Structural) Vector from `frame_a` to the center of mass resolved in `frame_a`

# Subsystems
- `translation`: [FixedTranslation](@ref) Fixed translation between `frame_a` and `frame_b`
- `translation_cm`: [FixedTranslation](@ref) Fixed translation between `frame_a` and the center of mass
- `body`: [Body](@ref) Body component placed at center of mass. This component holds the inertial properties

# Connectors
- `frame_a`
- `frame_b`
"""
@mtkmodel BodyShape begin
@structural_parameters begin
Expand Down
28 changes: 14 additions & 14 deletions src/PlanarMechanics/joints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
Revolute(; name, phi = 0.0, tau = 0.0, axisflange = false)
A revolute joint

# parameters
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
- `phi`: [rad] Initial angular position for the flange
- `tau`: [N.m] Initial Cut torque in the flange
# Parameters:
- `axisflange=false`: If `true`, a force flange is enabled, otherwise implicitly grounded"
- `phi`: [rad] Initial angular position for the flange
- `tau`: [Nm] Initial Cut torque in the flange

# states
- `phi(t)`: [rad] angular position
- `ω(t)`: [rad/s] angular velocity
- `α(t)`: [rad/s²] angular acceleration
- `j(t)`: [N.m] torque
# Variables:
- `phi(t)`: [rad] angular position
- `w(t)`: [rad/s] angular velocity
- `α(t)`: [rad/s²] angular acceleration
- `tau(t)`: [Nm] torque

# Connectors
- `frame_a` [Frame](@ref)
- `frame_b` [Frame](@ref)
- `fixed` [Fixed](@ref) if `axisflange == false`
- `flange_a` [Flange](@ref) if `axisflange == true`
- `support` [Support](@ref) if `axisflange == true`
- `frame_a` [Frame](@ref)
- `frame_b` [Frame](@ref)
- `fixed` [Fixed](@ref) if `axisflange == false`
- `flange_a` [Flange](@ref) if `axisflange == true`
- `support` [Support](@ref) if `axisflange == true`

https://github.com/dzimmer/PlanarMechanics/blob/743462f58858a808202be93b708391461cbe2523/PlanarMechanics/Joints/Revolute.mo
"""
Expand Down
5 changes: 3 additions & 2 deletions test/test_PlanarMechanics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,14 @@ end
body = Pl.BodyShape(r = [1.0, 0.0], m=1, I=0.1, gy=0)
revolute = Pl.Revolute()
wheel1 = Pl.SimpleWheel(color=gray)
wheel2 = Pl.SimpleWheel(color=gray, μ=.1)
wheel2 = Pl.SimpleWheel(color=gray, μ=.5)
input = Blocks.Constant(k=1)
end
@equations begin
connect(body.frame_a, revolute.frame_a)
connect(revolute.frame_b, wheel1.frame_a)
connect(input.output, wheel1.thrust)
revolute.phi ~ deg2rad(60)
revolute.phi ~ deg2rad(50)sin(2pi*0.2*t)
wheel2.thrust.u ~ 0

connect(wheel2.frame_a, body.frame_b)
Expand All @@ -423,6 +423,7 @@ end
prob = ODEProblem(ssys, defs, (0.0, 10.0))
sol = solve(prob, Rodas5P(), initializealg = BrownFullBasicInit())
@test SciMLBase.successful_retcode(sol)
# 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")
end


Expand Down
Loading