-
Notifications
You must be signed in to change notification settings - Fork 164
/
SphericalSpherical.mo
281 lines (268 loc) · 11.8 KB
/
SphericalSpherical.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
within Modelica.Mechanics.MultiBody.Joints;
model SphericalSpherical
"Spherical - spherical joint aggregation (1 constraint, no potential states) with an optional point mass in the middle"
import Modelica.Mechanics.MultiBody.Types;
extends Interfaces.PartialTwoFrames;
parameter Boolean animation=true "= true, if animation shall be enabled";
parameter Boolean showMass=true
"= true, if mass shall be shown (provided animation = true and m > 0)";
parameter Boolean computeRodLength=false
"= true, if rodLength shall be computed during initialization (see info)";
parameter SI.Length rodLength(
min=Modelica.Constants.eps,
fixed=not computeRodLength, start = 1)
"Distance between the origins of frame_a and frame_b (if computeRodLength=true, guess value)";
parameter SI.Mass m(min=0)=0
"Mass of rod (= point mass located in middle of rod)";
parameter SI.Diameter sphereDiameter=world.defaultJointLength
"Diameter of spheres representing the spherical joints"
annotation (Dialog(tab="Animation", group="if animation = true", enable=animation));
input Types.Color sphereColor=Modelica.Mechanics.MultiBody.Types.Defaults.JointColor
"Color of spheres representing the spherical joints"
annotation (Dialog(colorSelector=true, tab="Animation", group="if animation = true", enable=animation));
parameter SI.Diameter rodDiameter=sphereDiameter/Types.Defaults.JointRodDiameterFraction
"Diameter of rod connecting the two spherical joint"
annotation (Dialog(tab="Animation", group="if animation = true", enable=animation));
input Types.Color rodColor=Modelica.Mechanics.MultiBody.Types.Defaults.RodColor
"Color of rod connecting the two spherical joints"
annotation (Dialog(colorSelector=true, tab="Animation", group="if animation = true", enable=animation));
parameter SI.Diameter massDiameter=sphereDiameter
"Diameter of sphere representing the mass point"
annotation (Dialog(tab=
"Animation", group="if animation = true and showMass = true and m > 0",
enable=animation and showMass and m > 0));
input Types.Color massColor=Modelica.Mechanics.MultiBody.Types.Defaults.BodyColor
"Color of sphere representing the mass point" annotation (
Dialog(colorSelector=true, tab="Animation", group=
"if animation = true and showMass = true and m > 0",
enable=animation and showMass and m > 0));
input Types.SpecularCoefficient specularCoefficient = world.defaultSpecularCoefficient
"Reflection of ambient light (= 0: light is completely absorbed)"
annotation (Dialog(tab="Animation", group="if animation = true", enable=animation));
parameter Boolean kinematicConstraint=true
"= false, if no constraint shall be defined, due to analytically solving a kinematic loop (\"false\" should not be used by user, but only by MultiBody.Joints.Assemblies joints)"
annotation (Dialog(tab="Advanced"));
Real constraintResidue = rRod_0*rRod_0 - rodLength*rodLength
"Constraint equation of joint in residue form: Either length constraint (= default) or equation to compute rod force (for analytic solution of loops in combination with Internal.RevoluteWithLengthConstraint/PrismaticWithLengthConstraint)"
annotation (Dialog(tab="Advanced", enable=not kinematicConstraint));
parameter Boolean checkTotalPower=false
"= true, if total power flowing into this component shall be determined (must be zero)"
annotation (Dialog(tab="Advanced"));
SI.Force f_rod
"Constraint force in direction of the rod (positive on frame_a, when directed from frame_a to frame_b)";
SI.Position rRod_0[3]
"Position vector from frame_a to frame_b resolved in world frame";
SI.Position rRod_a[3]
"Position vector from frame_a to frame_b resolved in frame_a";
Real eRod_a[3](each final unit="1")
"Unit vector in direction from frame_a to frame_b, resolved in frame_a";
SI.Position r_CM_0[3]
"Dummy if m==0, or position vector from world frame to mid-point of rod, resolved in world frame";
SI.Velocity v_CM_0[3] "First derivative of r_CM_0";
SI.Force f_CM_a[3]
"Dummy if m==0, or inertial force acting at mid-point of rod due to mass point acceleration, resolved in frame_a";
SI.Force f_CM_e[3]
"Dummy if m==0, or projection of f_CM_a onto eRod_a, resolved in frame_a";
SI.Force f_b_a1[3]
"Force acting at frame_b, but without force in rod, resolved in frame_a";
SI.Power totalPower
"Total power flowing into this element, if checkTotalPower=true (otherwise dummy)";
protected
Visualizers.Advanced.Shape shape_rod(
shapeType="cylinder",
color=rodColor,
specularCoefficient=specularCoefficient,
length=rodLength,
width=rodDiameter,
height=rodDiameter,
lengthDirection=eRod_a,
widthDirection={0,1,0},
r=frame_a.r_0,
R=frame_a.R) if world.enableAnimation and animation;
Visualizers.Advanced.Shape shape_a(
shapeType="sphere",
color=sphereColor,
specularCoefficient=specularCoefficient,
length=sphereDiameter,
width=sphereDiameter,
height=sphereDiameter,
lengthDirection=eRod_a,
widthDirection={0,1,0},
r_shape=-eRod_a*(sphereDiameter/2),
r=frame_a.r_0,
R=frame_a.R) if world.enableAnimation and animation;
Visualizers.Advanced.Shape shape_b(
shapeType="sphere",
color=sphereColor,
specularCoefficient=specularCoefficient,
length=sphereDiameter,
width=sphereDiameter,
height=sphereDiameter,
lengthDirection=eRod_a,
widthDirection={0,1,0},
r_shape=eRod_a*(rodLength - sphereDiameter/2),
r=frame_a.r_0,
R=frame_a.R) if world.enableAnimation and animation;
Visualizers.Advanced.Shape shape_mass(
shapeType="sphere",
color=massColor,
specularCoefficient=specularCoefficient,
length=massDiameter,
width=massDiameter,
height=massDiameter,
lengthDirection=eRod_a,
widthDirection={0,1,0},
r_shape=eRod_a*(rodLength/2 - sphereDiameter/2),
r=frame_a.r_0,
R=frame_a.R) if world.enableAnimation and animation and showMass and m > 0;
equation
// Determine relative position vector between the two frames
if kinematicConstraint then
rRod_0 = transpose(frame_b.R.T)*(frame_b.R.T*frame_b.r_0) - transpose(
frame_a.R.T)*(frame_a.R.T*frame_a.r_0);
else
rRod_0 = frame_b.r_0 - frame_a.r_0;
end if;
//rRod_0 = frame_b.r_0 - frame_a.r_0;
rRod_a = Frames.resolve2(frame_a.R, rRod_0);
eRod_a = rRod_a/rodLength;
// Constraint equation
constraintResidue = 0;
// Cut-torques at frame_a and frame_b
frame_a.t = zeros(3);
frame_b.t = zeros(3);
/* Force and torque balance of rod
- Kinematics for center of mass CM of mass point
r_CM_0 = frame_a.r_0 + rRod_0/2;
v_CM_0 = der(r_CM_0);
a_CM_a = resolve2(frame_a.R, der(v_CM_0) - world.gravityAcceleration(r_CM_0));
- Inertial and gravity force in direction (f_CM_e) and orthogonal (f_CM_n) to rod
f_CM_a = m*a_CM_a
f_CM_e = f_CM_a*eRod_a; // in direction of rod
f_CM_n = rodLength(f_CM_a - f_CM_e); // orthogonal to rod
- Force balance in direction of rod
f_CM_e = fa_rod_e + fb_rod_e;
- Force balance orthogonal to rod
f_CM_n = fa_rod_n + fb_rod_n;
- Torque balance with respect to frame_a
0 = (-f_CM_n)*rodLength/2 + fb_rod_n*rodLength
The result is:
fb_rod_n = f_CM_n/2;
fa_rod_n = fb_rod_n;
fb_rod_e = f_CM_e - fa_rod_e;
fa_rod_e is the unknown computed from loop
*/
// f_b_a1 is needed in aggregation joints to solve kinematic loops analytically
if m > 0 then
r_CM_0 = frame_a.r_0 + rRod_0/2;
v_CM_0 = der(r_CM_0);
f_CM_a = m*Frames.resolve2(frame_a.R, der(v_CM_0) -
world.gravityAcceleration(r_CM_0));
f_CM_e = (f_CM_a*eRod_a)*eRod_a;
frame_a.f = (f_CM_a - f_CM_e)/2 + f_rod*eRod_a;
f_b_a1 = (f_CM_a + f_CM_e)/2;
frame_b.f = Frames.resolveRelative(f_b_a1 - f_rod*eRod_a, frame_a.R,
frame_b.R);
else
r_CM_0 = zeros(3);
v_CM_0 = zeros(3);
f_CM_a = zeros(3);
f_CM_e = zeros(3);
f_b_a1 = zeros(3);
frame_a.f = f_rod*eRod_a;
frame_b.f = -Frames.resolveRelative(frame_a.f, frame_a.R, frame_b.R);
end if;
if checkTotalPower then
totalPower = frame_a.f*Frames.resolve2(frame_a.R, der(frame_a.r_0)) +
frame_b.f*Frames.resolve2(frame_b.R, der(frame_b.r_0)) + (-m)*(der(
v_CM_0) - world.gravityAcceleration(r_CM_0))*v_CM_0 + frame_a.t*
Frames.angularVelocity2(frame_a.R) + frame_b.t*Frames.angularVelocity2(
frame_b.R);
else
totalPower = 0;
end if;
annotation (
Icon(coordinateSystem(
preserveAspectRatio=true,
extent={{-100,-100},{100,100}}), graphics={
Ellipse(
extent={{-95,-40},{-15,40}},
fillPattern=FillPattern.Sphere,
fillColor={192,192,192}),
Ellipse(
extent={{-84,-30},{-24,30}},
fillColor={255,255,255},
fillPattern=FillPattern.Solid),
Ellipse(
extent={{15,-40},{95,40}},
fillPattern=FillPattern.Sphere,
fillColor={192,192,192}),
Ellipse(
extent={{25,-29},{85,30}},
lineColor={128,128,128},
fillColor={255,255,255},
fillPattern=FillPattern.Solid),
Text(
extent={{-150,90},{150,50}},
textString="%name",
textColor={0,0,255}),
Rectangle(
extent={{-40,40},{41,-41}},
lineColor={255,255,255},
fillColor={255,255,255},
fillPattern=FillPattern.Solid),
Rectangle(
extent={{-51,6},{48,-4}},
fillPattern=FillPattern.HorizontalCylinder,
fillColor={192,192,192}),
Ellipse(
extent={{-68,15},{-39,-13}},
fillPattern=FillPattern.Sphere,
fillColor={192,192,192}),
Ellipse(
extent={{39,14},{68,-14}},
fillPattern=FillPattern.Sphere,
fillColor={192,192,192}),
Text(
extent={{-150,-60},{150,-90}},
textString="%rodLength")}),
Documentation(info="<html>
<p>
Joint that has a spherical joint on each of its two ends.
The rod connecting the two spherical joints is approximated by a
point mass that is located in the middle of the rod. When the mass
is set to zero (default), special code for a massless body is generated.
In the following default animation figure, the two spherical joints are
represented by two red spheres, the connecting rod by a grey cylinder
and the point mass in the middle of the rod by a light blue sphere:
</p>
<p>
<img src=\"modelica://Modelica/Resources/Images/Mechanics/MultiBody/Joints/SphericalSpherical.png\" alt=\"model Joints.SphericalSpherical\">
</p>
<p>
This joint introduces <strong>one constraint</strong> defining that the distance between
the origin of frame_a and the origin of frame_b is constant (= rodLength).
It is highly recommended to use this joint in loops
whenever possible, because this enhances the efficiency
considerably due to smaller systems of non-linear algebraic
equations.
</p>
<p>
It is sometimes desirable to <strong>compute</strong> the <strong>rodLength</strong>
of the connecting rod during initialization. For this, parameter
<strong>computeLength</strong> has to be set to <strong>true</strong> and instead <strong>one</strong> other,
easier to determine, position variable in the same loop
needs to have a fixed attribute of <strong>true</strong>. For example,
if a loop consists of one Revolute joint, one Prismatic joint and
a SphericalSpherical joint, one may fix the start values of the revolute
joint angle and of the relative distance of the prismatic joint
in order to compute the rodLength of the rod.
</p>
<p>
It is not possible to connect other components, such as a body with mass
properties or a special visual shape object to the rod connecting
the two spherical joints. If this is needed, use instead joint Joints.<strong>UniversalSpherical</strong>
that has this property.
</p>
</html>"));
end SphericalSpherical;