Manual (All)

From ODE Wiki

Jump to: navigation, search

Contents


Introduction

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

The Open Dynamics Engine (ODE) is a free, industrial quality library for simulating articulated rigid body dynamics. Proven applications include simulating ground vehicles, legged creatures, and moving objects in VR environments. It is fast, flexible and robust, and has built-in collision detection. ODE is being developed by Russell Smith with help from several contributors.

If rigid body simulation does not make much sense to you, check out What is a Physics SDK?.

Features

ODE is good for simulating articulated rigid body structures. An articulated structure is created when rigid bodies of various shapes are connected together with joints of various kinds. Examples are ground vehicles (where the wheels are connected to the chassis), legged creatures (where the legs are connected to the body), or stacks of objects.

ODE is designed to be used in interactive or real-time simulation. It is particularly good for simulating moving objects in changeable virtual reality environments. This is because it is fast, robust and stable, and the user has complete freedom to change the structure of the system even while the simulation is running.

ODE uses a highly stable integrator, so that the simulation errors should not grow out of control. The physical meaning of this is that the simulated system should not "explode" for no reason (believe me, this happens a lot with other simulators if you are not careful). ODE emphasizes speed and stability over physical accuracy.

ODE has hard contacts. This means that a special non-penetration constraint is used whenever two bodies collide. The alternative, used in many other simulators, is to use virtual springs to represent contacts. This is difficult to do right and extremely error-prone.

ODE has a built-in collision detection system. However you can ignore it and do your own collision detection if you want to. The current collision primitives are sphere, box, cylinder, capsule, plane, ray, and triangular mesh - more collision objects will come later. ODE's collision system provides fast identification of potentially intersecting objects, through the concept of "spaces". (See the Collision Matrix to find out which primives --- primitive collision are implemented)

Here are the features:

  • Rigid bodies with arbitrary mass distribution.
  • Joint types: ball-and-socket, hinge, slider (prismatic), hinge-2, fixed, angular motor, linear motor, universal.
  • Collision primitives: sphere, box, cylinder, capsule, plane, ray, and triangular mesh, convex.
  • Collision spaces: Quad tree, hash space, and simple.
  • Simulation method: The equations of motion are derived from a Lagrange multiplier velocity based model due to Trinkle/Stewart and Anitescu/Potra.
  • A first order integrator is being used. It's fast, but not accurate enough for quantitative engineering yet. Higher order integrators will come later.
  • Choice of time stepping methods: either the standard ``big matrix'' method or the newer iterative QuickStep method can be used.
  • Contact and friction model: This is based on the Dantzig LCP solver described by Baraff, although ODE implements a faster approximation to the Coloumb friction model.
  • Has a native C interface (even though ODE is mostly written in C++).
  • Has a C++ interface built on top of the C one.
  • Many unit tests, and more being written all the time.
  • Platform specific optimizations.

ODE's License

ODE is Copyright © 2001-2004 Russell L. Smith. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the terms of EITHER:

  • The GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The text of the GNU Lesser General Public License is included with this library in the file LICENSE.TXT.
  • The BSD-style license that is included with this library in the file LICENSE-BSD.TXT.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files LICENSE.TXT and LICENSE-BSD.TXT for more details.

The ODE Community

Do you have questions or comments about ODE? Think you can help? Please write to the ODE mailing list.

Install and Use

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

Getting Started

The latest "stable" release is available at the SourceForge.net file list. At the time of this writing, two packages are available: a build-it-yourself source code package and a prebuilt Windows binary package. For developers, a Subversion repository provides access to the development version of the library.

The prebuilt Windows binary package uses single-precision math and includes support for trimesh collision shapes. This package also includes the header files and linker import libraries -- everything you need to get started right away. If you choose to use these prebuilt binaries you can jump ahead to "Using ODE", below.

If you have any questions, there is a very helpful mailing list. If you plan on using ODE you should definitely sign up. The list is fairly low traffic, high signal-to-noise, and extremely useful. You might also want to search the archive of the old mailing list.

Getting the Source Code (Subversion)

If you downloaded the source code package from SourceForge, you can skip ahead to the next section.

The latest development version of the source code is stored in a Subversion repository on SourceForge. The usual disclaimers for in-development code apply.

If you are unfamiliar with Subversion you can get up to speed by reading the OpenDE Subversion page. Once you have your Subversion client installed and ready to go you can get the latest version of ODE from the https://svn.sourceforge.net/svnroot/opende/trunk url. In summary, it comes down to executing the svn command:

  $ svn co https://opende.svn.sourceforge.net/svnroot/opende/trunk opende

This command will create a directory called opende in your current working directory. To update it at a later time, simply execute this command in the opende/ directory:

  $ svn update

If you use TortoiseSVN, create an opende/ directory, go there, right-click and select 'SVN Checkout'. Then use this URL and click OK:

  https://opende.svn.sourceforge.net/svnroot/opende/trunk

Building ODE

Before you start, you should know that there are two parts to ODE. There is "ODE", which is the physics and collision detection library. Then there is "DrawStuff", a simple wrapper over Win32/X11 and OpenGL which is used for the demo applications. This often causes confusion: DrawStuff is a simple library written only to display the ODE demos and is not intended to be used in your own projects. DrawStuff requires OpenGL, ODE does not. You do not have to build DrawStuff or the demo applications in order to use ODE.

How To Build ODE With Visual Studio (2002 and up)

If you downloaded the source code from Subversion, copy the default configuration file `ode/build/config-default.h` to `ode/include/ode/config.h`. If you are using the stable source code package, this step has already been done for you.

The directory `ode/build` contains project files for all recent versions of Visual Studio. Locate the correct directory for your version of Visual Studio, open, and build. By default, the project files will use single-precision math. To switch to double-precision, edit `ode/include/ode/config.h` and replace `#define dSINGLE 1` with `#define dDOUBLE 1` and rebuild.

Note that Visual Studio 6 is no longer supported; please upgrade to Visual C++ Express Edition (it's free!).

How To Build ODE With Code::Blocks

Because Code::Blocks supports so many different platforms, we do not provide workspaces. Instead, use Premake to create a workspace tailored for your platform and project.

Download Premake and place it on your system path (or anywhere convenient). Then create a workspace like so:

  $ cd ode/build
  $ premake --with-demos --with-tests --target cb-gcc

To see a complete list of options:

  $ cd ode/build
  $ premake --help

How To Build ODE With MinGW and MSYS

MSYS is an environment that simulates most of the unix environment needed to build programs. The generic build instructions below should work for MinGW+MSYS. Just try to avoid building ODE in directories with spaces'.

If you are building from SVN, you'll need the msysDTK package, plus updated autoconf, automake and libtool packages.

How to Build ODE With MinGW alone

If you don't have (or don't want to use) MSYS you can use Premake. Just open a command prompt on the build directory, and run:

 premake --with-demos --with-tests --target gnu
 cd gnu
 make

The library and executables will be generated in the "lib" directory. For more build options, run:

 premake --help

How To Build ODE Just About Everywhere Else

If you downloaded the source code from Subversion, bootstrap autotools by running the command:

 $ sh autogen.sh

Note that you need to have recent autoconf (2.61), automake (1.10) and libtool. You may see some "underquoted definition" warnings depending on your platform, these are (for now) harmless warnings regarding scripts from other m4 installed packages.

Configure the build by running the command:

 $ ./configure

By default this will build ODE with as a static library with single precision math, with trimesh support, and debug symbols enabled. You can modify this default configuration by supplying options to configure. Type the command

 $ ./configure --help

...for a full list of available options. Here are a few of the more important ones:

  • --enable-double-precision enables double precision math
  • --with-trimesh=opcode use OPCODE trimesh support
  • --with-trimesh=gimpact use GIMPACT trimesh support
  • --with-trimesh=none disable trimesh support

Once configure has run successfully, you can build ODE with:

 $ make
 $ sudo make install

The latter command will also install an ode-config script and an ode.pc pkg-config script, which you can use to pass cflags and ldflags to your projects. They also supply the correct precision ode was compiled (with either -DdSINGLE or -DdDOUBLE).

Building and Running ODE Tests on Mac OSX

The latest version now contains native support for MacOS X, so the following is a bit outdated. So don't be scared by references to X11, chances are you don't need to worry about it.

ODE uses XWindows and OpenGL to render the scene being simulated. In order to build the example you will need to install Apple X11 server and the X11SDK (as well as the normal developer tools).

These are available from Apple. As of writing this can be found at: http://www.apple.com/macosx/x11. NOTE: there is a tiny link at the bottom right of the page for the SDK

Once the software is installed follow the normal build instructions.

Since ODE uses X11 you need to run the X11 server (which you should have installed, it's in the Applications Folder).

If you run the test app in the XTerm that the X11 server opens by default then they should run fine. If however you run them from a MacOS X Terminal then you need to define the environment variable DISPLAY. If DISPLAY is not defined then you will get a message saying: "cannot open X11 display".

For example to run the boxstack test you would type cd ode/test DISPLAY=:0.0 ./test_boxstack.exe You can define this environment variable in your shell startup scripts (for example in ~/.bashrc if you are using bash)

Using ODE

The best way to understand how to use ODE is to look at the test/example programs that come with it. Note the following things:

Source files that use ODE only need to include a single header file: #include <ode/ode.h> The ode directory in this statement is actually the include/ode directory of the ODE distribution.


All the necessary flags for compilation and linking can be retrieved from pkg-config. For example:

 gcc -c myprogram.c `pkg-config ode --cflags`                  # compile

 gcc myprogram.o -o myprogram `pkg-config ode --libs`          # link

 gcc myprogram.c -o myprogram `pkg-config ode --cflags --libs` # compile and link

Alternatively, the old ode-config is still available:

 gcc -c myprogram.c `ode-config --cflags`                      # compile

But it needs a Bourne Shell to run, which might be a problem on Windows platforms; downloading the single Win32 pkg-config executable is much simpler. Pkg-config also offer some extra features, like version testing and easier Autoconf integration. So its usage is preferred over the ode-config script.

When ODE is used with the dWorldStep function, heavy use is made of the stack for storing temporary values. For very large systems several megabytes of stack can be used. If you experience unexplained out-of-memory errors or data corruption, especially on Windows, try increasing the stack size, or switching to dWorldQuickStep.

Concepts

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

Background

Here is where I will write some background information about rigid body dynamics and simulation. But in the meantime, please refer to Baraff's excellent SIGGRAPH tutorial.

Rigid bodies

A rigid body has various properties from the point of view of the simulation. Some properties change over time:

  • Position vector (x,y,z) of the body's point of reference. Currently the point of reference must correspond to the body's center of mass.
  • Linear velocity of the point of reference, a vector (vx,vy,vz).
  • Orientation of a body, represented by a quaternion (qs,qx,qy,qz) or a 3x3 rotation matrix.
  • Angular velocity vector (wx,wy,wz) which describes how the orientation changes over time.

Other body properties are usually constant over time:

  • Mass of the body.
  • Position of the center of mass with respect to the point of reference. In the current implementation the center of mass and the point of reference must coincide.
  • Inertia matrix. This is a 3x3 matrix that describes how the body's mass is distributed around the center of mass. Conceptually each body has an x-y-z coordinate frame embedded in it, that moves and rotates with the body, as shown in figure 1.
Image:body.jpg

Figure 1: The body coordinate frame.

The origin of this coordinate frame is the body's point of reference. Some values in ODE (vectors, matrices etc) are relative to the body coordinate frame, and others are relative to the global coordinate frame.

Note that the shape of a rigid body is not a dynamical property (except insofar as it influences the various mass properties). It is only collision detection that cares about the detailed shape of the body.

Islands and Disabled Bodies

Bodies are connected to each other with joints. An "island" of bodies is a group that can not be pulled apart - in other words each body is connected somehow to every other body in the island.

Each island in the world is treated separately when the simulation step is taken. This is useful to know: if there are N similar islands in the simulation then the step computation time will be O(N).

Each body can be enabled or disabled. Disabled bodies are effectively "turned off" and are not updated during a simulation step. Disabling bodies is an effective way to save computation time when it is known that the bodies are motionless or otherwise irrelevant to the simulation.

If there are any enabled bodies in an island then every body in the island will be enabled at the next simulation step. Thus to effectively disable an island of bodies, every body in the island must be disabled. If a disabled island is touched by another enabled body then the entire island will be enabled, as a contact joint will join the enabled body to the island.

Integration

The process of simulating the rigid body system through time is called integration. Each integration step advances the current time by a given step size, adjusting the state of all the rigid bodies for the new time value. There are two main issues to consider when working with any integrator:

  • How accurate is it? That is, how closely does the behavior of the simulated system match what would happen in real life?
  • How stable is it? That is, will calculation errors ever cause completely non-physical behavior of the simulated system? (e.g. causing the system to "explode" for no reason).

ODE's current integrator is very stable, but not particularly accurate unless the step size is small. For most uses of ODE this is not a problem -- ODE's behavior still looks perfectly physical in almost all cases. However ODE should not be used for quantitative engineering until this accuracy issue has been addressed in a future release.

Force accumulators

Between each integrator step the user can call functions to apply forces to the rigid body. These forces are added to "force accumulators" in the rigid body object. When the next integrator step happens, the sum of all the applied forces will be used to push the body around. The forces accumulators are set to zero after each integrator step.

Joints and constraints

In real life a joint is something like a hinge, that is used to connect two objects. In ODE a joint is very similar: It is a relationship that is enforced between two bodies so that they can only have certain positions and orientations relative to each other. This relationship is called a constraint -- the words joint and constraint are often used interchangeably. Figure 2 shows three different constraint types.

Image:joints.jpg

Figure 2: Three different constraint types.

The first is a ball and socket joint that constraints the ``ball of one body to be in the same location as the "socket" of another body. The second is a hinge joint that constraints the two parts of the hinge to be in the same location and to line up along the hinge axle. The third is a slider joint that constraints the "piston" and "socket" to line up, and additionally constraints the two bodies to have the same orientation.

Each time the integrator takes a step all the joints are allowed to apply constraint forces to the bodies they affect. These forces are calculated such that the bodies move in such a way to preserve all the joint relationships.

Each joint has a number of parameters controlling its geometry. An example is the position of the ball-and-socket point for a ball-and-socket joint. The functions to set joint parameters all take global coordinates, not body-relative coordinates. A consequence of this is that the rigid bodies that a joint connects must be positioned correctly before the joint is attached.

Joint groups

A joint group is a special container that holds joints in a world. Joints can be added to a group, and then when those joints are no longer needed the entire group of joints can be very quickly destroyed with one function call. However, individual joints in a group can not be destroyed before the entire group is emptied.

This is most useful with contact joints, which are added and remove from the world in groups every time step.

Joint error and the error reduction parameter (ERP)

When a joint attaches two bodies, those bodies are required to have certain positions and orientations relative to each other. However, it is possible for the bodies to be in positions where the joint constraints are not met. This "joint error" can happen in two ways:

  • If the user sets the position/orientation of one body without correctly setting the position/orientation of the other body.
  • During the simulation, errors can creep in that result in the bodies drifting away from their required positions.

Figure 3 shows an example of error in a ball and socket joint (where the ball and socket do not line up).

Image:ball-and-socket-bad.jpg

Figure 3: An example of error in a ball and socket joint.

There is a mechanism to reduce joint error: during each simulation step each joint applies a special force to bring its bodies back into correct alignment. This force is controlled by the error reduction parameter (ERP), which has a value between 0 and 1.

The ERP specifies what proportion of the joint error will be fixed during the next simulation step. If ERP=0 then no correcting force is applied and the bodies will eventually drift apart as the simulation proceeds. If ERP=1 then the simulation will attempt to fix all joint error during the next time step. However, setting ERP=1 is not recommended, as the joint error will not be completely fixed due to various internal approximations. A value of ERP=0.1 to 0.8 is recommended (0.2 is the default).

A global ERP value can be set that affects most joints in the simulation. However some joints have local ERP values that control various aspects of the joint.

Soft constraint and constraint force mixing (CFM)

Most constraints are by nature "hard". This means that the constraints represent conditions that are never violated. For example, the ball must always be in the socket, and the two parts of the hinge must always be lined up. In practice constraints can be violated by unintentional introduction of errors into the system, but the error reduction parameter can be set to correct these errors.

Not all constraints are hard. Some "soft" constraints are designed to be violated. For example, the contact constraint that prevents colliding objects from penetrating is hard by default, so it acts as though the colliding surfaces are made of steel. But it can be made into a soft constraint to simulate softer materials, thereby allowing some natural penetration of the two objects when they are forced together.

There are two parameters that control the distinction between hard and soft constraints. The first is the error reduction parameter (ERP) that has already been introduced. The second is the constraint force mixing (CFM) value, that is described below.

Constraint Force Mixing (CFM)

What follows is a somewhat technical description of the meaning of CFM. If you just want to know how it is used in practice then skip to the next section.

Traditionally the constraint equation for every joint has the form

J * v = c

where v is a velocity vector for the bodies involved, J is a "Jacobian" matrix with one row for every degree of freedom the joint removes from the system, and c is a right hand side vector. At the next time step, a vector lambda is calculated (of the same size as c) such that the forces applied to the bodies to preserve the joint constraint are

force = JT * lambda

ODE adds a new twist. ODE's constraint equation has the form

J * v = c + CFM * lambda

where CFM is a square diagonal matrix. CFM mixes the resulting constraint force in with the constraint that produces it. A nonzero (positive) value of CFM allows the original constraint equation to be violated by an amount proportional to CFM times the restoring force lambda that is needed to enforce the constraint. Solving for lambda gives

(J M-1 JT + CFM/h) lambda = c/h

Thus CFM simply adds to the diagonal of the original system matrix. Using a positive value of CFM has the additional benefit of taking the system away from any singularity and thus improving the factorizer accuracy.

How To Use ERP and CFM

ERP and CFM can be independently set in many joints. They can be set in contact joints, in joint limits and various other places, to control the spongyness and springyness of the joint (or joint limit).

If CFM is set to zero, the constraint will be hard. If CFM is set to a positive value, it will be possible to violate the constraint by "pushing on it" (for example, for contact constraints by forcing the two contacting objects together). In other words the constraint will be soft, and the softness will increase as CFM increases. What is actually happening here is that the constraint is allowed to be violated by an amount proportional to CFM times the restoring force that is needed to enforce the constraint. Note that setting CFM to a negative value can have undesirable bad effects, such as instability. Don't do it.

By adjusting the values of ERP and CFM, you can achieve various effects. For example you can simulate springy constraints, where the two bodies oscillate as though connected by springs. Or you can simulate more spongy constraints, without the oscillation. In fact, ERP and CFM can be selected to have the same effect as any desired spring and damper constants. If you have a spring constant kp and damping constant kd, then the corresponding ODE constants are:

ERP = h kp / (h kp + kd)
CFM = 1 / (h kp + kd)

where h is the stepsize. These values will give the same effect as a spring-and-damper system simulated with implicit first order integration.

Increasing CFM, especially the global CFM, can reduce the numerical errors in the simulation. If the system is near-singular, then this can markedly increase stability. In fact, if the system is mis-behaving, one of the first things to try is to increase the global CFM.

Collision handling

There is a lot that needs to be written about collision handling.

Collisions between bodies or between bodies and the static environment are handled as follows:

  • Before each simulation step, the user calls collision detection functions to determine what is touching what. These functions return a list of contact points. Each contact point specifies a position in space, a surface normal vector, and a penetration depth.
  • A special contact joint is created for each contact point. The contact joint is given extra information about the contact, for example the friction present at the contact surface, how bouncy or soft it is, and various other properties.
  • The contact joints are put in a joint "group", which allows them to be added to and removed from the system very quickly. The simulation speed goes down as the number of contacts goes up, so various strategies can be used to limit the number of contact points.
  • A simulation step is taken.
  • All contact joints are removed from the system.

Note that the built-in collision functions do not have to be used - other collision detection libraries can be used as long as they provide the right kinds of contact point information.

Typical simulation code

A typical simulation will proceed like this:

  • Create a dynamics world.
  • Create bodies in the dynamics world.
  • Set the state (position etc) of all bodies.
  • Create joints in the dynamics world.
  • Attach the joints to the bodies.
  • Set the parameters of all joints.
  • Create a collision world and collision geometry objects, as necessary.
  • Create a joint group to hold the contact joints.
  • Loop:
    • Apply forces to the bodies as necessary.
    • Adjust the joint parameters as necessary.
    • Call collision detection.
    • Create a contact joint for every collision point, and put it in the contact joint group.
    • Take a simulation step.
    • Remove all joints in the contact joint group.
  • Destroy the dynamics and collision worlds.

Physics model

The various methods and approximations that are used in ODE are discussed here.

Friction Approximation

We really need more pictures here.

The Coulomb friction model is a simple, but effective way to model friction at contact points. It is a simple relationship between the normal and tangential forces present at a contact point (see the contact joint section for a description of these forces). The rule is:

| fT | <= mu * | fN |

where fN and fT are the normal and tangential force vectors respectively, and mu is the friction coefficient (typically a number around 1.0). This equation defines a "friction cone" - imagine a cone with fN as the axis and the contact point as the vertex. If the total friction force vector is within the cone then the contact is in "sticking mode", and the friction force is enough to prevent the contacting surfaces from moving with respect to each other. If the force vector is on the surface of the cone then the contact is in "sliding mode", and the friction force is typically not large enough to prevent the contacting surfaces from sliding. The parameter mu thus specifies the maximum ratio of tangential to normal force.

ODE's friction models are approximations to the friction cone, for reasons of efficiency. There are currently two approximations to chose from:

  • The meaning of mu is changed so that it specifies the maximum friction (tangential) force that can be present at a contact, in either of the tangential friction directions. This is rather non physical because it is independent of the normal force, but it can be useful and it is the computationally cheapest option. Note that in this case mu is a force limit an must be chosen appropriate to the simulation.
  • The friction cone is approximated by a friction pyramid aligned with the first and second friction directions (I really need a picture here). A further approximation is made: first ODE computes the normal forces assuming that all the contacts are frictionless. Then it computes the maximum limits fm for the friction (tangential) forces from
fm = mu * | fN |

and then proceeds to solve for the entire system with these fixed limits (in a manner similar to approximation 1 above). This differs from a true friction pyramid in that the "effective" mu is not quite fixed. This approximation is easier to use as mu is a unit-less ratio the same as the normal Coloumb friction coefficient, and thus can be set to a constant value around 1.0 without regard for the specific simulation.

Data Types and Conventions

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

The basic data types

The ODE library can be built to use either single or double precision floating point numbers. Single precision is faster and uses less memory, but the simulation will have more numerical error that can result in visible problems. You will get less accuracy and stability with single precision.

(must describe what factors influence accuracy and stability).

The floating point data type is dReal. Other commonly used types are dVector3, dVector4, dMatrix3, dMatrix4, dQuaternion.

The non-scalar floating point types are all implemented as simple arrays of dReals. Their layout conventions are as follows:

Name Implementation Format
dQuaternion dReal[4] [ w, x, y, z ], where w is the real part and x, y, and z form the imaginary part.
dVector4 dReal[4] [ x, y, z, 1.0 ]
dVector3 dReal[4] (sic) Same as dVector4.
dMatrix4 dReal[4*4] A 4x4 matrix, laid out in row-major order, usually used as a homogeneous transform matrix. This means that the upper-left 3x3 elements are a rotation matrix, the first three elements of the last column are a translation vector, and the last row is simply [ 0, 0, 0, 1 ].
dMatrix3 dReal[3*4] A 3x4 matrix with the elements laid out in row-major order. Usually used as a 4x4 homogeneous matrix (see above) with the last row omitted as implicit.
dMatrix6 dReal[6*8] Declared in ode/common.h, but not used anywhere.

Objects and IDs

There are various kinds of object that can be created:

  • dWorld - a dynamics world.
  • dSpace - a collision space.
  • dBody - a rigid body.
  • dGeom - geometry (for collision).
  • dJoint - a joint
  • dJointGroup - a group of joints.

Functions that deal with these objects take and return object IDs. The object ID types are dWorldID, dBodyID, etc.

Argument conventions

All 3-vectors (x,y,z) supplied to "set" functions are given as individual x,y,z arguments.

All 3-vector result arguments to get() function are pointers to arrays of dReal.

Larger vectors are always supplied and returned as pointers to arrays of dReal.

All coordinates are in the global frame except where otherwise specified.

C versus C++

The ODE library is written in C++, but its public interface is made of simple C functions, not classes. Why is this?

  • Using a C interface only is simpler - the features of C++ features do not help much for ODE.
  • It prevents C++ mangling and runtime-support problems across multiple compilers.
  • The user doesn't have to be familiar with C++ quirks to use ODE.

Debugging

The ODE library can be compiled in "debugging" or "release" mode. Debugging mode is slower, but function arguments are checked and many run-time tests are done to ensure internal consistency. Release mode is faster, but no checking is done.

World

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

General Functions

The world object is a container for rigid bodies and joints. Objects in different worlds can not interact, for example rigid bodies from two different worlds can not collide.

All the objects in a world exist at the same point in time, thus one reason to use separate worlds is to simulate systems at different rates.

Most applications will only need one world.

dWorldID dWorldCreate();

Create a new, empty world and return its ID number.

void dWorldDestroy (dWorldID);

Destroy a world and everything in it. This includes all bodies, and all joints that are not part of a joint group. Joints that are part of a joint group will be deactivated, and can be destroyed by calling, for example, dJointGroupEmpty.

void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z);
void dWorldGetGravity (dWorldID, dVector3 gravity);

Set and get the world's global gravity vector. In the SI units the Earth's gravity vector would be (0,0,-9.81), assuming that +z is up. The default is no gravity, i.e. (0,0,0).

void dWorldSetERP (dWorldID, dReal erp);
dReal dWorldGetERP (dWorldID);

Set and get the global ERP value, that controls how much error correction is performed in each time step. Typical values are in the range 0.1--0.8. The default is 0.2.

void dWorldSetCFM (dWorldID, dReal cfm);
dReal dWorldGetCFM (dWorldID);

Set and get the global CFM (constraint force mixing) value. Typical values are in the range 10-9 -- 1. The default is 10-5 if single precision is being used, or 10-10 if double precision is being used.

void dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable);
int dWorldGetAutoDisableFlag (dWorldID);
void dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_threshold);
dReal dWorldGetAutoDisableLinearThreshold (dWorldID);
void dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_threshold);
dReal dWorldGetAutoDisableAngularThreshold (dWorldID);
void dWorldSetAutoDisableSteps (dWorldID, int steps);
int dWorldGetAutoDisableSteps (dWorldID);
void dWorldSetAutoDisableTime (dWorldID, dReal time);
dReal dWorldGetAutoDisableTime (dWorldID);

Set and get the default auto-disable parameters for newly created bodies. See section 6.5 for a description of the auto-disable feature. The default parameters are:

  • AutoDisableFlag = disabled
  • AutoDisableLinearThreshold = 0.01
  • AutoDisableAngularThreshold = 0.01
  • AutoDisableSteps = 10
  • AutoDisableTime = 0
void dWorldImpulseToForce (dWorldID, dReal stepsize, dReal ix, dReal iy, dReal iz, dVector3 force);

If you want to apply a linear or angular impulse to a rigid body, instead of a force or a torque, then you can use this function to convert the desired impulse into a force/torque vector before calling the dBodyAdd... function.

This function is given the desired impulse as (ix,iy,iz) and puts the force vector in force. The current algorithm simply scales the impulse by 1/stepsize, where stepsize is the step size for the next step that will be taken.

This function is given a dWorldID because, in the future, the force computation may depend on integrator parameters that are set as properties of the world.

Stepping Functions

void dWorldStep (dWorldID, dReal stepsize);

Step the world. This uses a "big matrix" method that takes time on the order of m3 and memory on the order of m2, where m is the total number of constraint rows.

For large systems this will use a lot of memory and can be very slow, but this is currently the most accurate method.

void dWorldQuickStep (dWorldID, dReal stepsize);

Step the world. This uses an iterative method that takes time on the order of m*N and memory on the order of m, where m is the total number of constraint rows and N is the number of iterations.

For large systems this is a lot faster than dWorldStep, but it is less accurate.

QuickStep is great for stacks of objects especially when the auto-disable feature is used as well. However, it has poor accuracy for near-singular systems. Near-singular systems can occur when using high-friction contacts, motors, or certain articulated structures. For example, a robot with multiple legs sitting on the ground may be near-singular.

There are ways to help overcome QuickStep's inaccuracy problems:

  • Increase CFM.
  • Reduce the number of contacts in your system (e.g. use the minimum number of contacts for the feet of a robot or creature).
  • Don't use excessive friction in the contacts.
  • Use contact slip if appropriate
  • Avoid kinematic loops (however, kinematic loops are inevitable in legged creatures).
  • Don't use excessive motor strength.
  • Use force-based motors instead of velocity-based motors.

Increasing the number of QuickStep iterations may help a little bit, but it is not going to help much if your system is really near singular.

void dWorldSetQuickStepNumIterations (dWorldID, int num);
int dWorldGetQuickStepNumIterations (dWorldID);

Set and get the number of iterations that the QuickStep method performs per step. More iterations will give a more accurate solution, but will take longer to compute. The default is 20 iterations.

void dWorldSetQuickStepW (WorldID, dReal over_relaxation);
dReal dWorldGetQuickStepW (dWorldID);

Set and get the over-relaxation parameter for QuickStep's SOR algorithm. Default value is 1.3.

Variable Step Size: Don't!

Stepsize should be constant in your application. A variable stepsize is a one-way trip to a headache. For example, think of an object "resting" on the ground. It'll actually stabilize at a small depth into the ground. When step sizes vary, the ideal stable position will change at each step, and thus the object will jitter (and may very well gain energy!)

ODE was designed with fixed step-sizes in mind. It is possible to use variable step-sizes in ODE, but it isn't easy (and this editor can't help you).


Damping

See the rigid bodies damping documentation for more details.

dReal dWorldGetLinearDamping(const dWorldID);
dReal dWorldGetAngularDamping(const dWorldID);
void dWorldSetLinearDamping(dWorldID, dReal scale);
void dWorldSetAngularDamping(dWorldID, dReal scale);
void dWorldSetDamping(dWorldID w, dReal linear_scale, dReal angular_scale);
dReal dWorldGetLinearDampingThreshold(const dWorldID);
dReal dWorldGetAngularDampingThreshold(const dWorldID);
void dWorldSetLinearDampingThreshold(dWorldID, dReal threshold);
void dWorldSetAngularDampingThreshold(dWorldID, dReal threshold);

Set/Get the world's default damping parameters (bodies will use the world's parameters by default). The default threshold is 0.01, and the default damping is zero (no damping).

dReal dWorldGetMaxAngularSpeed(const dWorldID w);
void dWorldSetMaxAngularSpeed(const dWorldID w, dReal max_speed);

Set/Get the default maximum angular speed for new bodies.

Contact Parameters

void  dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel); 
dReal dWorldGetContactMaxCorrectingVel (dWorldID); 

Set and get the maximum correcting velocity that contacts are allowed to generate. The default value is infinity (i.e. no limit). Reducing this value can help prevent "popping" of deeply embedded objects.

void  dWorldSetContactSurfaceLayer (dWorldID, dReal depth); 
dReal dWorldGetContactSurfaceLayer (dWorldID); 

Set and get the depth of the surface layer around all geometry objects. Contacts are allowed to sink into the surface layer up to the given depth before coming to rest. The default value is zero. Increasing this to some small value (e.g. 0.001) can help prevent jittering problems due to contacts being repeatedly made and broken.

Rigid Body Functions

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

Creating and Destroying Bodies

dBodyID dBodyCreate (dWorldID);

Create a body in the given world with default mass parameters at position (0,0,0). Return its ID.

void dBodyDestroy (dBodyID);

Destroy a body. All joints that are attached to this body will be put into limbo (i.e. unattached and not affecting the simulation, but they will NOT be deleted).

Position and orientation

void dBodySetPosition (dBodyID, dReal x, dReal y, dReal z);
void dBodySetRotation (dBodyID, const dMatrix3 R);
void dBodySetQuaternion (dBodyID, const dQuaternion q);
void dBodySetLinearVel (dBodyID, dReal x, dReal y, dReal z);
void dBodySetAngularVel (dBodyID, dReal x, dReal y, dReal z);
const dReal * dBodyGetPosition (dBodyID);
const dReal * dBodyGetRotation (dBodyID);
const dReal * dBodyGetQuaternion (dBodyID);
const dReal * dBodyGetLinearVel (dBodyID);
const dReal * dBodyGetAngularVel (dBodyID);

These functions set and get the position, rotation, linear and angular velocity of the body. After setting a group of bodies, the outcome of the simulation is undefined if the new configuration is inconsistent with the joints/constraints that are present. When getting, the returned values are pointers to internal data structures, so the vectors are valid until any changes are made to the rigid body system structure.

dBodyGetRotation returns a 4x3 rotation matrix.

Mass and force

void dBodySetMass (dBodyID, const dMass *mass);
void dBodyGetMass (dBodyID, dMass *mass);

Set/get the mass of the body (see the mass functions).

void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddRelForce (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddRelTorque (dBodyID, dReal fx, dReal fy, dReal fz);
void dBodyAddForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
void dBodyAddForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
void dBodyAddRelForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
void dBodyAddRelForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);

Add forces to bodies (absolute or relative coordinates). The forces are accumulated on to each body, and the accumulators are zeroed after each time step.

The ...RelForce and ...RelTorque functions take force vectors that are relative to the body's own frame of reference.

The ...ForceAtPos and ...ForceAtRelPos functions take an extra position vector (in global or body-relative coordinates respectively) that specifies the point at which the force is applied. All other functions apply the force at the center of mass.

const dReal * dBodyGetForce (dBodyID);
const dReal * dBodyGetTorque (dBodyID);

Return the current accumulated force and torque vector. The returned pointers point to an array of 3 dReals. The returned values are pointers to internal data structures, so the vectors are only valid until any changes are made to the rigid body system.

void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z);
void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z);

Set the body force and torque accumulation vectors. This is mostly useful to zero the force and torque for deactivated bodies before they are reactivated, in the case where the force-adding functions were called on them while they were deactivated.

Utility

void dBodyGetRelPointPos (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyGetRelPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyGetPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

Utility functions that take a point on a body (px,py,pz) and return that point's position or velocity in global coordinates (in result). The dBodyGetRelPointXXX functions are given the point in body relative coordinates, and the dBodyGetPointVel function is given the point in global coordinates.

void dBodyGetPosRelPoint (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

This is the inverse of dBodyGetRelPointPos. It takes a point in global coordinates (x,y,z) and returns the point's position in body-relative coordinates (result).

void dBodyVectorToWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);
void dBodyVectorFromWorld (dBodyID, dReal px, dReal py, dReal pz, dVector3 result);

Given a vector expressed in the body (or world) coordinate system (x,y,z), rotate it to the world (or body) coordinate system (result).

Automatic Enabling and Disabling

Every body can be enabled or disabled. Enabled bodies participate in the simulation, while disabled bodies are turned off and do not get updated during a simulation step. New bodies are always created in the enabled state.

A disabled body that is connected through a joint to an enabled body will be automatically re-enabled at the next simulation step.

Disabled bodies do not consume CPU time, therefore to speed up the simulation bodies should be disabled when they come to rest. This can be done automatically with the auto-disable feature.

If a body has its auto-disable flag turned on, it will automatically disable itself when

  • It has been idle for a given number of simulation steps.
  • It has also been idle for a given amount of simulation time.

A body is considered to be idle when the magnitudes of both its linear velocity and angular velocity are below given thresholds.

Thus, every body has five auto-disable parameters: an enabled flag, a idle step count, an idle time, and linear/angular velocity thresholds. Newly created bodies get these parameters from world.

The following functions set and get the enable/disable parameters of a body.

void dBodyEnable (dBodyID);
void dBodyDisable (dBodyID);

Manually enable and disable a body. Note that a disabled body that is connected through a joint to an enabled body will be automatically re-enabled at the next simulation step.

int dBodyIsEnabled (dBodyID);

Return 1 if a body is currently enabled or 0 if it is disabled.

void dBodySetAutoDisableFlag (dBodyID, int do_auto_disable);
int dBodyGetAutoDisableFlag (dBodyID); 

Set and get the auto-disable flag of a body. If the do_auto_disable is nonzero the body will be automatically disabled when it has been idle for long enough.

void dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_threshold);
dReal dBodyGetAutoDisableLinearThreshold (dBodyID);

Set and get a body's linear velocity threshold for automatic disabling. The body's linear velocity magnitude must be less than this threshold for it to be considered idle. Set the threshold to dInfinity to prevent the linear velocity from being considered.

void dBodySetAutoDisableAngularThreshold (dBodyID, dReal angular_threshold);
dReal dBodyGetAutoDisableAngularThreshold (dBodyID);

Set and get a body's angular velocity threshold for automatic disabling. The body's linear angular magnitude must be less than this threshold for it to be considered idle. Set the threshold to dInfinity to prevent the angular velocity from being considered.

void dBodySetAutoDisableSteps (dBodyID, int steps);
int dBodyGetAutoDisableSteps (dBodyID);

Set and get the number of simulation steps that a body must be idle before it is automatically disabled. Set this to zero to disable consideration of the number of steps.

void dBodySetAutoDisableTime (dBodyID, dReal time);
dReal dBodyGetAutoDisableTime (dBodyID);

Set and get the amount of simulation time that a body must be idle before it is automatically disabled. Set this to zero to disable consideration of the amount of simulation time.

void dBodySetAutoDisableAverageSamplesCount (dBodyID, unsigned int average_samples_count);
int dBodyGetAutoDisableAverageSamplesCount (dBodyID);

To be written ...

void dBodySetAutoDisableDefaults (dBodyID);

Set the auto-disable parameters of the body to the default parameters that have been set on the world.

void dBodySetMovedCallback(dBodyID b, void (*callback)(dBodyID));

Use it to register a function callback that is invoked whenever the body moves (that is, while it is not disabled). This is useful for integrating ODE with 3D engines, where 3D entities must be moved whenever a ODE body move. The callback must have the prototype void callback(dBodyID).

Damping

Damping serves two purposes: reduce simulation instability, and to allow the bodies to come to rest (and possibly auto-disabling them).

Bodies are constructed using the world's current damping parameters. Setting the scales to 0 disables the damping.

Here is how it is done: after every time step linear and angular velocities are tested against the corresponding thresholds. If they are above, they are multiplied by (1 - scale). So a negative scale value will actually increase the speed, and values greater than one will make the object oscillate every step; both can make the simulation unstable.

To disable damping just set the damping scale to zero.

Note: The velocities are damped after the stepper function has moved the object. Otherwise the damping could introduce errors in joints. First the joint constraints are processed by the stepper (moving the body), then the damping is applied.

Note: The damping happens right after the moved callback is called; this way it still possible use the exact velocities the body has acquired during the step. You can even use the callback to create your own customized damping.

dReal dBodyGetLinearDamping(dBodyID b);
dReal dBodyGetAngularDamping(dBodyID b);
void dBodySetLinearDamping(dBodyID b, dReal scale);
void dBodySetAngularDamping(dBodyID b, dReal scale);

Set and get the body's damping scale. After setting a damping scale, the body will ignore the world's damping scale until dBodySetDampingDefaults() is called. If a scale was not set, it returns the world's damping scale.

void dBodySetDamping(dBodyID b, dReal linear_scale, dReal angular_scale);

Convenience function to set both linear and angular scales at once.

dReal dBodyGetLinearDampingThreshold(dBodyID b);
dReal dBodyGetAngularDampingThreshold(dBodyID b);
void dBodySetLinearDampingThreshold(dBodyID b, dReal threshold);
void dBodySetAngularDampingThreshold(dBodyID b, dReal threshold);

Set/Get the body's damping thresholds. The damping will be applied only if the linear/angular speed is above the threshold limit.

void dBodySetDampingDefaults(dBodyID b);

Resets the damping settings to the current world's settings.

dReal dBodyGetMaxAngularSpeed(dWorldID w);
void dBodySetMaxAngularSpeed(dWorldID w, dReal max_speed);

You can also limit the maximum angular speed. In contrast to the damping functions, the angular velocity is affected before the body is moved. This means that it will introduce errors in joints that are forcing the body to rotate too fast. Some bodies have naturally high angular velocities (like cars' wheels), so you may want to give them a very high (like the default, dInfinity) limit.

Miscellaneous Body Functions

void dBodySetData (dBodyID, void *data);
void *dBodyGetData (dBodyID);

Get and set the body's user-data pointer.

void dBodySetFiniteRotationMode (dBodyID, int mode);

This function controls the way a body's orientation is updated at each time step. The mode argument can be:

  • 0: An "infinitesimal" orientation update is used. This is fast to compute, but it can occasionally cause inaccuracies for bodies that are rotating at high speed, especially when those bodies are joined to other bodies. This is the default for every new body that is created.
  • 1: A ``finite orientation update is used. This is more costly to compute, but will be more accurate for high speed rotations. Note however that high speed rotations can result in many types of error in a simulation, and this mode will only fix one of those sources of error.
int dBodyGetFiniteRotationMode (dBodyID);

Return the current finite rotation mode of a body (0 or 1).

void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z);

This sets the finite rotation axis for a body. This is axis only has meaning when the finite rotation mode is set (see dBodySetFiniteRotationMode).

If this axis is zero (0,0,0), full finite rotations are performed on the body.

If this axis is nonzero, the body is rotated by performing a partial finite rotation along the axis direction followed by an infinitesimal rotation along an orthogonal direction.

This can be useful to alleviate certain sources of error caused by quickly spinning bodies. For example, if a car wheel is rotating at high speed you can call this function with the wheel's hinge axis as the argument to try and improve its behavior.

void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result);

Return the current finite rotation axis of a body.

int dBodyGetNumJoints (dBodyID b);

Return the number of joints that are attached to this body.

dJointID dBodyGetJoint (dBodyID, int index);

Return a joint attached to this body, given by index. Valid indexes are 0 to n-1 where n is the value returned by dBodyGetNumJoints.

dWorldID dBodyGetWorld(dBodyID b);

Retrieves the world attached to the given body.

void dBodySetGravityMode (dBodyID b, int mode);
int dBodyGetGravityMode (dBodyID b);

Set/get whether the body is influenced by the world's gravity or not. If mode is nonzero it is, if mode is zero, it isn't. Newly created bodies are always influenced by the world's gravity.

dGeomID dBodyGetFirstGeom(dBodyID b);
dGeomID dBodyGetNextGeom(dGeomID g);

Give access to all geoms associated with a body. Use dBodyGetFirstGeom() to retrieve the first geom, then call dBodyGetNextGeom() with the previous geom as argument, to retrieve the next.

Joint Types and Functions

image:ODETutorial.png
This article is part
of the ODE Manual
Chapters ...

Introduction
Install and Use
Concepts
Data Types and Conventions
World
Rigid Body Functions
Joint Types and Functions
StepFast
Support Functions
Collision Detection

Creating and Destroying Joints

dJointID dJointCreateBall (dWorldID, dJointGroupID);
dJointID dJointCreateHinge (dWorldID, dJointGroupID);
dJointID dJointCreateSlider (dWorldID, dJointGroupID);
dJointID dJointCreateContact (dWorldID, dJointGroupID, const dContact *);
dJointID dJointCreateUniversal (dWorldID, dJointGroupID);
dJointID dJointCreateHinge2 (dWorldID, dJointGroupID);
dJointID dJointCreatePR (dWorldID, dJointGroupID);
dJointID dJointCreatePU (dWorldID, dJointGroupID);
dJointID dJointCreatePiston (dWorldID, dJointGroupID);
dJointID dJointCreateFixed (dWorldID, dJointGroupID);
dJointID dJointCreateAMotor (dWorldID, dJointGroupID);
dJointID dJointCreateLMotor (dWorldID, dJointGroupID);
dJointID dJointCreatePlane2d (dWorldID, dJointGroupID);

Create a new joint of a given type. The joint is initially in "limbo" (i.e. it has no effect on the simulation) because it does not connect to any bodies. The joint group ID is 0 to allocate the joint normally. If it is nonzero the joint is allocated in the given joint group. The contact joint will be initialized with the given dContact structure.

void dJointDestroy (dJointID);

Destroy a joint, disconnecting it from its attached bodies and removing it from the world. However, if the joint is a member of a group then this function has no effect - to destroy that joint the group must be emptied or destroyed.

dJointGroupID dJointGroupCreate (int max_size);

Create a joint group. The max_size argument is now unused and should be set to 0. It is kept for backwards compatibility.

void dJointGroupDestroy (dJointGroupID);

Destroy a joint group. All joints in the joint group will be destroyed.

void dJointGroupEmpty (dJointGroupID);

Empty a joint group. All joints in the joint group will be destroyed, but the joint group itself will not be destroyed.

Miscellaneous Joint Functions

void dJointAttach (dJointID, dBodyID body1, dBodyID body2);

Attach the joint to some new bodies. If the joint is already attached, it will be detached from the old bodies first. To attach this joint to only one body, set body1 or body2 to zero - a zero body refers to the static environment. Setting both bodies to zero puts the joint into "limbo", i.e. it will have no effect on the simulation.

Some joints, like hinge-2 need to be attached to two bodies to work.

void dJointSetData (dJointID, void *data);
void *dJointGetData (dJointID);

Get and set the joint's user-da