Manual EasyDyn
Manual EasyDyn
EasyDyn 1.2.4
C++ library for the easy simulation
of dynamic problems
Olivier Verlinden, Georges Kouroussis
Boulevard Dolez 31
7000 MONS
BELGIQUE
Tel. : 32.65.37.41.84
Fax. : 32.65.37.41.83
Email: Olivier.Verlinden@fpms.ac.be
Chapter 1
Getting and installing the library
1.1
The library can be downloaded in source form from the software part of the web site
of the Department of Theoretical Mechanics, Dynamics and Vibrations of the Faculte
Polytechnique de Mons, a university in Belgium
http://mecara.fpms.ac.be
either in tarball or zip format.
Once you have chosen a parent directory, decompact the archive with
unzip EasyDyn-x.y.z
or
tar xvzf EasyDyn-x.y.z.tgz
1.2
Getting a compiler
To compile EasyDyn, you will need a C++ compiler. I recommend gcc the compiler of
the free software foundation, available for free from
http://www.gnu.org/software/gcc
Pay attention that you need a version of gcc above 3.0, with the new implementation
of the I/O streams.
If you have not yet understood that you should drop Windows, it will probably be easier
to download mingw, a complete and easy to install implementation of gcc, available from
http://www.mingw.org
Several integrated development environments (IDE) exist for mingw. I personnaly use
dev-cpp available from
1
http://www.bloodshed.net/dev/devcpp.html
http://sourceforge.net/projects/dev-cpp
Have a look at the file installFR.win (unfortunately only in French so far) which describes the complete installation process under Windows.
However, as it is written in pure C++, it is very likely that EasyDyn can be compiled
with any modern C++ compiler.
1.3
To compile EasyDyn you will need to compile and install the GNU scientific Library
which can be obtained from http://www.gnu.org/software/gsl. From version 1.2.4 of
EasyDyn, LAPACK is no longer necessary but you will need at least version 1.10 of the GSL.
Compiling and installing the GSL is trivial under Unix in general. Under Windows, you
can do the job in the unix way under the MSYS environment available from the web site
of Mingw. Otherwise, I provide a Mingw precompiled version of the GSL on the web site
of EasyDyn. Other precompiled versions can generally be found quite rapidly by some
search on the net.
1.4
To compile the EasyDyn library, a makefile is provided. It should work for any version
of gcc. It has been tested under Linux and under Windows with mingw. Compiling the
library is just a matter of entering
make
from the root directory of EasyDyn.
Please note that under mingw, make has been renamed mingw32-make to avoid confusion
with any other implementation.
At the end you should have the library libEasyDyn.a. You may remove all the object
files (*.o).
To install the library for general use, you just have to copy the subdirectory include/EasyDyn
into the general include directory of gcc, like for example
c:
mingw
include for mingw;
/usr/local/include under Unix;
and libEasyDyn.a into the general lib directory of gcc, like for example
c:
mingw
lib for mingw;
/usr/local/lib under Unix;
Scripts are provided which perform this work automatically under Unix and Windows
(for mingw).
Under Unix, the installation to /usr/local is performed by running
binlinux/EDinstall
from the root directory of EasyDyn (root privileges needed !). This operation also copies
the script EDbuild (cf. later) to /usr/local/bin.
Under Windows, you can perform the installation by running
binwin\EDinstall C:\mingw
from the root directory of EasyDyn (administrator privileges needed !). Enventually,
adapt C:\mingw to the actual directory of mingw. This operation also copies the script
EDbuild.bat (cf. later) to the bin directory of mingw.
If you want to use another compiler, the library is just the collection of the objects
obtained by compilation of all the .cpp files in the root directory of EasyDyn.
1.5
If you want to compile the examples, a makefile is provided for each of them. Either
you have a file called makefile and you just type
make
or you have a file like oscil1.mak and you type
make -f oscil1.mak
If you want to compile a new application applic.cpp from a shell window, you will
have to issue
to compile the application
c++ -c applic.cpp
where <EasyDynHome> represents the root directory of EasyDyn; if successfull, the
operation will result in the creation of a file called applic.o
to link the application
Chapter 2
The EasyDyn vector library
2.1
Introduction
2.2
Vector calculus is a very powerful tool to develop the laws of mechanics. It is however
important to be very clear about notations and conventions.
zi
zj
yi
r P/i
xi
r P/j
xj
yj
eP
z0
x0
y0
2.2.1
(2.1)
The set of components of a vector a, with respect to a frame i will be denoted by {a}i ,
which represents a 3x1 matrix such as
axi
{a}i = ayi
azi
(2.2)
The position of any point P can be described by its position with respect to a chosen
frame. We will denote rP/i the coordinate vector of point P with respect to frame i, thats
to say the vector running from frame i to point P. The coordinates of point P in the
coordinate system of frame i correspond to the 3 components of vector rP/i , with respect
to frame i, gathered in the column matrix {rP/i }i .
Generally, a global reference exists and is referenced as frame 0. The coordinate vector
of a point P with respect to the global reference frame, thats to say rP/0 can be denoted
for the sake of simplicity by eP .
2.2.2
The homogenous transformation matrix is a formalism which allows to represent the situation of a frame with respect to another one. According to this formalism, the relative
situation of frame j with respect to frame i is expressed from the homogeneous transformation matrix Ti,j , of dimension 4x4, which can be partitioned in the following way:
Ri,j {rj/i}i
000
1
Ti,j =
(2.3)
where
rj/i is the coordinate vector of frame j with respect to frame i;
Ri,j is the rotation tensor describing the orientation of frame j with respect to
frame i.
Ti,j
zi
zj
yi
xi
xj
T0,i
z0
x0
yj
T0,j
0
y0
Let us recall that the columns of Ri,j correspond to the unit vectors xj , yj and zj ,
expressed in the axes of frame i
(2.4)
|xj | = 1
xj = yj zj
(2.5)
(2.6)
The rotation tensor can be used on its own to determine the coordinates of a vector in
a frame when they are given in an another one:
{a}i = Ri,j {a}j
(2.7)
The homogeneous transformation matrices are not only used to describe the relative
situation of two frames but also permit to easily express the coordinates of a point with
respect to a frame from the ones with respect to another frame. The following relationship
can indeed be easily demonstrated
!
(2.8)
(2.9)
{rP/i }i
1
= Ti,j
{rP/j }j
1
which is equivalent to
For the sake of simplicity, we will introduce the operator such that
{rP/i }i = Ti,j {rP/j }j = {rj/i }i + Ri,j {rP/j }j
(2.10)
To conclude this section, let us mention that the homogeneous transformation matrices
have the advantage to enjoy the following property
Ti,k = Ti,j Tj,k
(2.11)
This property is often used to obtain a complex transformation matrix from the successive
multiplication of simpler matrices.
2.3
2.3.1
The vec class consists of 3 public variables of type double: x, y and z, representing the
components of the vector in a orthonormal, dextrosum coordinate system.
2.3.2
Assignment methods
By default, all components are set to zero by the constructor. After declaration
vec v;
vector v is equal to zero (v.x=v.y=v.z=0).
It is however allowed to assign the vector a given value in the declaration
vec v(1,2,3);
There are different ways to change the components of a vector
by changing directly the coordinates (the variables are public)
v.x=1; v.y=2; v.z=3;
by using put()
v.put(1,2,3);
To set all components to zero, the zero() method can be used
v.zero();
A vector can also be normalized with the method unite()
v.unite();
As a result, vector v becomes a unitary vector with the same direction (v = v/|v|).
2.3.3
Utility methods
2.3.4
Calculus operators
The classical operators for addition, substraction, dot product and cross product of vectors
have been overloaded. Multiplication or division by a double have been overloaded as well.
The use of operators is natural and self-explanatory. Table 2.1 illustrates the operators
and their physical meaning. The left column gives the mathematical operation and the
right one the way it is written in C++.
v = v1 + v 2
v = v + v1
v = v1
v = v1 v2
v = v v1
d = v1 v2
v = v1 v2
v = v1 d
v = d v1
v =vd
v = v1 /d
v = v/d
v=v1+v2;
v+=v1;
v=-v1;
v=v1-v2;
v-=v1;
d=v1*v2;
v=v1^v2;
v=v1*d;
v=d*v1;
v*=d;
v=v1/d;
v/=d;
10
himself the necessary transformations. The operation is made easy with the other classes
of the library: the rotation tensor and the homogenous transformation matrix.
Important remark
As the operator * was already used for the dot product, the operator ^ has been
chosen, by reference to the classical scale () also used to represent the cross-product.
Unfortunately, it doesnt have the priority of a classical multiplication operator as it is
evaluated after + and -. Consequently the following statement
v1+v2^v3;
will be evaluated as (v1 + v2 ) v3 .
The statement to evaluate v1 + v2 v3 in the way it is generally understood is then
v1+(v2^v3);
To make it short, brackets are welcome to avoid misunderstanding between you and
the compiler in long statements with several operators.
2.3.5
Input/output
The input and output operators have been overloaded and can be used naturally with
vectors. Here is an example
vec v(1,2,3);
cout << Vector v: << v << \n;
vec v2;
cin >> v2;
On output, the three coordinates of the vector are sent to the stream, separated by some
space but with no end-of-line. On input, three real values are expected.
Here again, the priority of operator ^ has surprising consequences as it is evaluated
after << and >>. For example, the following code wont be understood by the compiler
cout << v1^v2 << \n;
and must be written
cout << (v1^v2) << \n;
2.4
2.4.1
The trot class consists of 9 public variables of type double: r11, r12, r13, r21, r22, r23,
r31, r32 and r33, representing the components of the rotation tensor in an orthonormal
and positively oriented coordinate system.
11
The terms of the rotational tensor must verify the following constraints, expressing that
coordinate systems after transformation must remain orthonormal and positively oriented
2
2
2
r11
+ r21
+ r31
2
2
2
r12
+ r22
+ r32
2
2
2
r13
+ r23
+ r33
r11 r12 + r21 r22 + r31 r32
r11 r13 + r21 r23 + r31 r33
r12 r13 + r22 r23 + r32 r33
=1
=1
=1
=0
=0
=0
(2.12)
(2.13)
(2.14)
(2.15)
(2.16)
(2.17)
2.4.2
Assignment methods
By default, the rotation tensor corresponds to an identity matrix: all diagonal terms are
equal to 1 while other ones are null. After declaration
trot R;
tensor R is such that R.r11=R.r22=R.r33=1 and all other terms are equal to zero.
The terms of a tensor are public and can be changed directly. For example, a rotation
about the Z axis could be written
R.r11=cos(theta); R.r12=-sin(theta); R.r13=0;
R.r21=sin(theta); R.r22=cos(theta); R.r23=0;
R.r31=0;
R.r32=0;
R.r33=1;
A rotation tensor can be defined back as identity with the unite() method
R.unite();
2.4.3
Utility methods
The methods rotx(th), roty(th) and rotz(th) allow to directly define a rotation tensor
as a rotation of angle th about the X, Y or Z axis.
trot R,R1,R2;
R.rotx(0.1); R1.roty(0.2); R2.rotz(0.3);
The equivalent functions Rrotx, Rroty and Rrotz exist and return a rotation tensor.
The advantage is that a rotation tensor can be defined direcly as a combination of several
rotations.
12
trot R;
R=Rrotx(0.1)*Rroty(0.2)*Rrotz(0.3);
The inverse rotation tensor is given by the method inv()
trot R,Rinv;
Rinv=R.inv();
2.4.4
Calculus operators
As already seen in previous examples, the rotation tensors can be multiplied by each
other. A rotation tensor can also multiply a vector, implementing a change of coordinate
system. The use of operators is natural and self-explanatory. Table 2.2 illustrates the
operators and their physical meaning. The left column gives the mathematical operation
and the right one the way it is written in C++.
R = R1 R2
R = R R1
v = R v1
R=R1*R2;
R*=R1;
v=R*v1;
2.4.5
Each column of the rotation tensor corresponds to a unit vector. They are given respectively by the methods ux(), uy() and uz(), returning a data type vec. If the tensor is
applied to a coordinate system, each vector gives the unit vectors of the new coordinate
system.
trot R=Rroty(0.4);
vec v=R.ux();
The user can also set directly each of the unit vectors. The corresponding methods are
setux(vec v), setuy(vec v) and setuz(vec v).
trot R;
vec v1(1,2,0),v2(-2,1,0);
v1.unite();
v2.unite();
R.setux(v1);
R.setuy(v2);
It is the responsability of the user to insure that the rotation matrix remains orthonormal.
13
2.4.6
Input/output
The input and output operators have been overloaded and can be used naturally with
rotation tensors. Here is an example
trot R=Rrotz(1);
cout << Tensor R: \n << R;
trot R2;
cin >> R2;
On output, the 9 coordinates of the rotation tensor are sent to the stream, line by line (3
terms by line, separated by some space). A newline is output at the end of each line. On
input, 9 real values are expected.
2.5
2.5.1
The tiner class consists of 6 public variables of type double: Ixx, Iyy, Izz, Ixy, Ixz and
Iyz representing the components of the inertia tensor of a body in an orthonormal and
positively oriented coordinate system. In a given coordinate system, the inertia tensor
can be seen as the following 3x3 matrix
Iyy Iyz
Ixy
Ixz Iyz Izz
(2.18)
The terms on the diagonal are called moments of inertia while the others are called
products of inertia.
The matrix representing the inertia tensor is constant when expressed in a coordinate
system linked to the body. Otherwise, it depends on the orientation of the body.
2.5.2
Assignment methods
14
2.5.3
Calculus operators
(2.19)
vx
Ixx Ixy Ixz
v2x
Iyy Iyz vy
v2y = Ixy
vz
Ixz Iyz Izz
v2z
(2.20)
v2=Phi*v;
2.5.4
Input/output
The input and output operators have been overloaded and can be used naturally with
inertia tensors. Here is an example
tiner Phi(1.1,1.2,1.3);
cout << Tensor Phi: << Phi << \n;
tiner Phi2;
cin >> Phi2;
15
On output, the 6 parameters of the rotation tensor are sent to the stream, in the order
Ixx, Iyy, Izz, Ixy, Ixz and Iyz, separated by some space. No newline is sent to the
stream. On input, 6 real values are expected, the order of the parameters being the same
as in output.
2.6
2.6.1
The mth class consists of 2 public variables: a rotation tensor R and a vector e.
2.6.2
Assignment methods
At declaration, the vector and the rotation tensor get the default initial value of their
class, thats to say the null vector and the identity tensor.
The terms of R and e are public and can be changed directly
mth T;
T.R.r11=cos(theta);
T.e.x=2;
An homogenous transformation matrix can be reinitialized with the unite() method
T.unite();
2.6.3
Utility methods
The methods rotx(th), roty(th) and rotz(th) of the rotation tensor allow to directly
define a transformation matrix corresponding to a rotation of angle th about the X, Y or
Z axis.
mth T,T1,T2;
T.R.rotx(0.1); T1.R.roty(0.2); T2.R.rotz(0.3);
The equivalent functions Trotx(th), Troty(th) and Trotz(th) exist and return an
homogeneous transformation matrix
mth T;
T=Trotx(0.1)*Troty(0.2)*Trotz(0.3);
A transformation matrix corresponding to a displacement can be defined from a vector
vec v(1,2,3);
mth T;
T.e=v;
16
2.6.4
Calculus operators
The operators are similar to the ones of the rotation tensor and are self-explanatory.
Table 2.4 illustrates the operators and their physical meaning. The left column gives the
mathematical operation and the right one the way it is written in C++.
T = T1 T2
T = T T1
v = T v1
T=T1*T2;
T*=T1;
v=T*v1;
2.6.5
The unit vectors can of course be retrieved directly from the ones of the rotation tensor
mth T=Troty(0.4);
vec v=T.R.ux();
2.6.6
Input/output
The input and output operators have been overloaded and can be used naturally with
transformation matrices. Here is an example
mth T=Trotz(1);
cout << Homogeneous transformation matrix T: \n << T;
mth T2;
cin >> T2;
On output, the 12 coordinates of the rotation tensor are sent to the stream, line by line
(4 terms by line, separated by some space). A newline is output at the end of each line.
On input, 12 real values are expected.
17
2.7
2.7.1
Decomposition routines
Rotation decomposition
In order to decompose a rotation as 3 successive rotations about given axes, the library
yields the following procedures
void getrotxyz(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
void getrotyzx(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
void getrotzxy(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
void getrotyxz(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
void getrotxzy(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
void getrotzyx(trot R1, trot R2, double & th1, double
vec &axe1, vec &axe2, vec
& th3,
& th3,
& th3,
& th3,
& th3,
& th3,
For example, getrotxyz determines the 3 successive rotations about local axes X, Y
and Z to go from orientation R1 to orientation R2. On return, variables th1, th2 and th3
are the rotation angles while axe1, axe2 and axe3 are unit vectors corresponding to the
successive axes of rotation. Said in other words, after a call to getrotxyz, R2 is such that
R2=R1*Rrotx(th1)*Rroty(th2)*Rrotz(th3);
Moreover, the rotation axes in this example are such that axe1=R1.ux and axe3=R2.uz.
The other procedures are equivalent but correspond to different orders in the axes of
rotation.
2.7.2
Vector decomposition
2.8
18
Example
Chapter 3
The EasyDyn visualization library
3.1
Introduction
The visu part of the library allows to easily create .vol and .van files used by EasyAnim
to visualize and animate a scene composed of moving objects. The visu module largely
uses the vector part of the library.
EasyDyn/visu relies on two principal classes
shape : a shape attached to a moving object;
scene : a set of shapes.
To use this module, you will of course have to include the corresponding header in your
source file with
#include <EasyDyn/visu.h>
3.2
3.2.1
The shapes
Structure
20
vec **nodecoord;
shape(){};
shape(int n_mth,mth **mt,int *n_pt_per_mth,vec **pt,int n_edges,
int *enode1, int *enode2, int *ecolor,
int n_surf,int *n_nodesurf,int **numnodesurf,
int *sclor,int protection=1);
shape(mth **mt_per_pt,int n_pt,vec **pt,int n_edges,
int *enode1,int *enode2, int *eclr,
int n_surf,int *n_nodesurf,int **num_nodesurf,
int *sclr,int protection=1);
shape(mth **mt_per_pt,int n_pt,vec **pt,int protection=1);
shape(mth **mt_per_pt, istream &stream);
shape(int n_mth,mth **mt,int *n_pt_per_mth,vec **pt,int protection=1);
shape(int n_mth, mth **mt, istream &stream);
~shape();
shape *nextshape;
int GetNbrNodes();
int GetNbrEdges();
int GetNbrSurf();
virtual void WriteNodes(ostream &stream, int initnode);
virtual void WriteNodes(mth *mthvisu,ostream &stream, int initnode,
int norot=0);
void WriteEdges(ostream &stream, int initnode, int initedge);
void WriteSurf(ostream &stream, int initnode, int initsurf);
virtual void WriteCoord(ostream &stream);
virtual void WriteCoord(mth *mthvisu, ostream &stream, int norot=0);
virtual char* GetShapeType() { return "SHAPE"; };
virtual void WriteShape(ostream &stream);
};
Basically, a shape consists of a set of nodes and a set of edges and surfaces defined
between nodes.
The nodes are actually grouped according to the coordinate system they are attached
to. The variable nbrmth gives the number of involved coordinate systems. The situtation
of the latter is described by homogeneous transformation matrices, pointed to by the
array of matrix pointers mthref. The evolution of these matrices is managed by the
user, allowing to move the shapes and to create the animation. Each coordinate system
i (starting from 0) owns a number of nodes equal to nbrnodes[i] the local coordinates
being stored in the vector nodecoord[i]. The total number of nodes can be accessed by
the method GetNbrNodes(). The arrays nbrnodes and nodecoord must be allocated by
the user.
The number of edges is given by variable nbredges which can be accessed through the
method GetNbrEdges(). Each edge connects two nodes, whose index is stored in integer
arrays edgenode1 and edgenode2, which must be allocated by the user. The color of each
edge is stored in integer array edgecolor which must be allocated by the user. So far,
21
only color numbers from 0 to 15 can be defined, corresponding for historical reasons to
the ones defined in TurboPascal. The color codes are recalled in table 3.1. Note that the
numbering of the nodes starts from the first node of the first coordinate system (number
0) up to the last node of the last coordinate system.
Code
0
1
2
3
4
5
6
7
Color
Black
Blue
Green
Cyan
Red
Magenta
Brown
Light gray
Code
8
9
10
11
12
13
14
15
Color
Dark gray
Light blue
Light green
Light cyan
Light red
Light magenta
Yellow
White
3.2.2
Predefined shapes
To make the use of the library easier, several predefined shapes are provided, corresponding to classes derived from base class shape. In a general way, the user will declare a
pointer to base class shape, and allocate it as a derived class. Constructors are provided
to correctly initialize the predefined shapes. There exist 7 predefined shapes: the line, the
triangle, the box, the spline (class grspline), the frustum, the tyre and a last one (habfile)
where the structure of the shape is defined in an external text file.
22
Remark: for each predefined shape, a general constructor is defined to read the information related to the shape from a stream (the most often, it will be a file). This
information is assumed to be the one saved previously by the method WriteShape().
The constructor has the same form for each predefined shape and gives for example, for
the line
line(mth *mt, istream &stream);
with stream the input stream from which the information is read. The pointer mt has the
same meaning as in the other constructor form (see later) and relates to the coordinate
system in which the coordinates of the nodes of the shape are expressed.
The line
The class line defines a straight line between two points. Besides the stream version, the
following constructor is provided
line(mth *mt, vec pt1, vec pt2, int eclr);
with
mt the pointer to the homogeneous transformation matrix describing the position
of the local coordinate system of the shape;
pt1 and pt2 the coordinate vectors of the end points of the line with respect to the
local coordinate system;
eclr the color code of the line.
Here is an example
shape *s1;
vec e1(0,0,0), e2(1,0,0);
mth T=Trotz(0.5);
s1=new line(&T,e1,e2,3);
The triangle
The class triangle defines a triangle by its three vertices. Besides the stream version,
the following constructor is provided
triangle(mth *mt, vec c1, vec c2, vec c3, int eclr, int sclr);
with
mt the pointer to the homogeneous transformation matrix describing the position
of the local coordinate system of the shape;
23
c1, c2 and c3 the coordinate vectors of the vertices with respect to the local coordinate system;
eclr the color code of the edges;
sclr the color code of the surface of the triangle.
Here is an example
shape *s1;
vec e1(0,0,0), e2(1,0,0), e3(0,1,0);
mth T=Troty(0.5);
s1=new triangle(&T,e1,e2,e3,3,4);
The class triangle2 is a variant where the nodes of the triangle can be attached to
different coordinate systems, as shown by the constructor
triangle(mth *mt1, vec c1, mth *mt2, vec c2, mth *mt3, vec c3,
int eclr, int sclr);
The box
e1
y
x
e2
24
c1 and c2 the coordinate vectors of the two extreme vertices with respect to the
local coordinate system;
eclr the color code of the edges;
sclr the color code of the surfaces of the box.
Here is an example
shape *s1;
vec e1(-1,-1,-1), e2(1,1,1);
mth T=Tdisp(0.5,0,0);
s1=new box(&T,e1,e2,3,4);
The spline (class grspline)
The class grspline defines a spline from some points and the tangent vector of the curve
at these points. Besides the stream version, the following constructor is provided
grspline(mth *mt, vec *p, vec *t, int nt, int ns, int eclr);
with
mt the pointer to the homogeneous transformation matrix describing the position
of the local coordinate system of the shape;
p the coordinate vectors of the successive points describing the spline, with respect
to the local coordinate system;
t the tangent vectors at the successive points with respect to the local coordinate
system; pay attention that the amplitude of the tangent vector is relevant and
modifies the layout of the curve;
nt the number of sectors of the spline (equal to the number of points minus 1);
ns the number of straight segments used to represent a sector of the spline;
eclr the color code of the edges.
Here is an example
shape *s1;
vec e[2], t[2];
// We should get approximately a quarter of a circle
e[0].put(1,0,0); e[1].put(0,1,0);
t[0].put(0,1.567,0); t[1].put(-1.567,0,0);
mth T=Tdisp(0.5,0,0);
s1=new grspline(&T,e,t,1,10,3);
25
R2
z
R1
2 e2
1
e1
y
x
26
27
Rint
2
z
Rext
1
e2
e1
y
x
28
29
spirals
30
31
3.2.3
The user is free to redefine other classes derived from the base class shape. A look at the
code of the provided shapes will be more explanatory. However, note that you will have
to define at least
a constructor and, ideally, a destructor;
the method WriteShapes() which can anyway be empty.
3.3
3.3.1
The scenes
Building the scene
A scene is built by successively adding shapes, with the method AddShape. Here is an
example
shape *s1,*s2;
vec e1(-1,0,0), e2(1,0,0);
mth T1=Tdisp(5,0,0);
s1=new frustum(&T1,e1,e2,1.5,1.2,24,1,2);
32
3.3.2
Once the scene has been built by adding shapes, it can be saved to a file with extension
.vol by means of the method CreateVolFile()
thescene.CreateVolFile("myscene.vol");
In this file, the structure of the scene is described in terms of nodes, edges and surfaces
(cf. later).
With only the vol file, the scene can be visualized with EasyAnim.
3.3.3
3.3.4
By default, the scene is observed from the global coordinate system. If a moving observer
is wanted, like for example the driver in a car, it can be specified with the method
SetVisuFrame
mth Tref=Tdisp(10,10,10);
thescene.SetVisuFrame(&Tref);
33
When the animation is created, the homogeneous transformation matrix related to the
observer evolves as defined by the user.
Sometimes, it is desirable to follow the scene from a moving frame but without rotating
with it. The method SetVisuFrame will then be called like this
thescene.SetVisuFrame(&Tref,1);
Only the displacement part of matrix Tref will then be taken into account. The direction
of view will remain parallel to the global axis.
3.4
3.4.1
3.4.2
The structure of the file is simple. If the scene comprises Nn nodes, the file is a succession of blocks of Nn lines giving the X,Y and Z coordinates of each node, each block
corresponding to a saved configuration. The structure of the block is
x1 y1 z1
coordinates of node 1
x2 y2 z2
coordinates of node 2
:
xNn yNn zNn coordinates of last node
34
During animation by EasyAnim, the configurations are read successively and displayed
on the screen.
Chapter 4
The EasyDyn simulation library
4.1
Introduction
The sim part of the library allows to easily simulate any system described by a set of
second order differential equations of the form
q
, t) = 0
f(q, q,
(4.1)
with
t the time;
the vectors gathering the parameters of the system and their first and
q, q and q
second time derivatives;
f the vector gathering the residuals of the differential equations governing the behaviour of the physical system.
Lets recall that the problem can be solved only if the number of parameters is the same
as the number of differential equations.
The library was basically created to simulate the motion of mechanical systems but
can be used for any physical system whose behaviour is described in terms of second-order
differential equations.
First-order differential equations of the form
t) = 0
f(y, y,
(4.2)
, q becoming meaningless.
can be solved as well by considering y = q and y = q
4.2
4.2.1
The structure of the library is illustrated in figure 4.1. It involves the following global
variables
35
36
int nbrdof the number of degrees of freedom of the system (or number of parameters or number of differential equations);
double *f the array gathering the residuals of the considered differential equations
double *q, *qd, *qdd the arrays gathering the parameters and their first and
second time derivatives;
int nbrinput the number of inputs to the system (necessary only if you want to
export the input-output linearized system);
double *u the array gathering the inputs of the system (inputs represent any entry
exerting an action on the system: force, voltage, pressure,... that could be used to
control it); the inputs are expected to play a role in the differential equations);
double t the time;
char *application the name of the application used namely to create the result
file;
int DEBUG an option which, if different from zero, activates the printing of various
information during the simulation (equal to zero by default).
To be sure to declare the global variables only once, the main program will have to define
EASYDYNSIMMAIN before including the header file.
4.2.2
The user manages the main routine and must provide the following functions
void ComputeResidual() which computes the residuals f in terms of q, qd, qdd
and time t;
WriteDataHeader(ostream &OutFile) which sends to the OutFile output stream
the names, separated by some space, of the data the user wants to save; this
procedure is called only once at the beginning of an integration; eventually, it is
sufficient to call the provided routine WriteStateVariablesHeader which sends
the names of the data saved by the routine SaveStateVariables; pay attention
that a final linebreak must be sent, which is not done by the latter routine;
SaveData(ostream &OutFile) which sends to the OutFile output stream the values, separated by some space, of the data the user wants to save; this procedure
is called automatically at regular intervals by some integration routines or can be
called by the main program; eventually, it is sufficient to call the provided routine
SaveStateVariables which sends the time followed, for each variable i, by the value
of q[i], qd[i] and qdd[i] (t q[0] qd[0] qdd[0] q[1] ...); pay attention that
a final linebreak must be sent, which is not done by the latter routine.
37
Global variables
int nbrdof
double t
double *q, *qd, *qdd, *f
char *application
int DEBUG
Library
User
InitEasyDynSim()
main()
EndEasyDynSim()
StaticEquilibrium()
ComputePoles()
WriteStateVariablesHeader()
SaveStateVariables()
WriteDataHeader()
SaveData()
NewmarkIntegration()
ComputeResidual()
NewmarkInterval()
NewmarkOneStep()
4.2.3
Before calling any other procedure, the user must set the variable nbrdof and call the
routine InitEasyDynsim(), which allocates memory to all necessary arrays and sets all
terms of q, qd and qdd to zero. If you intend to use inputs, the variable nbrinput must
also be given and the initialization routine allocates memory for the array u. If not
specified, nbrinput is equal to zero and inputs can be ignored.
When the initialization is done, the integration routines can be called. Similarly at the
end of the program, the routine EndEasyDynsim should be called to clean the memory.
4.2.4
Integration routines
If the user wants the finest control on the integration, he will use the basic integration
routine
int NewmarkOneStep(double h, double &errqd, int *doflocked=0);
38
which performs an integration step with a time step h according to the Newmark formulas.
If the value returned by the function is different from zero, either the process was unable to
converge (1), or there was a numerical trouble (2). On return, errqd gives an estimation of
the rate of error on positions for the time step. The array doflocked allows to eventually
lock selected degrees of freedom. If doflocked is equal to zero it is ignored. Otherwise,
if doflocked[i] is different from zero, the ith degree of freedom is not modified by the
integration procedure and the ith residual is ignored. The evolution of the corresponding
degree of freedom can be specified in ComputeResidual. If not, it will follow a uniformly
accelerated evolution according to the initial conditions. Due to the ability of C++ to
use default arguments, doflocked will be automatically set to zero if it is not given in
the call.
At a higher level, we find the procedure
void NewmarkInterval(double tfinal, double &h, double hmax,
double *doflocked=0)
which integrates the equations up to time tfinal, with an initial time step h and a
maximum allowed time step hmax. To integrate over the interval, NewmarkInterval
makes successive calls to NewmarkOneStep while automatically adapting the time step to
keep the error below a chosen tolerance (1E-6).
The most often, the user will directly call the most general routine
void NewmarkIntegration(double tfinal, double hsave, double hmax,
int *doflocked=0)
which performs the integration up to time tfinal by regular time intervals equal to
hsave and with the maximum allowed time step hmax. The routine NewmarkIntegration
automatically opens a file called application.res, writes the header with the function
WriteDataHeader and saves the data after each interval by a call to SaveData. Practically,
NewmarkInterval is called for each integration interval.
4.2.5
Static equilibrium
For some particular cases, it is necessary to reach the static equilibrium before launching
the integration. This can be done by calling the following routine
void StaticEquilibrium(int *doflocked)
which search for the parameters qi verifying
0 , t0 ) = 0
f(q, q 0 , q
(4.3)
0
at time t0 for a given set of initial velocities and accelerations. The values of q 0 and q
are the ones in memory when calling StaticEquilibrium.
The most often, the accelerations will be equal to zero, which leads to a stationnary
behaviour. If velocities are equal to zero as well, the yielded configuration is an actual
static equlibrium position.
39
The vector doflocked specifies whether some particular degrees of freedom should be
locked during the process. Pratically, q[i] will be locked if doflocked is different from
zero.
4.2.6
(4.4)
(4.5)
with M, CT and KT respectively the mass matrix, the tangent damping matrix and the
tangent stiffness matrix, defined by
fj
Mij =
j 0
q
fj
CTij =
q j 0
fj
KTij =
qj 0
(4.6)
The eigenvalues (or roots or poles) i and the corresponding eigenvectors i result
from the solution of the following eigenvalue problem
2i M + i CT + +KT i = 0
(4.7)
As damping and stiffness tangent matrices are not necessarily symmetric, the eigenvalues and eigen vectors can be complex. However, as all matrices are real, complex roots
are always complex conjugates.
The eigenvalue analysis allows to directly assess the stability of the system. For recall,
the system will be unstable as soon as the real part of any pole is positive. If the root has
an imaginary part, the associated motion is oscillatory, the imaginary part corresponding
to the damped pulsation.
The determination of the poles and eigenvectors about the current configuration can
be performed by means of the routine ComputePoles, declared in the following way
void ComputePoles(int *doflocked, double freqmin=0, double freqmax=1E30)
When calling ComputePoles, the equations of motion are first linearized. The eigen
values and eigen modes are then determined and saved. The complete list of poles is
saved in the file application.lst, with the following format (cf example oscil5.cpp)
40
ALPHA
-0.628319
OMEGA
6.25169
FREQ(Hz)
0.994987
DAMP_RATIO
0.1
The number of the pole does not necessarily begins from 1. This is due to the fact that in
the case of complex conjugates poles, only the last one is saved. ALPHA, OMEGA represent
the real and imaginary part of the pole and are related to the damped frequency (FREQ)
and damping ratio (DAMP RATIO) by
f=
+ 2
(4.8)
The eigen modes are saved in the file application.mod but only the ones whose frequency
is between freqmin and freqmax. With the default values of freqmin and freqmax, all
modes should be saved. If different values of freqmin and freqmax are specified, another
file called application.ls2 is created, with only the roots whose frequency falls in the
specified range.
The file application.mod consists of successive data sets corresponding to the saved
modes. Each dat set holds the following information
NumberOfPole RealPart ImagPart Freq DampingRatio
1 ,real 1 ,imag
2 ,real 2 ,imag
:
:
N ,real N ,imag
where N represents the number of degrees of freedom of the system.
Practically, the linearization is performed by finite differences, the eigenvalue problem
being solved by a call to the GSL routine for unsymmetric real eigenvalue problems
(available from version 1.10). The latter replaces dggev of LAPACK, which is then no
longer necessary to build EasyDyn applications.
4.2.7
(4.9)
can be saved, for example, to set-up a controller of the system with the help of an appropriate tool.
When the reference configuration is specified in terms of positions, velocities, accelerations and inputs, the only thing to do is to call the routine
void SaveLinearizedSystem(int *doflocked=0)
where doflocked, if specified, allows to freeze some parameters.
The routine linarizes the system by finite differences and saves the mass, damping, stiffness and influence matrices, in ascii form, respectively in the files application.mm, application.cc, application.kk and application.ff.
41
Generally, inputs are defined in terms of time or according to a control strategy inside
the routine ComputeResidual(). When calling SaveLinearizedSystem(), the inputs
should remain unspecified. Otherwise, the numerical derivation will have no effect. Look
at the example of the DC motor (files dcmotor.cpp and dcmotorPID.cpp) for illustration.
4.3
4.3.1
(4.10)
q + 02 q = 0
(4.11)
often rewritten as
k
with 02 = m
.
The solution of this differential equation is given by
q = A cos(0 t + )
(4.12)
(4.13)
We will see how we can retrieve this solution with the help of EasyDyn.
4.3.2
The code below (oscil1.cpp) shows the simplest code to implement our problem. It is
based on a call to NewmarkIntegration
// Simulation of a mass-spring system (eigenfrequency=1 Hz)
#define EASYDYNSIMMAIN // to declare the global variables
#include <EasyDyn/sim.h>
double w0=2*3.14159265;
//--------------------------------------------------------------------
42
void ComputeResidual()
{
f[0]=qdd[0]+w0*w0*q[0];
}
//-------------------------------------------------------------------void WriteDataHeader(ostream &OutFile)
{
WriteStateVariablesHeader(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------void SaveData(ostream &OutFile)
{
SaveStateVariables(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------int main()
{
// Initialisation and memory allocation
nbrdof=1; application="oscil1";
InitEasyDynsim();
// Initial configuration
q[0]=1;
// Lets go !
NewmarkIntegration(5,0.01,0.005);
EndEasyDynsim();
}
//-------------------------------------------------------------------After compiling and running the code, the file oscil1.res contains the evolution of
q, q and q which can be easily plotted by GNUPLOT or a MATAB-alike program. The
result is shown in figure 4.2
4.3.3
Alternative implementations
If you want to manage the step by step procedure of the integration, you can call the
lower level routines NewmarkInterval or NewmarkOneStep.
If you call NewmarkInterval, you will only have to determine the initial accelerations
43
Harmonic oscillator
1
Position (m)
0.5
-0.5
-1
0
Time (s)
Figure 4.2: Results for the harmonic oscillator
and to manage the creation of the result file bu the integration should proceed fluently.
The main routine of the program will look like this (example oscil2.cpp)
int main()
{
// Initialisation and memory allocation
nbrdof=1; application="oscil2";
InitFpmsSim();
// Initial configuration
q[0]=1;
// Opening of the result file
ofstream ResFile("oscil2.res");
// Determining initial accelerations
t=0;
double errqd;
NewmarkOneStep(0,errqd);
// Saving initial state
SaveData(ResFile);
// Lets go !
double interval=0.01, tfinal=5, hmax=interval/2, h=hmax;
while (t<tfinal)
{
NewmarkInterval(t+interval,h,hmax);
cout << "t=" << t << endl;
44
SaveData(ResFile);
}
ResFile.close();
EndFpmsSim();
}
Note: the initial acceleration is determined by an initial call to NewmarkOneStep with
a null time step.
If you call NewmarkOneStep, you will have moreover to look at the stability of the
numerical integration. The main routine of the program will look like this (example
oscil3.cpp)
int main()
{
// Initialisation and memory allocation
nbrdof=1; application="oscil3";
InitFpmsSim();
// Initial configuration
q[0]=1;
// Opening of the result file
ofstream ResFile("oscil3.res");
// Determining initial accelerations
t=0;
double errqd;
NewmarkOneStep(0,errqd);
// Saving initial state
SaveData(ResFile);
// Lets go !
double h=0.05, tfinal=5;
int code;
while (t<tfinal)
{
code=NewmarkOneStep(h,errqd);
cout << "t=" << t << endl;
if (code==1) { cout << "No convergence\n"; exit(1); }
if (code==2) { cout << "Numerical trouble\n"; exit(2); }
SaveData(ResFile);
}
ResFile.close();
EndFpmsSim();
A direct use of NewmarkOneStep can lead to inaccurate results if the time step is not
appropriate. Figure 4.3 shows the result obtained if there are only 20 time steps per
period.
45
simulated
exact
0.8
Position (m)
0.6
0.4
0.2
0
-0.2
-0.4
-0.6
-0.8
-1
0
Time (s)
Figure 4.3: Results with a bad time step
4.3.4
You can determine the equilibrium position with a call to StaticEquilibrium. Here
below the main routine doing the job for the harmonic oscillator. The obtained position
should be equal to zero (example oscil4.cpp).
int main()
{
// Initialisation and memory allocation
nbrdof=1; application="oscil3";
InitFpmsSim();
// Initial configuration
q[0]=1;
t=0;
StaticEquilibrium();
cout << "Position reached for q=" << q[0] << endl;
EndFpmsSim();
}
4.3.5
You can determine the eigenvalues with a call to ComputePoles. Here below the main
routine doing the job for the harmonic oscillator. The operation is performed about the
equilibrium position (example oscil5.cpp).
46
4.4
The system represented in figure 4.4, consists of an hydraulic jack pushing a mass m,
attached to the ground by a spring of stiffness k. The jack comprises two chambers
numbered 1 and 2. It is a good test for EasyDyn as the hydraulic equations are wellknown to be particularly stiff.
Q1, pE1
11
00
00
11
00
11
00
11
00
11
00
11
00
11
00
11
00
11
00
11
Q2, pE2
Se 1
p V
1 1
S1
Se 2
p V
2 2
S2
111
000
000
111
000
111
000
111
2(pEi pi )
sign(pEi pi )
(4.14)
with Cd the orifice discharge coefficient, the fluid density, pi the fluid pressure inside
the chamber, and pEi the circuit pressure at the entrance of the chamber.
On the other hand, the flow is driven by the variation of volume of the chamber and
by the variation of the fluid pressure
Vi
Qi = V i + pi
K
(4.15)
with Vi the volume of the chamber and K the compressibility coefficient of the fluid.
47
If we consider that the position x=0 corresponds to the rest length of the spring, the
dynamic equilibrium of mass m is given by
m
x + kx p1 S1 + p2 S2 = 0
(4.16)
If V0i is the volume of the chamber for the position x=0, the volume can be expressed
in terms of x
V1 = V01 + S1 x
V2 = V02 S2 x
(4.17)
2(pE1 p1 )
Cd Se2
2(pE2 p2 )
V01 +S1 x
p1
K
S2 x +
sign(pE1 p1 ) = 0
(4.18)
V02 S2 x
p2
K
sign(pE2 p2 ) = 0
(4.19)
Although two equations are of first-order form, they can be naturally treated by specifying the 3 state variables as
q1 = x q2 = p1
q3 = p2
(4.20)
Here is the code implementing the simulation of the hydraulic system (hydrjack.cpp
in the examples\testsim directory).
48
if (t>0.01) pE1=1E7;
f[0]=m*qdd[0]+k*q[0]-p1*S1+p2*S2;
f[1]=S1*qd[0]+(V01+S1*q[0])*p1d/Kf
-Se1*Cd*sqrt(fabs(pE1-p1)/rho)*(pE1-p1)/(1+fabs(pE1-p1));
f[2]=-S2*qd[0]+(V02-S2*q[0])*p2d/Kf
-Se2*Cd*sqrt(fabs(pE2-p2)/rho)*(pE2-p2)/(1+fabs(pE2-p2));
}
//-------------------------------------------------------------------void WriteDataHeader(ostream &OutFile)
{
WriteStateVariablesHeader(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------void SaveData(ostream &OutFile)
{
SaveStateVariables(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------int main()
{
// Initialization and memory allocation
nbrdof=3; application="hydrjack";
InitEasyDynsim();
// Initial configuration
qd[1]=pE2/1E5;
qd[2]=pE2/1E5;
// Lets go !
DEBUG=1;
NewmarkIntegration(1,0.001,0.0005);
EndEasyDynsim();
}
//--------------------------------------------------------------------
The system has been simulated with the data in table 4.1 with the following initial
conditions
49
p1 0 = p2 0 =10 bar
for the pressure pE1 increasing linearly from 10 to 100 bar, in the interval [0:0.01] seconds.
The initial accelerations (
x, p1 and p2 ) are determined from the equations of motion.
Table 4.1: Simulation data for the hydraulic jack
m=10 kg
= 860 kg/m3
S2 =0.0005 m2
Cd = 0.611
k = 105 N/m
pE2 = 10 bar
V01 = 104 m3
Se1 =105 m2
K = 2 108 Pa
S1 =0.001 m2
V01 = 3 104 m3
Se2 =105 m2
It is of interest to note that the pressure has been expressed in bar so as to avoid
mixing state variables of completely different orders of magnitude.
Some results are presented in figures 4.5 to 4.8. The mass position (figure 4.5) is driven
initially by the flow through the orifices and stops when the equilibrium is reached between
the spring reaction and the force exerted by the jack. At the end of the simulation, the
pressure in the chambers is the one of the corresponding circuit (figure 4.6). The stiff
nature of the equations is illustrated on the evolution of the mass velocity (figure 4.7)
where the oscillations due to the fluid compressibility can be clearly identified. Figure 4.8
shows how the integration procedure adapts the time step during the simulation. This
evolution must be correlated with figure 4.7. In the beginning of the simulation, a small
time step is necessary due to heavy oscillations of the system. Later, the behaviour is
quasi-stationary and a larger time step is allowed. It can be seen that a maximum time
step of 0.0005 seconds has been specified by the user.
0.2
0.4
0.6
Time (s)
0.8
50
Pressures (bar)
p1
p2
0.2
0.4
0.6
0.8
Time (s)
0.2
0.4
0.6
0.8
Time (s)
Chapter 5
The EasyDyn multibody library
5.1
Introduction
The mbs part of the library is a frontend to the sim library for the simulation of multibody systems. It provides the routine ComputeResidual which automatically builds the
equations of motion if the user can provide
the kinematics of the system, thats to say the expression of position, velocity and
acceleration of each body in terms of the chosen configuration parameters and their
first and second time derivatives;
the expression of external forces exerted on each body in terms of time, configuration
parameters and their time derivatives.
5.2
5.2.1
All the global variables of the sim library automatically exist in mbs. Morevover, we have
int nbrbody: the number of bodies involved in the considered system;
structbody *body: an array gathering the properties of all bodies in the system.
The structure structbody is declared in the following way
struct structbody
{
double mass;
tiner PhiG;
mth T0G,TrefG;
vec vG, aG, omega, omegad,
vGrel, aGrel, omegarel, omegadrel,
R, MG;
};
51
52
with
mass the mass of the body;
PhiG the inertia tensor of the body with respect to the center of gravity, expressed
in the coordinate system of the body;
T0G the homogeneous tranformation matrix giving the situation of the coordinate
system of the body with respect to the global frame; the coordinate system of the
body must be located at the center of gravity;
vG, aG the velocity and acceleration vectors of the center of gravity, the coordinates
being expressed in the global coordinate system;
omega, omegad the rotational velocity and acceleration vectors of the body, the
coordinates being expressed in the global coordinate system;
TrefG the homogeneous tranformation matrix giving the situation of the coordinate
system of the body with respect to the one of the body used as a reference to express
the relative motion;
vGrel, aGrel the relative velocity and acceleration vectors of the center of gravity
with respect to the coordinate system of the body used as a reference to express
the relative motion; the coordinates are expressed in the coordinate system of the
reference body;
omegarel, omegadrel the rotational velocity and acceleration vectors of the body
with respect to the coordinate system of the body used as a reference to express
the relative motion; the coordinates are expressed in the coordinate system of the
reference body;
R,MG the resultant force and the resultant moment with respect to the center of
gravity, of inertia and applied efforts exerted on the body (the joint efforts dont
need to be considered in the proposed approach); the coordinates are expressed in
the global coordinate system.
All data related to the relative motion (TrefG, vGrel, aGrel, omegarel and omegadrel)
are used only if the motion of the body is defined from the relative motion with respect
to another body.
5.2.2
Like in sim, the user manages the main routine and must provide the following functions
SetInertiaData; this procedure is called only once at the beginning and initializes
the inertia data of each body, thats to say mass and PhiG; the software verifies the
following physical conditions
the mass must be positive;
53
mbs
sim
InitEasyDynmbs()
InitEasyDynsim()
EndEasyDynmbs()
EndEasyDynsim()
SetInertiaData()
main()
StaticEquilibrium()
WriteStateVariablesHeader()
SaveStateVariables()
WriteDataHeader()
SaveData()
NewmarkIntegration()
ComputeResidual()
NewmarkInterval()
ComputeMotionl()
ComputeResidualmbs()
AddAppliedEfforts()
AddGravityForces()
AddSpringForce()
AddDamperForce()
NewmarkOneStep()
any diagonal term of the inertia tensor mustnt be larger than the sum of the
two other ones.
ComputeMotion() building for each body the position matrix T0G and the vectors
vG, aG, omega and omegad from the array of the configuration parameters q and
their time derivatives qd and qdd. All entities can be described directly or from
the relative motion with respect to another body. In this case, the user builds the
relative position matrix TrefG and the vectors defining the relative motion vGrel,
aGrel, omegarel and omegadrel from the array of the configuration parameters
q and their time derivatives qd and qdd. The global motion is finally built from
the motion of the reference body and the relative motion, by calling the procedure
ComposeMotion() (cf. later).
AddAppliedEfforts adding to the vectors R and MG the contribution of the efforts
applied on each body (the inertia reactions being calculated automatically by the
internal routine ComputeInertiaEfforts); the job is made easier by some routines
implementing classical element forces like gravity, springs or dampers (cf. further).
ComputeResidual() which the most often will simply call ComputeResidualmbs(),
54
which builds the equations of motion from the efforts and the kinematics. The
procedures have been splitted in case the user would like to write supplementary
differential equations relative to non kinematic parameters, like for example the
model of a hydraulic actuator or a continuous controller.
5.2.3
Before calling the simulation routines, the user must set the variables nbrdof and nbrbody
and call the routine InitEasyDynmbs(), which itself calls InitEasyDynsim, allocates
memory for the body array and calls SetInertiaData.
Similarly at the end of the program, the routine EndEasyDynmbs should be called to
clean the memory.
5.2.4
Simulation routines
As mbs is a frontend to sim which simply implements in a particular way the routine
ComputeResidual, the simulation routines are used in the same way as in sim.
The routine ComputeResidualmbs automatically computes the residuals of the equations of motion of a multibody system whose kinematics is described in ComputeMotion
and subjected to efforts described in AddAppliedEfforts.
5.2.5
Motion composition
5.2.6
Some routines are provided to help the user in writing the routine AddAppliedEfforts.
Gravity
The routine AddGravityForces adds the gravity contribution of all the bodies of the
system; it is declared as
void AddGravityForces(vec grav);
with grav the gravity vector, expressed in the global coordinate system.
55
The routine AddSpringForce implements the efforts exerted by a spring attached to two
bodies; it is declared as
void AddSpringForce(double K, double L0,
int ibodyA, vec rA, int ibodyB, vec rB);
with K and L0 the stiffness and the rest length of the spring, ibodyA and ibodyB the
numbers of the bodies connected by the spring (i and j on figure 5.2), and rA and rB the
vectors giving the coordinates of the attachment points in the coordinate system of
the corresponding body.
body i
yi
zi
xi
rA
A
body j
yj
k, L0
B
rB
zj
xj
56
Kn the stiffness coefficient relative to the normal elastic contact force (cf. later for
computation of contact force);
pk the exponent of the penetration in the evaluation of the normal elastic contact
force
Cdamp the damping coefficient relative to normal contact force;
pd the exponent of the penetration in the evaluation of the normal damping contact
force
ffrict the friction coefficient;
vglim the limit slip velocity;
ibodyplane the number of the body to which the plane is attached (body i in figure
5.3);
rplane the vector coordinate of a point of the plane (rP in figure 5.3) with respect
to the center of gravity of body iplane; this vector is expressed in the coordinate
system attached to body iplane;
nplane a vector normal to the plane, oriented outwards relatively to the material
of body iplane; it is expressed in the coordinate system of the body iplane; this
vector doesnt need to be unitary;
ibodypoint the number of the body to which the contact point is attached (body
j in figure 5.3);
rpoint the vector coordinate of the contact point (rA in figure 5.3) with respect
to the center of gravity of body ipoint; this vector is expressed in the coordinate
system attached to body ipoint.
Practically, if pen represents the penetration of the contact point in the plane, the
normal contact force is calculated as follows (in the direction of the normal vector)
Fn = Kn penpk + Cdamp penpd
d pen
dt
(5.1)
A null value of pd can be used for a damping coefficient independent of penetration but
is not recommended for stability reasons.
57
Side view
xi
rP
A
P
rA
body j
yj
zj
A
xj
pen
Ft = f Fn
(5.2)
Strictly speaking, adhesion never arises but the slip velocity is limited to vglim as far
as the tangential force is below the adhesion limit.
Tyre
The routine AddTyreEfforts allows the user to deal with vehicles. So far the ground is
limited to the XY plane. The use of tyres needs a basic understanding of the classical
tyre models. The one which is used in EasyDyn is the so-called model of the University of
Arizona, developed by Gim Gwanghun, completely described in his Ph.D. thesis (Vehicle
Dynamic Simulation with a Comprehensive Model for Pneumatic Tyres, Gim, Gwanghun,
University of Arizona, 1988). It is an analytical model.
The routine is declared by
void AddTyreEfforts(int ibody, vec axe, structtyre tyre)
with
ibody the number of the body representing the wheel (the user must give the wheel
all necessary degrees of freedom, namely the axial rotation);
axe a vector giving the direction of the rotation axis, expressed in the coordinate
system of the body; this vector doesnt need to be unitary;
58
tyre a variable of type structtyre gathering all physical data of the tyre.
The structure structtyre is declared in the following way, which is self explanatory
struct
{
double
double
double
double
double
double
double
double
double
double
double
double
};
structtyre
r1; // outer radius
r2; // equivalent torus radius
Kz; // vertical stiffness
Cz; // vertical damping
Fznom; // nominal vertical force on tyre
Clongnom; // nominal longitudinal stiffness
nlong; // Clong=Clongnom*(Fz/Fznom)^nlong
Clatnom; // nominal cornering stiffness
nlat; // Clat=Clatnom*(Fz/Fznom)^nlat
Ccambernom; // nominal camber stiffness
ncamber; // Ccamber=Ccambernom*(Fz/Fznom)^ncamber
fClbs, fClbd; // Coulomb friction coefficients (static and dynamic)
As an indication, the longitudinal and cornering stiffnesses are worth, for a passenger
car radial tyre, about 8 times the nominal vertical force. For the same type of tyre, the
camber stiffness is about 0.6 times the nominal vertical force.
R11111
00000
2
00000
11111
11111
00000
00000
11111
R1
11111
00000
00000
11111
00000
11111
00000
11111
00000
11111
Figure 5.4: Tyre geometry
In figure 5.4, the parameters r1 and r2 are illustrated. It can be seen that the tyre is
considered to be a torus. The research of the contact point is based on this assumption.
5.3
5.3.1
59
Examples
Mass-spring system
The mass-spring system presented in the previous chapter can also be simulated in a
multibody way.
The corresponding code is given below (file spdp.cpp in the examples\testmbs directory).
// Simulation of a mass-spring-damper system
#define EASYDYNMBSMAIN // to declare the global variables
#include <EasyDyn/mbs.h>
// Definition of global application variables
vec ut; // direction of motion
// The result should be the same whatever ut
//-------------------------------------------------------------------void WriteDataHeader(ostream &OutFile)
{
WriteStateVariablesHeader(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------void SaveData(ostream &OutFile)
{
SaveStateVariables(OutFile);
OutFile << endl;
}
//-------------------------------------------------------------------void SetInertiaData()
{
// Inertia for body 0 (=ground in this example)
body[0].mass=1;
body[0].PhiG.put(1,1,1); // no impact on the result anyway
60
61
It can be seen in the code that the motion can be defined along any unit vector ut, for
the sake of testing the library. Moreover, a damper can be easily added.
5.3.2
Double pendulum
Let us consider the double pendulum illustrated in figure 5.5. It is composed of 2 bodies,
each one with its own frame. Two revolute joints constraint the motion of the bodies, on
O between the ground and body 1 and on A between bodies 1 and 2. It is easy to figure
out that the system has 2 degrees of freedom so that the configuration of the system can
be univoquely defined from angles q1 and q2 indicated on the figure.
The homogeneous transformation matrices giving the situation of the two bodies can
be easily established as
T0,1 =
T0,2 =
c1 s1 0
s1
c1
0
0
0
0
1
0
c12 s12 0
s12
c12
0
0
0
0
1
0
l1
s1
2
l1
c1
2
0
1
l2
l1 s1 + s12
2
l2
l1 c1 c12
2
0
1
(5.3)
(5.4)
with l1 and l2 the lengths of the arms, c1 = cos(q1 ), s1 = sin(q1 ), c12 = cos(q1 + q2 ) and
s12 = sin(q1 + q2 ).
62
y0
11111
00000
00000
11111
00000
11111
x0
O
y1
x1
G1
q1
A
y2
q2
x2
G2
=
=
=
=
q1~z0
1 OG1
(q1 + q2 )~z0
1 OA + 2 AG2
(5.5)
(5.6)
(5.7)
(5.8)
q1~z0
1 OG1 + 1 ( 1 OG1 )
(
q1 + q2 )~z0
1 OA + 1 ( 1 OA)
+ 2 AG2 + 2 ( 2 AG2 )
(5.9)
(5.10)
(5.11)
=
=
=
=
(5.12)
63
64
65
}
//--------------------------------------------------------------------
5.3.3
In the case of the double pendulum presented in the previous section, the user can also
choose to define the motion of the second body with respect to the first one. The function
ComputeMotion will then be written in the following way.
void ComputeMotion()
{
// Kinematics for body 0
body[0].T0G=Trotz(q[0])*Tdisp(0,-0.5*l1,0);
body[0].omega.put(0,0,qd[0]);
body[0].omegad.put(0,0,qdd[0]);
vec OG1=body[0].T0G.e;
body[0].vG=(body[0].omega^OG1);
body[0].aG=(body[0].omegad^OG1)+(body[0].omega^(body[0].omega^OG1));
// Kinematics for body 1
body[1].TrefG=Tdisp(0,-0.5*l1,0)*Trotz(q[1])*Tdisp(0,-0.5*l2,0);
body[1].omegarel=vcoord(0,0,qd[1]);
body[1].omegadrel=vcoord(0,0,qdd[1]);
vec AG2=body[1].TrefG.R*vcoord(0,-0.5*l2,0);
body[1].vGrel=(body[1].omegarel^AG2);
body[1].aGrel=(body[1].omegadrel^AG2)
+(body[1].omegarel^(body[1].omegarel^AG2));
ComposeMotion(1,0);
}
It is important to notice that the relative motion is completely written in the axes of
the intermediary reference frame.
5.4
The motion of the multibody system resulting from a time integration can be animated
with EasyAnim. For that purpose you will have to
1. globally declare a variable of type scene and an output stream for saving the images;
2. create graphical shapes attached to the bodies (practically to the homogeneous
transformation matrix describing the motion of the body)
3. add the shapes to the scene;
4. save the structure of the scene (specified in a file with extension .vol);
66
67
5.5
If a linear analysis of the multibody system has been performed by a call to ComputePoles,
the files for animation can be created by a simple call to the routine
void CreateVmoFile(scene &sc)
The routine relies on the existence of the file application.mod as well as the one of a
scene variable (sc) linking graphical objects to the bodies.
The routine creates the files application.vol and application.vmo which allow the
visualization with EasyAnim (from version 1.2).
Chapter 6
The CAGeM module
6.1
Introduction
Even with the help of the vector library, the risk of mistake remains high when developing the expressions of velocities and accelerations. Moreover the kinematics remains
problematic for an unexperienced user.
A supplementary tool, called CAGeM (Computer Aided Generation of Motion), has been
developed to help the users of EasyDyn. This module is a simple MuPAD script which automatically builds a C++ application using the mbs part of EasyDyn. For the kinematic
part, CAGeM symbolically derives the expression of the homogeneous transformation matrices and builds the expressions of the translation and rotation velocities and accelerations.
CAGeM is based on the fact that the homogeneous transformation matrix
T0,i =
R0,i {ei }0
000
1
(6.1)
yields, after time derivation, the rotation and the translation velocities as
{vi }0 =
d
{e }0
dt i
(6.2)
0,i RT0,i
{
i }0 = R
(6.3)
where {
i }0 is the skew symmetric matrix representing the vector product, such as:
0 az ay
0 ax
{a} = az
ay ax
0
One further derivation naturally leads to the accelerations
68
(6.4)
(6.5)
69
{ai }0 =
d
{v }0
dt i
(6.6)
{ i }0 =
d
{ }0
dt i
(6.7)
These relations can be easily obtained by using symbolic software. This method gives
an alternative to vector calculus to build the kinematics.
6.2
Installation
The MuPAD software is the result of several years of research at the University of Paderborn
(Germany). Since 1997 the development of interfaces and special applications of MuPAD
is done in a tight cooperation with SciFace Software. It can be downloaded free of charge
from the net at http://www.mupad.de and the installation is very easy (on Windows and
on Linux).
MuPAD is a general purpose computer algebra system for symbolic and numerical computations. The librairy is open source so that users can examine the code, implement
their own routines and data types easily and can also dynamically link C/C++ compiled
modules for raw speed and flexibility. It also offers features comparable to its commercial
counterparts like Mathematica or MathCad.
For the installation of CAGeM on Windows, the user copies the file cagem.mu anywhere on the disc and give the corresponding directory on MuPAD session (menu view >>
options >> kernel in User-defined startup file prompt).
On Linux, the installation is slightly different. The user must create a directory .mupad in his home directory, e.g. mkdir ~/.mupad and a new file userinit.mu, e.g. touch
~/.mupad/userinit.mu. This file must be edited to specify the MuPAD commands to execute each time MuPAD starts. In this case, the only command is read("XXX/cagem.mu")
where XXX is the complete path of the file cagem.mu.
6.3
To use CAGeM, the user provides a MuPAD code with the following information
the number of bodies, the number of configuration parameters and, optionally, the
number of dependent parameters;
the inertia data of each body;
the expression of the homogeneous transformation matrices (T0G[i] or TrefG[i]
according as the motion is defined with respect to the ground or relatively to another
body) of each body, expressed in terms of the chosen configuration parameters;
70
When the relative motion is defined with the variable TrefG[i], the user must indicate
the number of the reference body with the parameter BodyRef[i]. In this way, the C++
code will contain all the information to build the absolute motion (relative kinematics
and procedure ComposeMotion()).
According to the definition of the class tiner of the EasyDyn vector library, the components of the inertia tensor are represented only by the 6 variables Ixx[i], Iyy[i],
Izz[i], Ixy[i], Ixz[i] and Iyz[i], i representing the number of body.
In the same way, the equivalent functions Trotx(theta), Troty(theta), Trotz(theta)
and Tdisp(x,y,z) exist on CAGeM and return an homogeneous transformation matrix. A
supplementary function Trotn(nx,ny,nz,theta) is included and defines a rotation about
an axis whose direction cosines are equal to nx , ny and nz
C + nx2 (1 C)
nx ny (1 C) nz S nx nz (1 C) ny S
nz S + nx ny (1 C)
C + ny2 (1 C)
ny nz (1 C) nx S
nx nz (1 C) ny S nx S + nx nz (1 C)
C + nz2 (1 C)
0
0
0
0
0
0
1
(6.8)
6.4
Calling CAGeM
To start, call CAGeM with the simple command cagem(). A new window appears to
request the file (path and name) to analyze. In function of the options chosen by the user
on the data file the program gives on screen the CPUtime needed of each operation.
6.5
6.5.1
Examples
Double pendulum
The following code illustrates the users file related to the example of the double pendulum
// Title.
title:="Simulation of a double pendulum":
// Definition of nbrdof : number of degrees of freedom,
//
nbrbody : number of bodies,
//
nbrdep : number of dependent parameter(s) (optional).
71
FORCES
ANIM
STATIC
PLOT
LATEX_FR
LATEX_EN
Functionality
Default
Simplification of the various kinematics terms. This option, if equal to zero, is used where the simplification
needs a long CPUtime.
There exist applied forces other then gravity, expected
to be defined by the user in the file *.AppEff.cpp, which
will be included in the main code
Some lines of code are added so as to create animation
files during integration (a basic shape is attached to each
body)
The equilibrium position is determined before the integration
A gnuplot script file is generated to plot the evolution of
the configuration parameters and their first and second
time derivatives
Creation of LATEX 2 report in French
Creation of LATEX 2 report in English
Table 6.1: Different flags in CAGeM
nbrdof:= 2:
nbrbody:= 2:
nbrdep:= 0:
// Gravity vector.
gravity[1]:=0:
gravity[2]:=-9.81:
gravity[3]:=0:
// Some constant for geometry.
l0:=1:
l1:=2:
// Inertia characteristics.
mass[0]:=1:
mass[1]:=2:
Ixx[0]:=1:
Ixx[1]:=1:
Iyy[0]:=1:
Iyy[1]:=1:
Izz[0]:=l0^2/12*mass[0]:
Izz[1]:=l1^2/12*mass[1]:
// The kinematic part only comes down to.
T0G[0] := Trotz(q[0]-PI/2) * Tdisp(l0/2,0,0):
T0G[1] := Trotz(q[0]-PI/2) * Tdisp(l0,0,0) * Trotz(q[1]) * Tdisp(l1/2,0,0):
// or TrefG[1] := Tdisp(l0/2,0,0) * Trotz(q[1]) * Tdisp(l1/2,0,0):
//
BodyRef[1] := 0:
0
0
0
0
72
// Initial conditions.
qi[0]:=3.14/2:
// Simulation condition.
FinalTime:=5:
StepSave:=0.01:
StepMax:=0.005:
// Optional flags.
SIMPLIFY:=1:
ANIM:=1:
PLOT:=1:
LATEX_EN:=1:
For the sake of illustration, the code generated to compute the accelerations of the two
bodies looks like this
body[0].aG.x
body[0].aG.y
body[0].aG.z
body[1].aG.x
=
=
=
=
+
body[1].aG.y =
+
+
body[1].aG.z =
6.5.2
(1.0/2.0)*qdd[0]*cos(q[0]) - (1.0/2.0)*(qd[0]*qd[0])*sin(q[0]) ;
(1.0/2.0)*qdd[0]*sin(q[0]) + (1.0/2.0)*(qd[0]*qd[0])*cos(q[0]) ;
0.0 ;
qdd[0]*cos(q[0]) - (qd[0]*qd[0])*sin(q[0]) + qdd[0]*cos(q[0] + q[1])
qdd[1]*cos(q[0] + q[1]) - (qd[0]*qd[0])*sin(q[0] + q[1])
(qd[1]*qd[1])*sin(q[0] + q[1]) - 2.0*qd[0]*qd[1]*sin(q[0] + q[1]) ;
qdd[0]*sin(q[0]) + (qd[0]*qd[0])*cos(q[0]) + qdd[0]*sin(q[0] + q[1])
qdd[1]*sin(q[0] + q[1]) + (qd[0]*qd[0])*cos(q[0] + q[1])
(qd[1]*qd[1])*cos(q[0] + q[1]) + 2.0*qd[0]*qd[1]*cos(q[0] + q[1]) ;
0.0 ;
Slider-crank mechanism
The symbolic capabilities of MuPAD allow the user to define intermediary parameters. For
example, with the slidercrank mechanism illustrated in figure 6.1, the parameters and
x can be expressed univoquely in terms of angle q0 by
l1 sin(q0 )
= arcsin
l2
(6.9)
and
x = l1 cos(q0 ) + l1 cos()
(6.10)
The relationships can be introduced symbolically in the MuPAD code describing the
system as
// Title.
title:="Simulation of a slider-crank mechanism":
// Definition of nbrdof : number of degrees of freedom,
//
nbrbody : number of bodies,
//
nbrdep : number of dependent parameter(s) (optional).
73
OA=lc
AB=lr
body 2
1111111111111111
0000000000000000
0000000000000000
1111111111111111
74
For this example, the following relation gives a view of the results complexity
{vG,S1 } =
q0 sin(2 q0 ) 4 q
2
sin(q0 )
cos(2 q0 )
+7/8
8
cos(2 q0 )
+7/8
8
q0 cos(q0 )
(6.11)
The response of the system subjected to gravity has been simulated from initial conditions q0 = 1 rad and q0 = 0 rad/s. The time evolution of the dodies displacements,
represented in figure 6.2, shows that the mechanism comes back to the same position
after one oscillation, as there is no energy dissipation.
3
crank [rad]
slider [rad]
piston [m]
displacements
1
0
-1
-2
-3
-4
-5
0
Time [s]
75