Skip to content

Commit 38abc92

Browse files
authored
more wheel updates (#93)
* more wheel updates * wheel solves but goes in the wrong direction * test passes with world.n hacks * finishing touches * try global scoping n * set world parameter defaults using keyword * redefine defaults again * scalarize world n * switch to wheel rolling on xz plane * move wheel tests to own file
1 parent 70346cc commit 38abc92

File tree

7 files changed

+178
-79
lines changed

7 files changed

+178
-79
lines changed

ext/Render.jl

+28-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ function render!(scene, ::typeof(Frame), sys, sol, t)
341341
true
342342
end
343343

344-
function render!(scene, T::Union{typeof(Revolute), typeof(RevolutePlanarLoopConstraint)}, sys, sol, t)
344+
function render!(scene, ::Union{typeof(Revolute), typeof(RevolutePlanarLoopConstraint)}, sys, sol, t)
345345
r_0 = get_fun(sol, collect(sys.frame_a.r_0))
346346
n = get_fun(sol, collect(sys.n))
347347
color = get_color(sys, sol, :red)
@@ -533,7 +533,34 @@ function render!(scene, ::typeof(UniversalSpherical), sys, sol, t)
533533
Sphere(r2, sphere_diameter/2)
534534
end
535535
mesh!(scene, thing; color=sphere_color, specular = Vec3f(1.5))
536+
true
537+
end
536538

539+
function render!(scene, ::typeof(RollingWheelJoint), sys, sol, t)
540+
541+
r_0 = get_fun(sol, collect(sys.frame_a.r_0))
542+
# framefun = get_frame_fun(sol, sys.frame_a)
543+
rotfun = get_rot_fun(sol, sys.frame_a)
544+
color = get_color(sys, sol, :red)
545+
546+
radius = try
547+
sol(sol.t[1], idxs=sys.radius)
548+
catch
549+
0.05f0
550+
end |> Float32
551+
thing = @lift begin
552+
# radius = sol($t, idxs=sys.radius)
553+
O = r_0($t)
554+
# T_w_a = framefun($t)
555+
R_w_a = rotfun($t)
556+
n_w = R_w_a[:, 3] # Wheel rotates around z axis
557+
# n_w = R_w_a*n_a # Rotate to the world frame
558+
width = radius/10
559+
p1 = Point3f(O + width*n_w)
560+
p2 = Point3f(O - width*n_w)
561+
Makie.GeometryBasics.Cylinder(p1, p2, radius)
562+
end
563+
mesh!(scene, thing; color, specular = Vec3f(1.5), shininess=20f0, diffuse=Vec3f(1))
537564
true
538565
end
539566

src/components.jl

+13-9
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,27 @@ end
3838
"""
3939
World(; name, render=true)
4040
"""
41-
@component function World(; name, render=true, point_gravity=false)
41+
@component function World(; name, render=true, point_gravity=false, n = [0.0, -1.0, 0.0], g=9.80665, mu=3.986004418e14)
4242
# World should have
4343
# 3+3+9+3 // r_0+f+R.R+τ
4444
# - (3+3) // (f+t)
4545
# = 12 equations
46+
n0 = n
47+
g0 = g
48+
mu0 = mu
4649
@named frame_b = Frame()
47-
@parameters n[1:3]=[0, -1, 0] [description = "gravity direction of world"]
48-
@parameters g=9.80665 [description = "gravitational acceleration of world"]
49-
@parameters mu=3.986004418e14 [description = "Gravity field constant [m³/s²] (default = field constant of earth)"]
50+
@parameters n[1:3]=n0 [description = "gravity direction of world"]
51+
@parameters g=g0 [description = "gravitational acceleration of world"]
52+
@parameters mu=mu0 [description = "Gravity field constant [m³/s²] (default = field constant of earth)"]
5053
@parameters render=render
5154
@parameters point_gravity = point_gravity
55+
n = Symbolics.scalarize(n)
5256
O = ori(frame_b)
53-
eqs = Equation[collect(frame_b.r_0) .~ 0;
54-
O ~ nullrotation()
55-
# vec(D(O).R .~ 0); # QUESTION: not sure if I should have to add this, should only have 12 equations according to modelica paper
56-
]
57-
ODESystem(eqs, t, [], [n; g; mu; point_gravity; render]; name, systems = [frame_b])
57+
eqs = Equation[
58+
collect(frame_b.r_0) .~ 0;
59+
O ~ nullrotation()
60+
]
61+
ODESystem(eqs, t, [], [n; g; mu; point_gravity; render]; name, systems = [frame_b])#, defaults=[n => n0; g => g0; mu => mu0])
5862
end
5963

6064
"""

src/joints.jl

+39-38
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,9 @@ end
449449
"""
450450
RollingWheelJoint(; name, radius, angles, x0, y0, z0)
451451
452-
Joint (no mass, no inertia) that describes an ideal rolling wheel (rolling on the plane z=0). See [`RollingWheel`](@ref) for a realistic wheel model with inertia.
452+
Joint (no mass, no inertia) that describes an ideal rolling wheel (rolling on the plane y=0). See [`RollingWheel`](@ref) for a realistic wheel model with inertia.
453453
454-
A joint for a wheel rolling on the x-y plane of the world frame.
454+
A joint for a wheel rolling on the x-z plane of the world frame.
455455
The rolling contact is considered being ideal, i.e. there is no
456456
slip between the wheel and the ground. This is simply
457457
gained by two non-holonomic constraint equations on velocity level
@@ -462,22 +462,22 @@ the wheel can not take off.
462462
463463
The origin of the frame `frame_a` is placed in the intersection
464464
of the wheel spin axis with the wheel middle plane and rotates
465-
with the wheel itself. The y-axis of `frame_a` is identical with
466-
the wheel spin axis, i.e. the wheel rotates about y-axis of `frame_a`.
465+
with the wheel itself. The z-axis of `frame_a` is identical with
466+
the wheel spin axis, i.e. the wheel rotates about z-axis of `frame_a`.
467467
A wheel body collecting the mass and inertia should be connected to
468468
this frame.
469469
470470
# Arguments and parameters:
471471
472472
name: Name of the rolling wheel joint component
473473
radius: Radius of the wheel
474-
angles: Angles to rotate world-frame into frame_a around z-, y-, x-axis
474+
angles: Angles to rotate world-frame into frame_a around y-, z-, x-axis
475475
476476
# Variables:
477477
- `x`: x-position of the wheel axis
478478
- `y`: y-position of the wheel axis
479479
- `z`: z-position of the wheel axis
480-
- `angles`: Angles to rotate world-frame into `frame_a` around z-, y-, x-axis
480+
- `angles`: Angles to rotate world-frame into `frame_a` around y-, z-, x-axis
481481
- `der_angles`: Derivatives of angles
482482
- `r_road_0`: Position vector from world frame to contact point on road, resolved in world frame
483483
- `f_wheel_0`: Force vector on wheel, resolved in world frame
@@ -500,16 +500,15 @@ angles: Angles to rotate world-frame into frame_a around z-, y-, x-axis
500500
# Connector frames
501501
- `frame_a`: Frame for the wheel joint
502502
"""
503-
function RollingWheelJoint(; name, radius, angles = zeros(3), x0, y0, z0 = 0, sequence = [3, 2, 1])
504-
@named frame_a = Frame(varw=true)
503+
@component function RollingWheelJoint(; name, radius, angles = zeros(3), der_angles=zeros(3), x0=0, y0 = radius, z0=0, sequence = [2, 3, 1], iscut=false)
505504
@parameters begin radius = radius, [description = "Radius of the wheel"] end
506505
@variables begin
507-
(x(t) = x0), [state_priority = 1, description = "x-position of the wheel axis"]
508-
(y(t) = y0), [state_priority = 1, description = "y-position of the wheel axis"]
509-
(z(t) = z0), [state_priority = 1, description = "z-position of the wheel axis"]
506+
(x(t) = x0), [state_priority = 15, description = "x-position of the wheel axis"]
507+
(y(t) = y0), [state_priority = 0, description = "y-position of the wheel axis"]
508+
(z(t) = z0), [state_priority = 15, description = "z-position of the wheel axis"]
510509
(angles(t)[1:3] = angles),
511-
[description = "Angles to rotate world-frame into frame_a around z-, y-, x-axis"]
512-
(der_angles(t)[1:3] = zeros(3)), [description = "Derivatives of angles"]
510+
[state_priority = 5, description = "Angles to rotate world-frame into frame_a around z-, y-, x-axis"]
511+
(der_angles(t)[1:3] = der_angles), [state_priority = 5, description = "Derivatives of angles"]
513512
(r_road_0(t)[1:3] = zeros(3)),
514513
[
515514
description = "Position vector from world frame to contact point on road, resolved in world frame",
@@ -528,7 +527,7 @@ function RollingWheelJoint(; name, radius, angles = zeros(3), x0, y0, z0 = 0, se
528527
# ]
529528
(e_axis_0(t)[1:3] = zeros(3)),
530529
[description = "Unit vector along wheel axis, resolved in world frame"]
531-
(delta_0(t)[1:3] = [0,0,-radius]),
530+
(delta_0(t)[1:3] = [0,-radius, 0]),
532531
[description = "Distance vector from wheel center to contact point"]
533532
(e_n_0(t)[1:3] = zeros(3)),
534533
[
@@ -543,8 +542,8 @@ function RollingWheelJoint(; name, radius, angles = zeros(3), x0, y0, z0 = 0, se
543542
description = "Unit vector in longitudinal direction of road at contact point, resolved in world frame",
544543
]
545544

546-
(s(t) = 0), [state_priority = 1, description = "Road surface parameter 1"]
547-
(w(t) = 0), [state_priority = 1, description = "Road surface parameter 2"]
545+
(s(t) = 0), [description = "Road surface parameter 1"]
546+
(w(t) = 0), [description = "Road surface parameter 2"]
548547
(e_s_0(t)[1:3] = zeros(3)),
549548
[description = "Road heading at (s,w), resolved in world frame (unit vector)"]
550549

@@ -560,25 +559,26 @@ function RollingWheelJoint(; name, radius, angles = zeros(3), x0, y0, z0 = 0, se
560559

561560
angles,der_angles,r_road_0,f_wheel_0,e_axis_0,delta_0,e_n_0,e_lat_0,e_long_0,e_s_0,v_0,w_0,vContact_0,aux = collect.((angles,der_angles,r_road_0,f_wheel_0,e_axis_0,delta_0,e_n_0,e_lat_0,e_long_0,e_s_0,v_0,w_0,vContact_0,aux))
562561

562+
@named frame_a = Frame(varw=true)
563563
Ra = ori(frame_a, true)
564564

565-
Rarot = axes_rotations(sequence, angles, der_angles)
565+
Rarot = axes_rotations(sequence, angles, -der_angles) # The - is the neg_w change
566566

567567
equations = [
568-
Ra ~ Rarot
568+
connect_orientation(Ra, Rarot; iscut) # Ra ~ Rarot
569569
Ra.w ~ Rarot.w
570570

571571
# frame_a.R is computed from generalized coordinates
572572
collect(frame_a.r_0) .~ [x, y, z]
573573
der_angles .~ D.(angles)
574574

575575
# Road description
576-
r_road_0 .~ [s, w, 0]
577-
e_n_0 .~ [0, 0, 1]
576+
r_road_0 .~ [s, 0, w]
577+
e_n_0 .~ [0, 1, 0]
578578
e_s_0 .~ [1, 0, 0]
579579

580580
# Coordinate system at contact point (e_long_0, e_lat_0, e_n_0)
581-
e_axis_0 .~ resolve1(Ra, [0, 1, 0])
581+
e_axis_0 .~ resolve1(Ra, [0, 0, 1])
582582
aux .~ (cross(e_n_0, e_axis_0))
583583
e_long_0 .~ (aux ./ _norm(aux))
584584
e_lat_0 .~ (cross(e_long_0, e_n_0))
@@ -616,14 +616,14 @@ end
616616
"""
617617
RollingWheel(; name, radius, m, I_axis, I_long, width=0.035, x0, y0, kwargs...)
618618
619-
Ideal rolling wheel on flat surface z=0 (5 positional, 3 velocity degrees of freedom)
619+
Ideal rolling wheel on flat surface y=0 (5 positional, 3 velocity degrees of freedom)
620620
621-
A wheel rolling on the x-y plane of the world frame including wheel mass.
621+
A wheel rolling on the x-z plane of the world frame including wheel mass.
622622
The rolling contact is considered being ideal, i.e. there is no
623623
slip between the wheel and the ground.
624624
The wheel can not take off but it can incline toward the ground.
625-
The frame frame_a is placed in the wheel center point and rotates
626-
with the wheel itself.
625+
The frame `frame_a` is placed in the wheel center point and rotates
626+
with the wheel itself. A [`Revolute`](@ref) joint rotationg around `n = [0, 1, 0]` is required to attach the wheel to a wheel axis.
627627
628628
# Arguments and parameters:
629629
- `name`: Name of the rolling wheel component
@@ -633,29 +633,30 @@ with the wheel itself.
633633
- `I_long`: Moment of inertia of the wheel perpendicular to its axis
634634
- `width`: Width of the wheel (default: 0.035)
635635
- `x0`: Initial x-position of the wheel axis
636-
- `y0`: Initial y-position of the wheel axis
636+
- `z0`: Initial z-position of the wheel axis
637637
- `kwargs...`: Additional keyword arguments passed to the `RollingWheelJoint` function
638638
639639
# Variables:
640640
- `x`: x-position of the wheel axis
641-
- `y`: y-position of the wheel axis
642-
- `angles`: Angles to rotate world-frame into `frame_a` around z-, y-, x-axis
643-
- `der_angles`: Derivatives of angles
641+
- `z`: z-position of the wheel axis
642+
- `angles`: Angles to rotate world-frame into `frame_a` around y-, z-, x-axis
643+
- `der_angles`: Derivatives of angles (y: like rotational velocity of a spinning coin, z: wheel forward spin speed, x: wheel falling over speed)
644644
645645
# Named components:
646646
- `frame_a`: Frame for the wheel component
647647
- `rollingWheel`: Rolling wheel joint representing the wheel's contact with the road surface
648648
"""
649-
@component function RollingWheel(; name, radius, m, I_axis, I_long, width = 0.035, x0, y0,
649+
@component function RollingWheel(; name, radius, m, I_axis, I_long, width = 0.035, x0=0, z0=0,
650650
angles = zeros(3), der_angles = zeros(3), kwargs...)
651651
@named begin
652652
frame_a = Frame()
653-
rollingWheel = RollingWheelJoint(; radius, angles, x0, y0, kwargs...)
653+
rollingWheel = RollingWheelJoint(; radius, angles, x0, z0, der_angles, kwargs...)
654654
body = Body(r_cm = [0, 0, 0],
655+
state_priority = 0,
655656
m = m,
656657
I_11 = I_long,
657-
I_22 = I_axis,
658-
I_33 = I_long,
658+
I_22 = I_long,
659+
I_33 = I_axis,
659660
I_21 = 0,
660661
I_31 = 0,
661662
I_32 = 0)
@@ -669,16 +670,16 @@ with the wheel itself.
669670
width = width, [description = "Width of the wheel"]
670671
end
671672
sts = @variables begin
672-
(x(t) = x0), [state_priority = 2.0, description = "x-position of the wheel axis"]
673-
(y(t) = y0), [state_priority = 2.0, description = "y-position of the wheel axis"]
673+
(x(t) = x0), [state_priority = 20, description = "x-position of the wheel axis"]
674+
(z(t) = z0), [state_priority = 20, description = "z-position of the wheel axis"]
674675
(angles(t)[1:3] = angles),
675-
[description = "Angles to rotate world-frame into frame_a around z-, y-, x-axis"]
676-
(der_angles(t)[1:3] = der_angles), [state_priority = 3.0, description = "Derivatives of angles"]
676+
[state_priority = 30, description = "Angles to rotate world-frame into frame_a around y-, z-, x-axis"]
677+
(der_angles(t)[1:3] = der_angles), [state_priority = 30, description = "Derivatives of angles"]
677678
end
678679
# sts = reduce(vcat, collect.(sts))
679680

680681
equations = Equation[rollingWheel.x ~ x
681-
rollingWheel.y ~ y
682+
rollingWheel.z ~ z
682683
collect(rollingWheel.angles) .~ collect(angles)
683684
collect(rollingWheel.der_angles) .~ collect(der_angles)
684685
connect(body.frame_a, frame_a)

src/orientation.jl

+12-2
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,18 @@ end
145145
skew(s) = [0 -s[3] s[2]; s[3] 0 -s[1]; -s[2] s[1] 0]
146146
skewcoords(R::AbstractMatrix) = [R[3, 2]; R[1, 3]; R[2, 1]]
147147

148-
function planar_rotation(axis, phi, der_angle)
148+
149+
"""
150+
planar_rotation(axis, phi, phid)
151+
152+
Generate a rotation matrix for a rotation around the specified axis (axis-angle representation).
153+
"""
154+
function planar_rotation(axis, phi, phid)
149155
length(axis) == 3 || error("axis must be a 3-vector")
150156
axis = collect(axis)
151157
ee = collect(axis * axis')
152158
R = ee + (I(3) - ee) * cos(phi) - skew(axis) * sin(phi)
153-
w = axis * der_angle
159+
w = axis * phid
154160
RotationMatrix(R, w)
155161
end
156162

@@ -279,7 +285,11 @@ to_mb(Q::Rotations.QuatRotation) = to_mb(vec(Q))
279285

280286

281287
## Euler
288+
"""
289+
axes_rotations(sequence, angles, der_angles; name = :R_ar)
282290
291+
Generate a rotation matrix for a rotation around the specified axes (Euler/Cardan angles).
292+
"""
283293
function axes_rotations(sequence, angles, der_angles, name = :R_ar)
284294
R = axis_rotation(sequence[3], angles[3]) *
285295
axis_rotation(sequence[2], angles[2]) *

test/runtests.jl

+4-29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using Multibody
33
using Test
44
using JuliaSimCompiler
55
using OrdinaryDiffEq
6+
using LinearAlgebra
67
t = Multibody.t
78
D = Differential(t)
89
doplot() = false
@@ -740,35 +741,9 @@ sol = solve(prob, Rodas4())
740741
@test sol[cm.inertia1.flange_a.tau] 10*sol[cm.inertia2.flange_a.tau] rtol=1e-2
741742
end
742743

743-
# ==============================================================================
744-
## Rolling wheel ===============================================================
745-
# ==============================================================================
746-
using LinearAlgebra
747-
# The wheel does not need the world
748-
@testset "Rolling wheel" begin
749-
@named wheel = RollingWheel(radius = 0.3, m = 2, I_axis = 0.06,
750-
I_long = 0.12,
751-
x0 = 0.2,
752-
y0 = 0.2,
753-
der_angles = [0, 5, 1])
754-
755-
756-
wheel = complete(wheel)
757-
758-
defs = [
759-
collect(world.n .=> [0, 0, -1]);
760-
vec(ori(wheel.frame_a).R .=> I(3));
761-
vec(ori(wheel.body.frame_a).R .=> I(3));
762-
# collect(D.(cwheel.rollingWheel.angles)) .=> [0, 5, 1]
763-
]
764-
765-
@test_skip begin # Does not initialize
766-
ssys = structural_simplify(IRSystem(wheel))
767-
prob = ODEProblem(ssys, defs, (0, 10))
768-
sol = solve(prob, Rodas5P(autodiff=false))
769-
@info "Write tests"
770-
end
771-
744+
@testset "wheels" begin
745+
@info "Testing wheels"
746+
include("test_wheels.jl")
772747
end
773748

774749
# ==============================================================================

test/test_quaternions.jl

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# test utils
22
import Multibody.Rotations.QuatRotation as Quat
33
import Multibody.Rotations
4+
import Multibody.Rotations: RotXYZ
45
function get_R(sol, frame, t)
56
reshape(sol(t, idxs=vec(ori(frame).R.mat)), 3, 3)
67
end

0 commit comments

Comments
 (0)