Skip to content

Commit 49ad6c5

Browse files
Enable rotations in Bushing
- Enable rotational force laws. - Add test model BoxBushing. - Fix optional argument handling in function measFrameRotation.
1 parent 68b91eb commit 49ad6c5

File tree

5 files changed

+159
-20
lines changed

5 files changed

+159
-20
lines changed

src/Composition/ForceElements/Bushing.jl

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,44 @@
11

22
"""
33
force = Bushing(; obj1, obj2,
4-
nominalForce = Modia3D.ZeroVector3D,
5-
stiffness = Modia3D.ZeroVector3D,
6-
damping = Modia3D.ZeroVector3D )
4+
nominalForce = Modia3D.ZeroVector3D,
5+
stiffness = Modia3D.ZeroVector3D,
6+
damping = Modia3D.ZeroVector3D,
7+
nominalTorque = Modia3D.ZeroVector3D,
8+
rotStiffness = Modia3D.ZeroVector3D,
9+
rotDamping = Modia3D.ZeroVector3D,
10+
largeAngles = false )
711
812
Return a `force` acting as bushing between `obj1::`[`Object3D`](@ref) and
9-
`obj2::`[`Object3D`](@ref). Vectors `stiffness`, `damping` and
10-
`nominalForce` define the stiffness, damping and nominal force values in
11-
x, y and z direction of `obj1`. The orientation of `obj2` does not
12-
influence the resulting forces.
13+
`obj2::`[`Object3D`](@ref). The force directions are defined by `obj1`,
14+
i.e. the orientation of `obj2` does not influence the resulting forces.
15+
16+
# Arguments
17+
18+
- `nominalForce` defines the nominal force vector, i.e. the force that
19+
acts when spring and damper forces are zero. Positive values act in
20+
positive axis directions at `obj1` and in opposite directions at `obj2`.
21+
- `stiffness` defines linear stiffness coefficients in x-, y- and
22+
z-direction.
23+
- `damping` defines linear damping coefficients in x-, y- and
24+
z-direction.
25+
- `nominalTorque` defines nominal torques about alpha, beta and gamma
26+
directions.
27+
- `rotStiffness` defines linear stiffness coefficients about alpha-,
28+
beta- and gamma-direction.
29+
- `rotDamping` defines linear damping coefficients about alpha-,
30+
beta- and gamma-direction.
31+
- `largeAngles` can be used to enable large angle mode.
32+
- When disabled, small deformation angles (< 10°) are assumed. This
33+
option deals equally with rotations [alpha, beta gamma] about the
34+
axes [x, y, z] of `obj1`, but causes approximation errors for
35+
larger angles.
36+
- When enabled, the deformation angles and torque directions are
37+
calculated as [Cardan (Tait–Bryan) angles](https://en.wikipedia.org/wiki/Euler_angles#Chained_rotations_equivalence)
38+
(rotation sequence x-y-z from `obj1` to `obj2`). This option
39+
supports angles up to nearly 90°, but introduces local rotation
40+
directions [alpha, beta gamma] which differ from the axes [x, y, z]
41+
of `obj1` and increases computation effort.
1342
"""
1443
mutable struct Bushing <: Modia3D.AbstractForceElement
1544

@@ -19,19 +48,76 @@ mutable struct Bushing <: Modia3D.AbstractForceElement
1948
nominalForce::SVector{3,Float64}
2049
stiffness::SVector{3,Float64}
2150
damping::SVector{3,Float64}
51+
nominalTorque::SVector{3,Float64}
52+
rotStiffness::SVector{3,Float64}
53+
rotDamping::SVector{3,Float64}
54+
largeAngles::Bool
2255

2356
function Bushing(; obj1::Object3D,
2457
obj2::Object3D,
2558
nominalForce::AbstractVector = Modia3D.ZeroVector3D,
2659
stiffness::AbstractVector = Modia3D.ZeroVector3D,
27-
damping::AbstractVector = Modia3D.ZeroVector3D)
60+
damping::AbstractVector = Modia3D.ZeroVector3D,
61+
nominalTorque::AbstractVector = Modia3D.ZeroVector3D,
62+
rotStiffness::AbstractVector = Modia3D.ZeroVector3D,
63+
rotDamping::AbstractVector = Modia3D.ZeroVector3D,
64+
largeAngles::Bool = false )
65+
66+
nomForce = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N" , nominalForce)
67+
stiff = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N/m" , stiffness)
68+
damp = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N*s/m" , damping)
69+
nomTorque = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N*m" , nominalTorque)
70+
rotStiff = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N*m/rad" , rotStiffness)
71+
rotDamp = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N*m*s/rad", rotDamping)
2872

29-
nomForce = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N" , nominalForce)
30-
stiff = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N/m" , stiffness)
31-
damp = Modia3D.convertAndStripUnit(SVector{3,Float64}, u"N*s/m", damping)
73+
return new(obj1, obj2, nomForce, stiff, damp, nomTorque, rotStiff, rotDamp, largeAngles)
3274

33-
return new(obj1, obj2, nomForce, stiff, damp)
75+
end
76+
end
77+
78+
79+
# Compute deformation angles from rotation matrix
80+
function anglesFromRotation(largeAngles::Bool, R12::SMatrix{3,3,Float64}, w12::SVector{3,Float64})
81+
if largeAngles
82+
sbe = clamp(R12[3,1], -1.0, 1.0)
83+
cbe = sqrt(1.0 - sbe*sbe)
84+
if (cbe > 1e-12)
85+
sal = -R12[3,2]/cbe
86+
cal = R12[3,3]/cbe
87+
al = atan(-R12[3,2], R12[3,3])
88+
be = asin(sbe)
89+
ga = atan(-R12[2,1], R12[1,1])
90+
ald = w12[1] + (sal*w12[2] - cal*w12[3])*sbe/cbe
91+
bed = cal*w12[2] + sal*w12[3]
92+
gad = (-sal*w12[2] + cal*w12[3])/cbe
93+
return (@SVector[al, be, ga], @SVector[ald, bed, gad], @SMatrix[sal sbe; cal cbe])
94+
else
95+
@error("Gimbal lock of Bushing transformation.")
96+
return (@SVector[0.0, 0.0, 0.0], @SVector[0.0, 0.0, 0.0], @SMatrix[0.0 0.0; 0.0 0.0])
97+
end
98+
else
99+
al = R12[2,3]
100+
be = R12[3,1]
101+
ga = R12[1,2]
102+
ald = w12[1]
103+
bed = w12[2]
104+
gad = w12[3]
105+
if (max(abs(al), abs(be), abs(ga)) > 0.174)
106+
@warn("Bushing angle exceeds 10 deg.")
107+
end
108+
return (@SVector[al, be, ga], @SVector[ald, bed, gad], @SMatrix[0.0 0.0; 0.0 0.0])
109+
end
110+
end
34111

112+
# Compute torque vector from force law moments
113+
function torqueFromMoments(largeAngles::Bool, moments::SVector{3,Float64}, sico::SMatrix{2,2,Float64,4})
114+
if largeAngles
115+
tx = moments[1] + sico[1,2]*moments[3]
116+
ty = sico[2,1]*moments[2] - sico[1,1]*sico[2,2]*moments[3]
117+
tz = sico[1,1]*moments[2] + sico[2,1]*sico[2,2]*moments[3]
118+
return @SVector[tx, ty, tz]
119+
else
120+
return moments
35121
end
36122
end
37123

@@ -43,12 +129,18 @@ function initializeForceElement(force::Bushing)
43129
end
44130

45131
function evaluateForceElement(force::Bushing)
132+
R12 = measFrameRotation(force.obj2; frameOrig=force.obj1)
46133
r12 = measFramePosition(force.obj2; frameOrig=force.obj1, frameCoord=force.obj1)
134+
w12 = measFrameRotVelocity(force.obj2; frameOrig=force.obj1, frameCoord=force.obj1)
47135
v12 = measFrameTransVelocity(force.obj2; frameOrig=force.obj1, frameCoord=force.obj1, frameObsrv=force.obj1)
136+
(ang, angd, sico) = anglesFromRotation(force.largeAngles, R12, w12)
48137

49138
f12 = force.stiffness .* r12 + force.damping .* v12 + force.nominalForce
139+
mom = force.rotStiffness .* ang + force.rotDamping .* angd + force.nominalTorque
140+
t12 = torqueFromMoments(force.largeAngles, mom, sico)
50141

51142
applyFrameForcePair!(force.obj2, force.obj1, f12; frameCoord=force.obj1)
143+
applyFrameTorquePair!(force.obj2, force.obj1, t12; frameCoord=force.obj1)
52144
return nothing
53145
end
54146

src/Composition/ForceElements/SpringDamperPtP.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ Return a `force` acting as point-to-point parallel spring-damper between
1616
- `nominalForce` defines the nominal force, i.e. the force that acts when
1717
spring and damper forces are zero. Positive values represent tension.
1818
- `springForceLaw` defines the force law of the spring:
19-
A `Float64` value represents a linear stiffness coefficient.
20-
An univariate `Function` is used to compute the spring force dependent
21-
of its deflection. Positive values represent tension.
19+
- A `Float64` value represents a linear stiffness coefficient.
20+
- An univariate `Function` is used to compute the spring force
21+
dependent of its deflection. Positive values represent tension.
2222
- `damperForceLaw` defines the force law of the damper:
23-
A `Float64` value represents a linear damping coefficient.
24-
An univariate `Function` is used to compute the damper force dependent
25-
of its deflection velocity. Positive values represent expansion.
23+
- A `Float64` value represents a linear damping coefficient.
24+
- An univariate `Function` is used to compute the damper force
25+
dependent of its deflection velocity. Positive values represent
26+
expansion.
2627
"""
2728
mutable struct SpringDamperPtP <: Modia3D.AbstractForceElement
2829

src/Composition/frameMeasurements.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Return relative RotationMatrix `R` from frame `frameOrig` into frame `frameMeas`
55
66
If `frameOrig` is omitted `R` represents the absolute rotation of `frameMeas`.
77
"""
8-
function measFrameRotation(frameMeas::Object3D; frameOrig::Object3D)
8+
function measFrameRotation(frameMeas::Object3D; frameOrig::Union{Object3D, Nothing}=nothing)
99
R_MeasOrig = copy(frameMeas.R_abs) # R_MeasOrig := R_MeasWorld
1010
if !isnothing(frameOrig)
1111
R_MeasOrig = R_MeasOrig * frameOrig.R_abs' # R_MeasOrig := R_MeasOrig * R_OrigWorld^T

test/ForceElements/BoxBushing.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module BoxBushingSimulation
2+
3+
using ModiaLang
4+
5+
import Modia3D
6+
using Modia3D.ModiaInterface
7+
8+
const largeAngles = true
9+
if largeAngles
10+
startAngles = [0.8, 0.4, 0.2]
11+
else
12+
startAngles = [0.12, 0.06, 0.03]
13+
end
14+
15+
BoxBushing = Model(
16+
Length = 0.1,
17+
Mass = 1.0,
18+
IMoment = 0.1,
19+
visualMaterial = VisualMaterial(color="IndianRed1", transparency=0.5),
20+
world = Object3D(feature=Scene(gravityField=UniformGravityField(g=9.81, n=[0, 0, -1]), nominalLength=:Length)),
21+
worldFrame = Object3D(parent=:world,
22+
feature=Visual(shape=CoordinateSystem(length=:Length))),
23+
box = Object3D(feature=Solid(shape=Box(lengthX=:Length, lengthY=:Length, lengthZ=:Length),
24+
massProperties=MassProperties(; mass=:Mass, Ixx=:IMoment, Iyy=:IMoment, Izz=:IMoment),
25+
visualMaterial=:(visualMaterial))),
26+
joint = FreeMotion(obj1=:world, obj2=:box, r=Var(init=[0.2, 0.1, 0.05]), rot=Var(init=startAngles)),
27+
force = Bushing(obj1=:world, obj2=:box,
28+
stiffness=[50.0, 100.0, 200.0], damping=[1.0, 2.0, 4.0],
29+
rotStiffness=[5.0, 10.0, 20.0], rotDamping=[0.1, 0.2, 0.4], largeAngles=largeAngles)
30+
)
31+
32+
boxBushing = @instantiateModel(buildModia3D(BoxBushing), aliasReduction=false, unitless=true)
33+
34+
stopTime = 5.0
35+
dtmax = 0.1
36+
if largeAngles
37+
requiredFinalStates = [-0.013214812736859366, 0.0005523898753356359, -0.04904666002334838, 0.0757946142513073, 0.003341416782112683, -4.954082753506823e-5, -0.056708142772310295, 0.0009597147602342456, 4.340509280339362e-5, 0.3809257838547459, 0.001097814904879805, -0.00018842580793933223]
38+
else
39+
requiredFinalStates = [-0.01321449035293881, 0.0005522522622707078, -0.04904666423238891, 0.07579225811468479, 0.0033409499586830368, -4.943166272627969e-5, -0.008049782190986742, 0.00034916853581158153, 1.460039207707777e-6, 0.04510769348383226, 0.0018233644599616554, 9.80056841368889e-6]
40+
end
41+
simulate!(boxBushing, stopTime=stopTime, dtmax=dtmax, log=true, requiredFinalStates=requiredFinalStates)
42+
43+
@usingModiaPlot
44+
plot(boxBushing, ["joint.r", "joint.v", "joint.rot", "joint.w"], figure=1)
45+
46+
end

test/ForceElements/BoxNonLinearSpringDamperPtP.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using ModiaLang
55
import Modia3D
66
using Modia3D.ModiaInterface
77

8-
interpolatedForceLaws = false
8+
const interpolatedForceLaws = false
99
l0 = 0.1
1010
f0 = 5.0
1111
fc(x) = sign(x) * 100.0 * abs(x)^1.2

0 commit comments

Comments
 (0)