Skip to content

Commit 7eeef35

Browse files
committed
Revolute(..) and Prismatic(..) joints can define axis of rotation/translation optionally as vector, e.g., axis = [1.0, 2.0, 3.0].
1 parent 01c825d commit 7eeef35

File tree

8 files changed

+88
-93
lines changed

8 files changed

+88
-93
lines changed

docs/src/index.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ julia -JModia3D_sysimage.so (otherwise)
101101
- Additional keyword arguments of Object3D: `Object3D(..., fixedInParent=true, velocity=[0.0, 0.0, 0.0], angularVelocity=[0.0, 0.0, 0.0])`.
102102
A freely moving Object3D is defined with `Object3D(..., fixedInParent=false, ...)`. The states and other code for such Object3Ds are
103103
no longer visible in the generated code (so compilation is faster).
104-
104+
105+
- `Revolute(..)` and `Prismatic(..)` joints can define axis of rotation/translation optionally as vector, e.g., `axis = [1.0, 2.0, 3.0]`.
106+
105107
- New variants of functions: `Modia3D.rot1(angle,v), Modia3D.rot2(angle,v), Modia3D.rot3(angle,v), Modia3D.resolve1(rotation,v2), Modia3D.resolve2(rotation,v1)`.
106108

107109
**Deprecated**
@@ -110,13 +112,13 @@ julia -JModia3D_sysimage.so (otherwise)
110112
Note, Object3D has variables `translation, rotation, velocity, angularVelocity` instead of `r, rot, v, w` of `FreeMotion`.
111113
Furthermore, `angularVelocity` is resolved in the parent `Object3D` whereas `w` in `FreeMotion(obj1=.., obj2=..., ..)` is resolved in
112114
`obj2` and not in `obj1`. This means in particular that the init/start value `FreeMotion(.., w=Var(start=w_start)...)` needs
113-
to be transformed in Object3D with `Object3D(..., fixedInParent=false, rotation=xxx, angularVelocity = Modia3D.resolve1(rotation,w_start))`.
114-
115+
to be transformed in Object3D with `Object3D(..., fixedInParent=false, rotation=XXX, angularVelocity = Modia3D.resolve1(XXX,w_start))`.
116+
As a side effect,
115117

116118
**Non-backwards compatible changes**
117119

118120
- Since Modia3D 0.11.0 is based on Modia 0.9.0, the non-backwards compatible changes of Modia have also an effect on Modia3D
119-
(for details, see the release notes of Modia 0.9.0).
121+
(for details, see the release notes of Modia 0.9.0). Typically, this should give problems only in seldomly occuring corner cases.
120122

121123

122124
### Version 0.10.4

src/Composition/joints/Prismatic.jl

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,19 @@
55
# Modia3D.Composition (Modia3D/Composition/_module.jl)
66
#
77

8-
9-
10-
get_eAxis(::Type{F}, axis::Int) where F <: Modia3D.VarFloatType = axis== 1 ? SVector{3,F}( 1.0, 0.0, 0.0) :
11-
axis== 2 ? SVector{3,F}( 0.0, 1.0, 0.0) :
12-
axis== 3 ? SVector{3,F}( 0.0, 0.0, 1.0) :
13-
axis== -1 ? SVector{3,F}( -1.0, 0.0, 0.0) :
14-
axis== -2 ? SVector{3,F}( 0.0, -1.0, 0.0) :
15-
axis== -3 ? SVector{3,F}( 0.0, 0.0, -1.0) :
16-
error("Modia3D.Composition.Prismatic: axis = ", axis, " but must be 1, 2, 3, -1, -2, or -3.")
17-
188
"""
199
joint = Prismatic(; obj1, obj2, axis=1, s=0, v=0, canCollide=true)
2010
21-
Return a `joint` that translates `obj2::`[`Object3D`](@ref) with respect to
22-
`obj1::`[`Object3D`](@ref) along coordinate axis `axis` (`axis = 1,2,3,-1,-2,-3`)
23-
of `obj1`. The initial position is `s` and the initial velocity is `v`. Negative
24-
`axis` values describe axes in negative axis directions, i.e. the signs of the values
25-
of `s` and `v` are reversed. If `canCollide=false`, no collision detection will occur
26-
between `obj1` and `obj2` (and `Object3D`s that are directly or indirectly rigidly
27-
fixed to `obj1` or `obj2`).
11+
Return a Prismatic `joint` that translates `obj2::`[`Object3D`](@ref) with respect to
12+
`obj1::`[`Object3D`](@ref) along coordinate axis `axis` (`axis = 1,2,3,-1,-2,-3`)
13+
of `obj1` with signed distance `s`. Optionally, `axis` can be a vector, such as `axis = [1.0, 2.0, 3.0]`,
14+
defining the direction of the axis of translation in `obj1`. The initial position is `s`
15+
and the initial velocity is `v`.
16+
17+
If `canCollide=false`, no collision detection will occur between `obj1` and `obj2` (and `Object3D`s that are
18+
directly or indirectly rigidly fixed to `obj1` or `obj2`). Note, if both `obj1` and `obj2` have
19+
solids defined that are overlapping along the axis of translation, then `canCollide=false`
20+
is the only meaningful value.
2821
2922
It is currently not supported that a `Prismatic` joint *closes a kinematic loop*.
3023
"""
@@ -34,53 +27,51 @@ mutable struct Prismatic{F <: Modia3D.VarFloatType} <: Modia3D.AbstractJoint
3427
obj1::Object3D{F}
3528
obj2::Object3D{F}
3629

37-
posAxis::Int # = 1,2,3
38-
posMovement::Bool # = true, if movement along posAxis, otherwise in negative posAxis
39-
eAxis::SVector{3,F} # Unit vector in direction of axis
40-
ndof::Int
41-
canCollide::Bool # = false, if no collision between obj1 and obj2
30+
eAxis::SVector{3,F} # Translation axis with norm(eAxis) = 1
31+
canCollide::Bool # = false, if no collision between obj1 and obj2
4232

4333
s::F
4434
v::F
4535
a::F
4636

4737
function Prismatic{F}(; obj1::Object3D{F},
48-
obj2::Object3D{F},
49-
path::String="",
50-
axis::Int = 1,
51-
s::Real = F(0.0),
52-
v::Real = F(0.0),
53-
canCollide::Bool = true) where F <: Modia3D.VarFloatType
38+
obj2::Object3D{F},
39+
path::String="",
40+
axis::Union{Int, AbstractVector} = 1,
41+
s::Real = F(0.0),
42+
v::Real = F(0.0),
43+
canCollide::Bool = true) where F <: Modia3D.VarFloatType
5444

5545
(parent,obj,cutJoint) = attach(obj1, obj2, name = "Prismatic joint") # an error is triggered if cutJoint=true
5646

57-
if !(1 <= abs(axis) <= 3)
58-
error("\nError from Prismatic joint connecting ", Modia3D.fullName(obj1), " with ", Modia3D.fullName(obj2), ":\n",
59-
" axis = $axis, but must be 1,2,3 or -1,-2-3.!")
47+
if typeof(axis) <: Int
48+
eAxis = abs(axis) == 1 ? SVector{3,F}(1, 0, 0) :
49+
(abs(axis) == 2 ? SVector{3,F}(0, 1, 0) :
50+
(abs(axis) == 3 ? SVector{3,F}(0, 0, 1) :
51+
error("\nError from $path = Prismatic(obj1 = :($(obj1.path)), obj2 = :($(obj2.path)), axis = $axis, ...):\n",
52+
" axis = must be 1,2,3 or -1,-2,-3 or a vector with 3 elements!")))
53+
if axis < 0
54+
eAxis = -eAxis
55+
end
56+
else
57+
eAxis = normalize(SVector{3,F}(ustrip.(axis)))
58+
if !all(isfinite,eAxis)
59+
error("\nError from $path = Prismatic(obj1 = :($(obj1.path)), obj2 = :($(obj2.path)), axis = $axis, ...):\n",
60+
" normalize(axis) results in vector $eAxis that has non-finite elements!")
61+
end
6062
end
6163

62-
if !(typeof(s) <: AbstractFloat) || !(typeof(v) <: AbstractFloat)
63-
error("\nError from Prismatic joint connecting ", Modia3D.fullName(obj1), " with ", Modia3D.fullName(obj2), ":\n",
64-
" s and/or v are not <: AbstractFloat!")
65-
end
66-
67-
axis = obj === obj2 ? axis : -axis
68-
eAxis = get_eAxis(F, axis)
69-
64+
eAxis = obj === obj2 ? eAxis : -eAxis
7065
s = Modia3D.convertAndStripUnit(F, u"m" , s)
7166
v = Modia3D.convertAndStripUnit(F, u"m/s", v)
7267

73-
posAxis = abs(axis)
74-
posMovement = axis > 0
75-
76-
obj.joint = new(path, parent, obj, posAxis, posMovement, eAxis, 1, canCollide, s, v, F(0.0) )
68+
obj.joint = new(path, parent, obj, eAxis, canCollide, s, v, F(0.0) )
7769
obj.jointKind = PrismaticKind
7870
obj.jointIndex = 0
7971
obj.ndof = 1
8072
obj.canCollide = canCollide
8173
obj.r_rel = eAxis*s
8274
obj.R_rel = Modia3D.NullRotation(F)
83-
8475
parent.hasChildJoint = true
8576
return obj.joint
8677
end

src/Composition/joints/Revolute.jl

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
# This file is part of module
55
# Modia3D.Composition (Modia3D/Composition/_module.jl)
66
#
7-
import Modia3D.Frames
8-
97

108
"""
119
joint = Revolute(;obj1, obj2, axis=3, phi=0, w=0, canCollide=false)
1210
1311
Return a Revolute `joint` that rotates `obj1::`[`Object3D`](@ref) into
14-
`obj2::`[`Object3D`](@ref) along the axis `axis` of `obj1` (`axis = 1,2,3,-1,-2,-3`).
15-
The initial start angle is `phi` and the initial angular velocity
16-
is `w`. Negative `axis` values describe axes in negative axis directions, i.e.
17-
the signs of the values of `phi` and `w` are reversed. If `canCollide=false`, no
18-
collision detection will occur between `obj1` and `obj2` (and `Object3D`s that are
19-
directly or indirectly rigidly fixed to `obj1` or `obj2`).
12+
`obj2::`[`Object3D`](@ref) along the axis `axis` of `obj1` (`axis = 1,2,3,-1,-2,-3`) with angle `phi`.
13+
Optionally, `axis` can be a vector, such as `axis = [1.0, 2.0, 3.0]`, defining the direction of the
14+
axis of rotation in `obj1`. The initial start angle is `phi` and the initial angular velocity
15+
is `w`.
16+
17+
If `canCollide=false`, no collision detection will occur between `obj1` and `obj2` (and `Object3D`s that are
18+
directly or indirectly rigidly fixed to `obj1` or `obj2`). Note, if both `obj1` and `obj2` have
19+
solids defined that are overlapping around the axis of rotation, then collision will be permanently
20+
occuring and `canCollide=false` is the only meaningful value.
2021
2122
It is currently not supported that a `Revolute` joint *closes a kinematic loop*.
2223
"""
@@ -26,49 +27,51 @@ mutable struct Revolute{F <: Modia3D.VarFloatType} <: Modia3D.AbstractJoint
2627
obj1::Object3D{F}
2728
obj2::Object3D{F}
2829

29-
posAxis::Int # = 1,2,3
30-
posMovement::Bool # = true, if movement along posAxis, otherwise in negative posAxis
31-
ndof::Int
32-
canCollide::Bool # = false, if no collision between obj1 and obj2
30+
eAxis::SVector{3,F} # Rotation axis with norm(eAxis) = 1
31+
canCollide::Bool # = false, if no collision between obj1 and obj2
3332

3433
phi::F
3534
w::F
3635
a::F
3736

3837
function Revolute{F}(; obj1::Object3D{F},
39-
obj2::Object3D{F},
40-
path::String="",
41-
axis::Int = 3,
42-
phi::Real = F(0.0),
43-
w::Real = F(0.0),
44-
canCollide::Bool = false) where F <: Modia3D.VarFloatType
38+
obj2::Object3D{F},
39+
path::String="",
40+
axis::Union{Int, AbstractVector} = 3,
41+
phi::Real = F(0.0),
42+
w::Real = F(0.0),
43+
canCollide::Bool = false) where F <: Modia3D.VarFloatType
4544

4645
(parent,obj,cutJoint) = attach(obj1, obj2, name = "Revolute joint") # an error is triggered if cutJoint=true
4746

48-
if !(1 <= abs(axis) <= 3)
49-
error("\nError from Revolute joint connecting ", Modia3D.fullName(obj1), " with ", Modia3D.fullName(obj2), ":\n",
50-
" axis = $axis, but must be 1,2,3 or -1,-2-3.!")
47+
if typeof(axis) <: Int
48+
eAxis = abs(axis) == 1 ? SVector{3,F}(1, 0, 0) :
49+
(abs(axis) == 2 ? SVector{3,F}(0, 1, 0) :
50+
(abs(axis) == 3 ? SVector{3,F}(0, 0, 1) :
51+
error("\nError from $path = Revolute(obj1 = :($(obj1.path)), obj2 = :($(obj2.path)), axis = $axis, ...):\n",
52+
" axis = must be 1,2,3 or -1,-2,-3 or a vector with 3 elements!")))
53+
if axis < 0
54+
eAxis = -eAxis
55+
end
56+
else
57+
eAxis = normalize(SVector{3,F}(ustrip.(axis)))
58+
if !all(isfinite,eAxis)
59+
error("\nError from $path = Revolute(obj1 = :($(obj1.path)), obj2 = :($(obj2.path)), axis = $axis, ...):\n",
60+
" normalize(axis) results in vector $eAxis that has non-finite elements!")
61+
end
5162
end
5263

53-
if !(typeof(phi) <: AbstractFloat) || !(typeof(w) <: AbstractFloat)
54-
error("\nError from Revolute joint connecting ", Modia3D.fullName(obj1), " with ", Modia3D.fullName(obj2), ":\n",
55-
" phi and/or w are not <: AbstractFloat!")
56-
end
57-
58-
axis = obj === obj2 ? axis : -axis
64+
eAxis = obj === obj2 ? eAxis : -eAxis
5965
phi = Modia3D.convertAndStripUnit(F, u"rad" , phi)
6066
w = Modia3D.convertAndStripUnit(F, u"rad/s", w)
6167

62-
posAxis = abs(axis)
63-
posMovement = axis > 0
64-
65-
obj.joint = new(path, parent, obj, posAxis, posMovement, 1, canCollide, phi, w, F(0.0))
68+
obj.joint = new(path, parent, obj, eAxis, canCollide, phi, w, F(0.0))
6669
obj.jointKind = RevoluteKind
6770
obj.jointIndex = 0
6871
obj.ndof = 1
6972
obj.canCollide = canCollide
7073
obj.r_rel = Modia3D.ZeroVector3D(F)
71-
obj.R_rel = Frames.rotAxis(posAxis, posMovement, phi)
74+
obj.R_rel = Modia3D.rot_e(eAxis, phi)
7275
parent.hasChildJoint = true
7376
return obj.joint
7477
end

src/Composition/joints/joints.jl

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -347,14 +347,14 @@ function computePositionsVelocitiesAccelerations!(mbs::MultibodyData{F,TimeType}
347347
revolute = mbs.revolute[obj.jointIndex]
348348

349349
obj.r_abs = parent.r_abs
350-
obj.R_rel = Frames.rotAxis(revolute.posAxis, revolute.posMovement, revolute.phi)
350+
obj.R_rel = Frames.rot_e(revolute.eAxis, revolute.phi)
351351
obj.R_abs = obj.R_rel*parent.R_abs
352352

353353
obj.v0 = parent.v0
354354
obj.a0 = parent.a0
355355

356-
w_rel = Frames.axisValue(revolute.posAxis, revolute.posMovement, revolute.w)
357-
z_rel = Frames.axisValue(revolute.posAxis, revolute.posMovement, revolute.a)
356+
w_rel = revolute.eAxis*revolute.w
357+
z_rel = revolute.eAxis*revolute.a
358358

359359
obj.w = obj.R_rel*(parent.w + w_rel)
360360
obj.z = obj.R_rel*(parent.z + z_rel + cross(parent.w, w_rel))
@@ -449,13 +449,12 @@ function computeAccelerations!(mbs::MultibodyData{F,TimeType}, tree::Vector{Obje
449449
revolute = mbs.revolute[obj.jointIndex]
450450

451451
obj.a0 = parent.a0
452-
z_rel = Frames.axisValue(revolute.posAxis, revolute.posMovement, revolute.a)
452+
z_rel = revolute.eAxis*revolute.a
453453
obj.z = obj.R_rel*(parent.z + z_rel)
454454

455455
elseif jointKind == PrismaticKind
456456
prismatic = mbs.prismatic[obj.jointIndex]
457457

458-
eAxis = prismatic.posMovement ? prismatic.posAxis : -prismatic.posAxis
459458
a_rel = prismatic.eAxis*prismatic.a
460459
obj.a0 = parent.a0 + parent.R_abs'*(a_rel + cross(parent.z, obj.r_rel))
461460
obj.z = parent.z
@@ -516,7 +515,7 @@ function computeObject3DForcesTorquesAndGenForces!(mbs::MultibodyData{F,TimeType
516515
parent.t += obj.R_rel'*obj.t
517516
obj.f = -obj.f
518517
obj.t = -obj.t
519-
revoluteGenForces[obj.jointIndex] = revolute.posMovement ? obj.t[revolute.posAxis] : -obj.t[revolute.posAxis]
518+
revoluteGenForces[obj.jointIndex] = dot(revolute.eAxis,obj.t)
520519

521520
elseif jointKind == PrismaticKind
522521
prismatic = mbs.prismatic[obj.jointIndex]
@@ -858,7 +857,7 @@ getGenForcesHiddenJoints(mbs, qdd_hidden) = mbs.hiddenGenForces
858857
function setAngle!(revolute::Revolute, phi::F) where F <: Modia3D.VarFloatType
859858
obj = revolute.obj2
860859
revolute.phi = phi
861-
obj.R_rel = Frames.rotAxis(revolute.posAxis, revolute.posMovement, phi)
860+
obj.R_rel = Frames.rot_e(revolute.eAxis, phi)
862861
obj.R_abs = obj.R_rel*obj.parent.R_abs
863862
return revolute
864863
end

src/Frames/rotationMatrix.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ with `angle` if `positive=true` and otherwise with `-angle`.
145145
"""
146146
R = Modia3D.rot_e(e, angle)
147147
148-
Return rotation matrix that rotates around angle `angle` along unit axis `e`.
149-
This function assumes that `norm(e) == 1`.
148+
Return rotation matrix that rotates frame1 around `angle` along unit axis `e`
149+
arriving at frame2. This function assumes that `norm(e) == 1`.
150150
"""
151-
@inline function rot_e(e::AbstractVector,angle::Number)
151+
@inline function rot_e(e::AbstractVector, angle::Number)
152152
(s,c) = sincos(angle)
153153
F = typeof(s)
154154
return e*e' + (NullRotation(F) - e*e')*c - skew(e)*s

src/Modia3D.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Modia3D
55

66
const path = dirname(dirname(@__FILE__)) # Absolute path of package directory
77
const Version = "0.11.0-dev"
8-
const Date = "2022-05-26"
8+
const Date = "2022-05-29"
99

1010
println("\nImporting Modia3D Version $Version ($Date)")
1111

test/Basic/ModelsForPrecompilation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Dummy = Model3D(
1313
rev = Revolute(obj1=:world, obj2=:frame1),
1414

1515
body2 = Object3D(feature=Solid(shape=Box(lengthX=1.0, lengthY=0.1, lengthZ=0.1), massProperties=MassProperties(mass=1.0))),
16-
prism = Prismatic(obj1=:world , obj2=:body2, axis=2),
16+
prism = Prismatic(obj1=:world , obj2=:body2, axis=[0.0, 20.0, 0.0]),
1717

1818
body3 = Object3D(parent=:world, fixedToParent=:false,
1919
translation=[0.0, 1.0, 0.0],

test/Basic/Pendulum.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Pendulum = Model3D(
66
world = Object3D(feature=Scene()),
77
body = Object3D(feature=Solid(massProperties=MassProperties(mass=1.0))),
88
bodyFrame = Object3D(parent=:body, translation=[-0.5, 0.0, 0.0]),
9-
rev = Revolute(obj1=:world, obj2=:bodyFrame)
9+
rev = Revolute(obj1=:world, obj2=:bodyFrame, axis=[0.0, 0.0, 1.0])
1010
)
1111

1212
pendulum = @instantiateModel(Pendulum, unitless=true, log=false, logDetails=false, logCode=true, logStateSelection=false, logCalculations=false)

0 commit comments

Comments
 (0)