Skip to content

Commit 5730541

Browse files
committed
add two wheel models
1 parent 5859606 commit 5730541

File tree

4 files changed

+358
-24
lines changed

4 files changed

+358
-24
lines changed

ext/Render.jl

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ function render!(scene, ::typeof(P.Body), sys, sol, t)
719719
sol(sol.t[1], idxs=sys.render)==true || return true # yes, == true
720720
color = get_color(sys, sol, :purple)
721721
r_cm = get_fun(sol, collect(sys.r))
722-
framefun = get_frame_fun_2d(sol, sys.frame)
722+
framefun = get_frame_fun_2d(sol, sys.frame_a)
723723
radius = sol(sol.t[1], idxs=sys.radius) |> Float32
724724
thing = @lift begin # Sphere
725725
# Ta = framefun($t)
@@ -814,4 +814,33 @@ function render!(scene, ::Union{typeof(P.Spring), typeof(P.SpringDamper)}, sys,
814814
true
815815
end
816816

817+
function render!(scene, ::Union{typeof(P.Wheel), typeof(P.SlipBasedWheelJoint)}, sys, sol, t)
818+
819+
r_0 = get_fun(sol, [sys.frame_a.x, sys.frame_a.y])
820+
rotfun = get_rot_fun_2d(sol, sys.frame_a)
821+
color = get_color(sys, sol, :red)
822+
823+
# TODO: add some form of assumetry to indicate that the wheel is rotating
824+
825+
radius = try
826+
sol(sol.t[1], idxs=sys.radius)
827+
catch
828+
0.05f0
829+
end |> Float32
830+
thing = @lift begin
831+
# radius = sol($t, idxs=sys.radius)
832+
O = [r_0($t)..., 0]
833+
# T_w_a = framefun($t)
834+
R_w_a = rotfun($t)
835+
n_a = [0,1] # Wheel rotates around y axis
836+
n_w = [R_w_a*n_a; 0] # Rotate to the world frame
837+
width = radius/10
838+
p1 = Point3f(O + width*n_w)
839+
p2 = Point3f(O - width*n_w)
840+
Makie.GeometryBasics.Cylinder(p1, p2, radius)
841+
end
842+
mesh!(scene, thing; color, specular = Vec3f(1.5), shininess=20f0, diffuse=Vec3f(1))
843+
true
844+
end
845+
817846
end

src/PlanarMechanics/components.jl

Lines changed: 189 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ Linear 2D translational spring damper model
376376
@variables begin
377377
v_relx(t)
378378
v_rely(t)
379-
ω_rel(t) = 0
379+
w_rel(t) = 0
380380
s_relx(t)
381381
s_rely(t)
382382
phi_rel(t) = 0
@@ -397,9 +397,9 @@ Linear 2D translational spring damper model
397397
phi_rel ~ frame_b.phi - frame_a.phi
398398
v_relx ~ D(s_relx)
399399
v_rely ~ D(s_rely)
400-
ω_rel ~ D(phi_rel)
400+
w_rel ~ D(phi_rel)
401401

402-
tau ~ c_phi * (phi_rel - phi_rel0) + d_phi * ω_rel
402+
tau ~ c_phi * (phi_rel - phi_rel0) + d_phi * w_rel
403403
frame_a.tau ~ -tau
404404
frame_b.tau ~ tau
405405
f_x ~ c_x * (s_relx - s_relx0) + d_x * v_relx
@@ -412,3 +412,189 @@ Linear 2D translational spring damper model
412412
# lossPower ~ d_x * v_relx * v_relx + d_y * v_rely * v_rely
413413
end
414414
end
415+
416+
417+
@mtkmodel SimpleWheel begin
418+
@structural_parameters begin
419+
friction_model = :viscous
420+
end
421+
@parameters begin
422+
(radius = 0.3), [description = "Radius of the wheel"]
423+
(color[1:4] = [1, 0, 0, 1]), [description = "Color of the wheel in animations"]
424+
μ = 1e9, [description = "Viscous friction coefficient"]
425+
# Fy0 = 1e4, [description = "Lateral friction force at zero longitudinal force"]
426+
# μx = Fy0, [description = "Maximum longitudinal friction force"]
427+
end
428+
@variables begin
429+
θ(t), [guess=0, description="wheel angle"] # wheel angle
430+
Vx(t), [guess=0, description="longitudinal velocity (resolved in local frame)"]
431+
Vy(t)=0, [description="lateral velocity (resolved in local frame)"]
432+
Fy(t), [guess=0, description="lateral friction force"]
433+
Fx(t), [guess=0, description="applied longitudinal wheel force"]
434+
end
435+
@components begin
436+
frame_a = Frame()
437+
thrust = Blocks.RealInput()
438+
end
439+
begin
440+
R_W_F = ori_2d(frame_a) # rotation matrix, local to global
441+
veqs = R_W_F'*[D(frame_a.x), D(frame_a.y)] #~ [Vx, Vy]
442+
feqs = R_W_F'*[frame_a.fx, frame_a.fy] #~ [Fx, Fy]
443+
end
444+
445+
446+
@equations begin
447+
θ ~ frame_a.phi
448+
449+
# road friction
450+
Fx ~ thrust.u
451+
# if friction_model == :viscous
452+
Fy ~ μ*Vy
453+
# elseif friction_model == :ellipse
454+
# Fy ~ Fy0*sqrt(1 - (Fx/μ)^2)*Vy
455+
# end
456+
veqs[1] ~ Vx
457+
veqs[2] ~ Vy
458+
feqs[1] ~ Fx
459+
feqs[2] ~ Fy
460+
461+
# R'*[D(frame.x), D(frame.y)] ~ [Vx, Vy]
462+
# R'*[frame.fx, frame.fy] ~ [Fx, Fy]
463+
464+
frame_a.tau ~ 0 # Assume that wheel does not transmit any torque
465+
end
466+
end
467+
468+
"""
469+
limit_S_triple(x_max, x_sat, y_max, y_sat, x)
470+
471+
Returns a point-symmetric Triple S-Function
472+
473+
A point symmetric interpolation between points `(0, 0), (x_max, y_max) and (x_sat, y_sat)`, provided `x_max < x_sat`. The approximation is done in such a way that the 1st function's derivative is zero at points `(x_max, y_max)` and `(x_sat, y_sat)`. Thus, the 1st function's derivative is continuous for all `x`. The higher derivatives are discontinuous at these points.
474+
"""
475+
function limit_S_triple(x_max, x_sat, y_max, y_sat, x)
476+
if x > x_max
477+
return limit_S_form(x_max, x_sat, y_max, y_sat, x)
478+
elseif x < -x_max
479+
return limit_S_form(-x_max, -x_sat, -y_max, -y_sat, x)
480+
else
481+
return limit_S_form(-x_max, x_max, -y_max, y_max, x)
482+
end
483+
end
484+
485+
486+
"""
487+
limit_S_form(x_min, x_max, y_min, y_max, x)
488+
489+
Returns a S-shaped transition
490+
491+
A smooth transition between points `(x_min, y_min)` and `(x_max, y_max)`. The transition is done in such a way that the 1st function's derivative is continuous for all `x`. The higher derivatives are discontinuous at input points.
492+
"""
493+
function limit_S_form(x_min, x_max, y_min, y_max, x)
494+
x2 = x - x_max/2 - x_min/2
495+
x2 = x2*2/(x_max-x_min)
496+
y = clamp(-0.5*x2^3 + 1.5*x2, -1, 1)
497+
y = y*(y_max-y_min)/2
498+
y = y + y_max/2 + y_min/2
499+
return y
500+
end
501+
502+
503+
@register_symbolic limit_S_triple(x_max::Real, x_sat::Real, y_max::Real, y_sat::Real, x::Real)
504+
@register_symbolic limit_S_form(x_min::Real, x_max::Real, y_min::Real, y_max::Real, x::Real)
505+
506+
507+
@component function SlipBasedWheelJoint(;
508+
name,
509+
r = [1, 0],
510+
N,
511+
vAdhesion_min,
512+
vSlide_min,
513+
sAdhesion,
514+
sSlide,
515+
mu_A,
516+
mu_S,
517+
render = true,
518+
color = [0.1, 0.1, 0.1, 1],
519+
z = 0,
520+
diameter = 0.1,
521+
width = diameter * 0.6,
522+
radius = 0.1,
523+
w_roll = nothing,
524+
)
525+
systems = @named begin
526+
frame_a = Frame()
527+
flange_a = Rotational.Flange()
528+
dynamicLoad = Blocks.RealInput()
529+
end
530+
pars = @parameters begin
531+
r[1:2] = r, [description = "Driving direction of the wheel at angle phi = 0"]
532+
N = N, [description = "Base normal load"]
533+
vAdhesion_min = vAdhesion_min, [description = "Minimum adhesion velocity"]
534+
vSlide_min = vSlide_min, [description = "Minimum sliding velocity"]
535+
sAdhesion = sAdhesion, [description = "Adhesion slippage"]
536+
sSlide = sSlide, [description = "Sliding slippage"]
537+
mu_A = mu_A, [description = "Friction coefficient at adhesion"]
538+
mu_S = mu_S, [description = "Friction coefficient at sliding"]
539+
render = render, [description = "Render the wheel in animations"]
540+
color[1:4] = color, [description = "Color of the wheel in animations"]
541+
z = 0, [description = "Position z of the body"]
542+
diameter = diameter, [description = "Diameter of the rims"]
543+
width = width, [description = "Width of the wheel"]
544+
radius = radius, [description = "Radius of the wheel"]
545+
end
546+
r = collect(r)
547+
l = Multibody._norm(r)
548+
e = Multibody._normalize(r) # Unit vector in direction of r
549+
550+
vars = @variables begin
551+
e0(t)[1:2], [description="Unit vector in direction of r resolved w.r.t. inertial frame"]
552+
phi_roll(t), [guess=0, description="wheel angle"] # wheel angle
553+
(w_roll(t)=w_roll), [guess=0, description="Roll velocity of wheel"]
554+
v(t)[1:2], [description="velocity"]
555+
v_lat(t), [guess=0, description="Driving in lateral direction"]
556+
v_long(t), [guess=0, description="Velocity in longitudinal direction"]
557+
v_slip_long(t), [guess=0, description="Slip velocity in longitudinal direction"]
558+
v_slip_lat(t), [guess=0, description="Slip velocity in lateral direction"]
559+
v_slip(t), [description="Slip velocity"]
560+
f(t), [description="Longitudinal force"]
561+
f_lat(t), [description="Longitudinal force"]
562+
f_long(t), [description="Longitudinal force"]
563+
fN(t), [description="Base normal load"]
564+
vAdhesion(t), [description="Adhesion velocity"]
565+
vSlide(t), [description="Sliding velocity"]
566+
end
567+
e0 = collect(e0)
568+
v = collect(v)
569+
vars = [
570+
e0; phi_roll; w_roll; v; v_lat; v_long; v_slip_long; v_slip_lat; v_slip; f; f_lat; f_long; fN; vAdhesion; vSlide
571+
]
572+
573+
R = ori_2d(frame_a)
574+
575+
equations = [
576+
e0 .~ R * e
577+
v .~ D.([frame_a.x, frame_a.y])
578+
579+
phi_roll ~ flange_a.phi
580+
w_roll ~ D(phi_roll)
581+
v_long ~ v' * e0
582+
v_lat ~ -v[1] * e0[2] + v[2] * e0[1]
583+
v_slip_lat ~ v_lat - 0
584+
v_slip_long ~ v_long - radius * w_roll
585+
v_slip ~ sqrt(v_slip_long^2 + v_slip_lat^2) + 0.0001
586+
-f_long * radius ~ flange_a.tau
587+
frame_a.tau ~ 0
588+
vAdhesion ~ max(vAdhesion_min, sAdhesion * abs(radius * w_roll))
589+
vSlide ~ max(vSlide_min, sSlide * abs(radius * w_roll))
590+
fN ~ max(0, N + dynamicLoad.u)
591+
f ~ fN * limit_S_triple(vAdhesion, vSlide, mu_A, mu_S, v_slip)
592+
f_long ~ f * v_slip_long / v_slip
593+
f_lat ~ f * v_slip_lat / v_slip
594+
f_long ~ [frame_a.fx, frame_a.fy]'e0
595+
f_lat ~ [frame_a.fy, -frame_a.fx]'e0
596+
]
597+
598+
return ODESystem(equations, t, vars, pars; name, systems)
599+
600+
end

src/PlanarMechanics/utils.jl

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
@connector Frame begin
2-
x(t), [description = "x position"]
3-
y(t), [description = "y position"]
4-
phi(t), [state_priority=2, description = "rotation angle (counterclockwise)"]
5-
fx(t), [connect = Flow, description = "force in the x direction"]
6-
fy(t), [connect = Flow, description = "force in the y direction"]
7-
tau(t), [connect = Flow, description = "torque (clockwise)"]
1+
@connector function Frame(; name, render=false, length=1.0, radius=0.1)
2+
vars = @variables begin
3+
x(t), [description = "x position"]
4+
y(t), [description = "y position"]
5+
phi(t), [state_priority=2, description = "rotation angle (counterclockwise)"]
6+
fx(t), [connect = Flow, description = "force in the x direction"]
7+
fy(t), [connect = Flow, description = "force in the y direction"]
8+
tau(t), [connect = Flow, description = "torque (clockwise)"]
9+
end
10+
pars = @parameters begin
11+
render = render, [description = "Render the joint in animations"]
12+
length = length, [description = "Length of each axis in animations"]
13+
radius = radius, [description = "Radius of each axis in animations"]
14+
end
15+
16+
ODESystem(Equation[], t, vars, pars; name, metadata = Dict(:frame_2d => true))
817
end
918
Base.@doc """
1019
Frame(;name)
1120
1221
Coordinate system (2-dim.) fixed to the component with one cut-force and cut-torque
1322
14-
# States:
15-
- `x`: [m] x position
16-
- `y`: [m] y position
17-
- `phi`: [rad] rotation angle (counterclockwise)
18-
- `fx`: [N] force in the x direction
19-
- `fy`: [N] force in the y direction
20-
- `tau`: [N.m] torque (clockwise)
23+
# Variables:
24+
- `x`: [m] x position
25+
- `y`: [m] y position
26+
- `phi`: [rad] rotation angle (counterclockwise)
27+
- `fx`: [N] force in the x direction
28+
- `fy`: [N] force in the y direction
29+
- `tau`: [N.m] torque (clockwise)
30+
31+
# Parameters:
32+
- `render`: [Bool] Render the joint in animations
33+
- `length`: [m] Length of each axis in animations
34+
- `radius`: [m] Radius of each axis in animations
2135
""" Frame
2236

2337
function ori_2d(frame)

0 commit comments

Comments
 (0)