worldsim/src/phyhinge.cpp

00001 /********************************************************************************
00002  *  WorldSim -- library for robot simulations                                   *
00003  *  Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "phyhinge.h"
00021 #include "private/phyjointprivate.h"
00022 #include "private/phyobjectprivate.h"
00023 #include "private/worldprivate.h"
00024 
00025 // This is needed because the isnan and isinf functions are not present windows
00026 #ifdef FARSA_WIN
00027     #include <float.h>
00028     #define isnan(x) _isnan(x)
00029 #endif
00030 
00031 namespace farsa {
00032 
00033 void PhyHinge::construct( const wVector& axis, const wVector& centre, real startAngle, PhyObject* parent, PhyObject* child, bool cp ) {
00034     dof = new PhyDOF( this, axis, centre, false );
00035     dofsv << dof;
00036     dofv = 1;
00037     //--- calculate the localMatrixParent
00038     //--- start creating an orthonormal coordinate frame using grammSchmidt procedure
00039     localMatrixParent = wMatrix::grammSchmidt(axis);
00040     //--- calculate the axis that connect the centre of joint with centre of child object
00041     wVector start = centre;
00042     wVector end = child->matrix().w_pos;
00043     if ( parent ) {
00044         start -= parent->matrix().w_pos;
00045     }
00046     wVector newx = (start-end).normalize();
00047     if ( isnan(newx[0]) || isnan(newx[1]) || isnan(newx[2]) ) {
00048         // this happens when it is not possible to calculate the axis above,
00049         // --- in this case nothing has to be done
00050     } else {
00051         //--- rotate the matrix in order to align X axis calculated from grammSchmidt procedure
00052         //    to the axis connecting centres. In this way, angle zero correspond to position at which
00053         //    child object was connected by this joint
00054         real sinAngle = (newx * localMatrixParent.x_ax) % localMatrixParent.z_ax;
00055         real cosAngle = newx % localMatrixParent.x_ax;
00056         real angle = atan2( sinAngle, cosAngle );
00057         wQuaternion ql( axis, angle );
00058         localMatrixParent = localMatrixParent * wMatrix( ql, wVector(0,0,0) );
00059     }
00060     localMatrixParent.w_pos = centre;
00061     localMatrixParent.sanitifize();
00062     
00063     //--- calculate the local matrix respect to child object
00064     if ( parent ) {
00065         globalMatrixParent = localMatrixParent * parent->matrix();
00066     } else {
00067         globalMatrixParent = localMatrixParent;
00068     }
00069     localMatrixChild = globalMatrixParent * child->matrix().inverse();
00070 
00071     localMatrixParent.w_pos = wVector(0,0,0);
00072     //--- it will rotate
00073     //    the localMatrixParent in order to align the axis
00074     //    with the requested startAngle
00075     wQuaternion q( axis, startAngle );
00076     localMatrixParent = localMatrixParent * wMatrix( q, wVector(0,0,0) );
00077     localMatrixParent.w_pos = centre;
00078     if ( parent ) {
00079         globalMatrixParent = localMatrixParent * parent->matrix();
00080     } else {
00081         globalMatrixParent = localMatrixParent;
00082     }
00083 
00084     dof->setAxis( globalMatrixParent.z_ax );
00085     dof->setXAxis( globalMatrixParent.x_ax );
00086     dof->setYAxis( globalMatrixParent.y_ax );
00087     dof->setCentre( globalMatrixParent.w_pos );
00088     
00089     if ( !localMatrixParent.sanityCheck() ) {
00090         qWarning( "Sanity Check Failed on localMatrixParent" );
00091     }
00092     if ( !localMatrixChild.sanityCheck() ) {
00093         qWarning( "Sanity Check Failed on localMatrixChild" );
00094     }
00095     
00096     if ( cp ) {
00097         createPrivateJoint();
00098     }
00099 }
00100 
00101 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, real startAngle, PhyObject* parent, PhyObject* child, bool cp )
00102     : PhyJoint( parent, child ) {
00103     construct( axis, centre, startAngle, parent, child, cp );
00104 }
00105 
00106 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, PhyObject* parent, PhyObject* child, bool cp )
00107     : PhyJoint( parent, child ) {
00108     construct( axis, centre, 0, parent, child, cp );
00109 }
00110 
00111 PhyHinge::PhyHinge( const wVector& axis, real startAngle, PhyObject* parent, PhyObject* child, bool cp )
00112     : PhyJoint( parent, child ) {
00113     construct( axis, wVector(0,0,0), startAngle, parent, child, cp );
00114 }
00115 
00116 PhyHinge::PhyHinge( const wVector& axis, PhyObject* parent, PhyObject* child, bool cp )
00117     : PhyJoint( parent, child ) {
00118     construct( axis, wVector(0,0,0), 0, parent, child, cp );
00119 }
00120 
00121 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, const wVector& x_axis, PhyObject* parent, PhyObject* child, bool cp )
00122     : PhyJoint( parent, child ) {
00123     dof = new PhyDOF( this, axis, centre, false );
00124     dofsv << dof;
00125     dofv = 1;
00126     //--- calculate the localMatrixParent
00127     //--- supposing that axis and x_axis are ortogonals
00128     localMatrixParent.x_ax = x_axis;
00129     localMatrixParent.y_ax = axis * x_axis;
00130     localMatrixParent.z_ax = axis;
00131     localMatrixParent.w_pos = centre;
00132     localMatrixParent.sanitifize();
00133     
00134     //--- calculate the local matrix respect to child object
00135     if ( parent ) {
00136         globalMatrixParent = localMatrixParent * parent->matrix();
00137     } else {
00138         globalMatrixParent = localMatrixParent;
00139     }
00140     localMatrixChild = globalMatrixParent * child->matrix().inverse();
00141     if ( parent ) {
00142         globalMatrixParent = localMatrixParent * parent->matrix();
00143     } else {
00144         globalMatrixParent = localMatrixParent;
00145     }
00146 
00147     dof->setAxis( globalMatrixParent.z_ax );
00148     dof->setXAxis( globalMatrixParent.x_ax );
00149     dof->setYAxis( globalMatrixParent.y_ax );
00150     dof->setCentre( globalMatrixParent.w_pos );
00151     
00152     if ( !localMatrixParent.sanityCheck() ) {
00153         qWarning( "Sanity Check Failed on localMatrixParent" );
00154     }
00155     if ( !localMatrixChild.sanityCheck() ) {
00156         qWarning( "Sanity Check Failed on localMatrixChild" );
00157     }
00158     
00159     if ( cp ) {
00160         createPrivateJoint();
00161     }
00162 }
00163 
00164 wVector PhyHinge::centre() const {
00165     //--- calculate global matrix if necessary
00166     if ( parent() ) {
00167         globalMatrixParent = localMatrixParent * parent()->matrix();
00168     }
00169     return globalMatrixParent.w_pos;
00170 }
00171 
00172 void PhyHinge::createPrivateJoint() {
00173 #ifdef WORLDSIM_USE_NEWTON
00174     priv->joint = NewtonConstraintCreateUserJoint( worldpriv->world, 6, PhyJointPrivate::userBilateralHandler, 0, priv->child, priv->parent );
00175     NewtonJointSetUserData( priv->joint, this );
00176 #endif
00177 }
00178 
00179 void PhyHinge::updateJointInfo() {
00180     //--- calculate the global matrices
00181     //--- if parent doesn't exist, then globalMatrixParent and localMatrixParent coincide and
00182     //--- there is no need to re-assign it because it was already done in constructor
00183     if ( parent() ) {
00184         globalMatrixParent = localMatrixParent * parent()->matrix();
00185     }
00186     globalMatrixChild = localMatrixChild * child()->matrix();
00187 
00188     if ( !globalMatrixParent.sanityCheck() ) {
00189         qWarning( "Sanity Check Failed on globalMatrixParent" );
00190     }
00191     if ( !globalMatrixChild.sanityCheck() ) {
00192         qWarning( "Sanity Check Failed on globalMatrixChild" );
00193     }
00194 
00195     dof->setAxis( globalMatrixParent.z_ax );
00196     dof->setXAxis( globalMatrixParent.x_ax );
00197     dof->setYAxis( globalMatrixParent.y_ax );
00198     dof->setCentre( globalMatrixParent.w_pos );
00199 
00200     PhyDOF* dof = dofsv[0];
00201     //--- calculate the rotation assuming the local X axis of joint frame as 0
00202     //--- and following the right-hand convention
00203     real sinAngle = (globalMatrixParent.x_ax * globalMatrixChild.x_ax) % globalMatrixChild.z_ax;
00204     real cosAngle = globalMatrixChild.x_ax % globalMatrixParent.x_ax;
00205     real angle = atan2( sinAngle, cosAngle );
00206     real vel;
00207     // Velocity is computed in two different ways depending on whether the joint is enabled or not:
00208     // if it is, we use Newton functions to get the angular velocities (for a better result), otherwise
00209     // we revert to computing the difference with the previous position divided by the timestep
00210     if (enabled) {
00211         //--- the velocity is calculated projecting the angular velocity of objects on the main axis (z_ax)
00212         //    This code is not general, becuase it is not appliable in the case of parent==NULL
00213         //    and also return different results respect to the kinematic way
00214         //    Then [Gianluca] comment it and replaced with the same code used in the kinematic mode
00215         wVector omegaParent, omegaChild;
00216 #ifdef WORLDSIM_USE_NEWTON
00217         if ( parent() ) {
00218             NewtonBodyGetOmega( priv->parent, &omegaParent[0] );
00219         } else {
00220             omegaParent = wVector(0, 0, 0);
00221         }
00222         NewtonBodyGetOmega( priv->child, &omegaChild[0] );
00223 #endif
00224         real velP = omegaParent % globalMatrixChild.z_ax;
00225         real velC = omegaChild % globalMatrixChild.z_ax;
00226         vel = velC - velP;
00227         //vel = (angle - dof->position()) / (world()->timeStep());
00228     } else {
00229         vel = (angle - dof->position()) / (world()->timeStep());
00230     }
00231     dof->setPosition( angle );
00232     dof->setVelocity( vel );
00233 }
00234 
00235 void PhyHinge::updateJoint( real timestep ) {
00236     // If the joint is disabled, we don't do anything here
00237     if (!enabled) {
00238         return;
00239     }
00240 
00241     // Updating the global matrices and the dof
00242     updateJointInfo();
00243 
00244     const real angle = dof->position();
00245     const real vel = dof->velocity();
00246 
00247 #ifdef WORLDSIM_USE_NEWTON
00248     //--- Restrict the movement on the centre of the joint along all tree orthonormal direction
00249     NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.x_ax[0] );
00250     NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
00251     NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.y_ax[0] );
00252     NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
00253     NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.z_ax[0] );
00254     NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
00255 
00256     //--- In order to constraint the rotation about X and Y axis of the joint
00257     //--- we use LinearRow (that are stronger) getting a point far from objects along
00258     //--- the Z axis. Doing this if the two object rotates about X or Y axes then
00259     //--- the difference between qChild and qParent augments and then Newton Engine will apply
00260     //--- a corresponding force (that applyied outside the centre of the object will become
00261     //--- a torque) that will blocks any rotation about X and Y axis
00262     real len = 50000.0;
00263     wVector qChild( globalMatrixChild.w_pos + globalMatrixChild.z_ax.scale(len) );
00264     wVector qParent( globalMatrixParent.w_pos + globalMatrixParent.z_ax.scale(len) );
00265     NewtonUserJointAddLinearRow( priv->joint, &qChild[0], &qParent[0], &globalMatrixChild.x_ax[0] );
00266     NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
00267     NewtonUserJointAddLinearRow( priv->joint, &qChild[0], &qParent[0], &globalMatrixChild.y_ax[0] );
00268     NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
00269 
00270     if ( dof->isLimited() ) {
00271         real lolimit;
00272         real hilimit;
00273         dof->limits( lolimit, hilimit );
00274         if ( angle < lolimit ) {
00275             real relAngle = lolimit - angle; // - lolimit;
00276             //--- the command is opposite to the movement required to go away from the limit
00277             //    so, it will be overrided for going away from the limit
00278             NewtonUserJointAddAngularRow( priv->joint, relAngle, &globalMatrixChild.z_ax[0] );
00279             NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00280             NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 50000.0, 100 );
00281             //--- this allow the joint to move back freely
00282             NewtonUserJointSetRowMinimumFriction( priv->joint, 0.0 );
00283             return;
00284         } else if ( angle > hilimit ) {
00285             real relAngle = hilimit - angle; // - hilimit;
00286             //--- the command is opposite to the movement required to go away from the limit
00287             //    so, it will be overrided for going away from the limit            
00288             NewtonUserJointAddAngularRow( priv->joint, relAngle, &globalMatrixChild.z_ax[0] );
00289             NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00290             NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 50000.0, 100 );
00291             //--- this allow the joint to move forth freely
00292             NewtonUserJointSetRowMaximumFriction( priv->joint, 0.0 );
00293             return;
00294         }
00295     }
00296         
00297     //--- if it reach this point means that the joint if far from limits
00298     //--- and we check for type of motion and we'll apply the corresponding entity
00299     real force, accel, wishangle, wishvel, sign;
00300     switch( dof->motion() ) {
00301     case PhyDOF::force:
00302         //--- force will be used to set-up max and min torque newton will use to
00303         //--- resolve the constraint and accel to the double of appliedForce
00304         //--- this means that Newton will apply exactly forse amount of torque around
00305         //--- the axis
00306         force = fabs( dof->appliedForce() );
00307         accel = 2.0*dof->appliedForce();
00308         NewtonUserJointAddAngularRow( priv->joint, 0.0, &globalMatrixChild.z_ax[0] );
00309         NewtonUserJointSetRowMinimumFriction( priv->joint, -force );
00310         NewtonUserJointSetRowMaximumFriction( priv->joint, +force );
00311         NewtonUserJointSetRowAcceleration( priv->joint, accel );
00312         NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00313         break;
00314     case PhyDOF::vel:
00315         //--- I'm not sure that this implementation is correct
00316         accel = 0.8*( dof->desiredVelocity() - vel ) / ( timestep );
00317         wishangle = (dof->desiredVelocity()*timestep);
00318         NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
00319         NewtonUserJointSetRowAcceleration( priv->joint, accel );
00320         NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00321         break;
00322     case PhyDOF::off:
00323     case PhyDOF::pos:
00324         wishangle = dof->desiredPosition() - angle;
00325         wishvel = wishangle / timestep;
00326         if ( fabs(wishvel) > dof->maxVelocity() ) {
00327             sign = (wishvel<0) ? -1.0 : +1.0;
00328             wishvel = sign*dof->maxVelocity();
00329         }
00330         accel = 0.8*( wishvel - vel ) / ( timestep );
00331         NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
00332         NewtonUserJointSetRowAcceleration( priv->joint, accel );
00333         NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00334         //NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
00335         //NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 30000.0, 2000 );
00336         //NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
00337         break;
00338     default:
00339         break;
00340     }
00341 #endif
00342 
00343 }
00344 
00345 } // end namespace farsa