phyhinge.cpp
1 /********************************************************************************
2  * WorldSim -- library for robot simulations *
3  * Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "phyhinge.h"
21 #include "private/phyjointprivate.h"
22 #include "private/phyobjectprivate.h"
23 #include "private/worldprivate.h"
24 
25 // This is needed because the isnan and isinf functions are not present windows
26 #ifdef FARSA_WIN
27  #include <float.h>
28  #define isnan(x) _isnan(x)
29 #else
30  #define isnan(x) std::isnan(x)
31 #endif
32 
33 namespace farsa {
34 
35 void PhyHinge::construct( const wVector& axis, const wVector& centre, real startAngle, PhyObject* parent, PhyObject* child, bool cp ) {
36  dof = new PhyDOF( this, axis, centre, false );
37  dofsv << dof;
38  dofv = 1;
39  forceOnJoint = wVector(0.0, 0.0, 0.0);
40 
41  //--- calculate the localMatrixParent
42  //--- start creating an orthonormal coordinate frame using grammSchmidt procedure
44  //--- calculate the axis that connect the centre of joint with centre of child object
45  wVector start = centre;
46  wVector end = child->matrix().w_pos;
47  if ( parent ) {
48  start -= parent->matrix().w_pos;
49  }
50  wVector newx = (start-end).normalize();
51  if ( isnan(newx[0]) || isnan(newx[1]) || isnan(newx[2]) ) {
52  // this happens when it is not possible to calculate the axis above,
53  // --- in this case nothing has to be done
54  } else {
55  //--- rotate the matrix in order to align X axis calculated from grammSchmidt procedure
56  // to the axis connecting centres. In this way, angle zero correspond to position at which
57  // child object was connected by this joint
58  real sinAngle = (newx * localMatrixParent.x_ax) % localMatrixParent.z_ax;
59  real cosAngle = newx % localMatrixParent.x_ax;
60  real angle = atan2( sinAngle, cosAngle );
61  wQuaternion ql( axis, angle );
62  localMatrixParent = localMatrixParent * wMatrix( ql, wVector(0,0,0) );
63  }
64  localMatrixParent.w_pos = centre;
66 
67  //--- calculate the local matrix respect to child object
68  if ( parent ) {
69  globalMatrixParent = localMatrixParent * parent->matrix();
70  } else {
72  }
73  localMatrixChild = globalMatrixParent * child->matrix().inverse();
74 
75  localMatrixParent.w_pos = wVector(0,0,0);
76  //--- it will rotate
77  // the localMatrixParent in order to align the axis
78  // with the requested startAngle
79  wQuaternion q( axis, startAngle );
80  localMatrixParent = localMatrixParent * wMatrix( q, wVector(0,0,0) );
81  localMatrixParent.w_pos = centre;
82  if ( parent ) {
83  globalMatrixParent = localMatrixParent * parent->matrix();
84  } else {
86  }
87 
88  dof->setAxis( globalMatrixParent.z_ax );
89  dof->setXAxis( globalMatrixParent.x_ax );
90  dof->setYAxis( globalMatrixParent.y_ax );
91  dof->setCentre( globalMatrixParent.w_pos );
92 
93  if ( !localMatrixParent.sanityCheck() ) {
94  qWarning( "Sanity Check Failed on localMatrixParent" );
95  }
96  if ( !localMatrixChild.sanityCheck() ) {
97  qWarning( "Sanity Check Failed on localMatrixChild" );
98  }
99 
100  if ( cp ) {
102  }
103 }
104 
105 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, real startAngle, PhyObject* parent, PhyObject* child, bool cp )
106  : PhyJoint( parent, child ) {
107  construct( axis, centre, startAngle, parent, child, cp );
108 }
109 
110 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, PhyObject* parent, PhyObject* child, bool cp )
111  : PhyJoint( parent, child ) {
112  construct( axis, centre, 0, parent, child, cp );
113 }
114 
115 PhyHinge::PhyHinge( const wVector& axis, real startAngle, PhyObject* parent, PhyObject* child, bool cp )
116  : PhyJoint( parent, child ) {
117  construct( axis, wVector(0,0,0), startAngle, parent, child, cp );
118 }
119 
120 PhyHinge::PhyHinge( const wVector& axis, PhyObject* parent, PhyObject* child, bool cp )
121  : PhyJoint( parent, child ) {
122  construct( axis, wVector(0,0,0), 0, parent, child, cp );
123 }
124 
125 PhyHinge::PhyHinge( const wVector& axis, const wVector& centre, const wVector& x_axis, PhyObject* parent, PhyObject* child, bool cp )
126  : PhyJoint( parent, child ) {
127  dof = new PhyDOF( this, axis, centre, false );
128  dofsv << dof;
129  dofv = 1;
130  //--- calculate the localMatrixParent
131  //--- supposing that axis and x_axis are ortogonals
132  localMatrixParent.x_ax = x_axis;
133  localMatrixParent.y_ax = axis * x_axis;
134  localMatrixParent.z_ax = axis;
135  localMatrixParent.w_pos = centre;
137 
138  //--- calculate the local matrix respect to child object
139  if ( parent ) {
141  } else {
143  }
145  if ( parent ) {
147  } else {
149  }
150 
151  dof->setAxis( globalMatrixParent.z_ax );
152  dof->setXAxis( globalMatrixParent.x_ax );
153  dof->setYAxis( globalMatrixParent.y_ax );
154  dof->setCentre( globalMatrixParent.w_pos );
155 
156  if ( !localMatrixParent.sanityCheck() ) {
157  qWarning( "Sanity Check Failed on localMatrixParent" );
158  }
159  if ( !localMatrixChild.sanityCheck() ) {
160  qWarning( "Sanity Check Failed on localMatrixChild" );
161  }
162 
163  if ( cp ) {
165  }
166 }
167 
169  //--- calculate global matrix if necessary
170  if ( parent() ) {
172  }
173  return globalMatrixParent.w_pos;
174 }
175 
177 {
178  return forceOnJoint;
179 }
180 
182 #ifdef WORLDSIM_USE_NEWTON
183  priv->joint = NewtonConstraintCreateUserJoint( worldpriv->world, 6, PhyJointPrivate::userBilateralHandler, 0, priv->child, priv->parent );
184  NewtonJointSetUserData( priv->joint, this );
185 #endif
186 }
187 
189  //--- calculate the global matrices
190  //--- if parent doesn't exist, then globalMatrixParent and localMatrixParent coincide and
191  //--- there is no need to re-assign it because it was already done in constructor
192  if ( parent() ) {
194  }
196 
197  if ( !globalMatrixParent.sanityCheck() ) {
198  qWarning( "Sanity Check Failed on globalMatrixParent" );
199  }
200  if ( !globalMatrixChild.sanityCheck() ) {
201  qWarning( "Sanity Check Failed on globalMatrixChild" );
202  }
203 
204  dof->setAxis( globalMatrixParent.z_ax );
205  dof->setXAxis( globalMatrixParent.x_ax );
206  dof->setYAxis( globalMatrixParent.y_ax );
207  dof->setCentre( globalMatrixParent.w_pos );
208 
209  PhyDOF* dof = dofsv[0];
210  //--- calculate the rotation assuming the local X axis of joint frame as 0
211  //--- and following the right-hand convention
212  real sinAngle = (globalMatrixParent.x_ax * globalMatrixChild.x_ax) % globalMatrixChild.z_ax;
213  real cosAngle = globalMatrixChild.x_ax % globalMatrixParent.x_ax;
214  real angle = atan2( sinAngle, cosAngle );
215  real vel;
216  // Velocity is computed in two different ways depending on whether the joint is enabled or not:
217  // if it is, we use Newton functions to get the angular velocities (for a better result), otherwise
218  // we revert to computing the difference with the previous position divided by the timestep
219  if (enabled) {
220  //--- the velocity is calculated projecting the angular velocity of objects on the main axis (z_ax)
221  // This code is not general, becuase it is not appliable in the case of parent==NULL
222  // and also return different results respect to the kinematic way
223  // Then [Gianluca] comment it and replaced with the same code used in the kinematic mode
224  wVector omegaParent, omegaChild;
225 #ifdef WORLDSIM_USE_NEWTON
226  if ( parent() ) {
227  NewtonBodyGetOmega( priv->parent, &omegaParent[0] );
228  } else {
229  omegaParent = wVector(0, 0, 0);
230  }
231  NewtonBodyGetOmega( priv->child, &omegaChild[0] );
232 #endif
233  real velP = omegaParent % globalMatrixChild.z_ax;
234  real velC = omegaChild % globalMatrixChild.z_ax;
235  vel = velC - velP;
236  //vel = (angle - dof->position()) / (world()->timeStep());
237  } else {
238  vel = (angle - dof->position()) / (world()->timeStep());
239  }
240  dof->setPosition( angle );
241  dof->setVelocity( vel );
242 }
243 
244 void PhyHinge::updateJoint( real timestep ) {
245  // If the joint is disabled, we don't do anything here
246  if (!enabled) {
247  return;
248  }
249 
250  // Updating the global matrices and the dof
251  updateJointInfo();
252 
253  const real angle = dof->position();
254  const real vel = dof->velocity();
255 
256 #ifdef WORLDSIM_USE_NEWTON
257  //--- Restrict the movement on the centre of the joint along all tree orthonormal direction
258  NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.x_ax[0] );
259  NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
260  NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.y_ax[0] );
261  NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
262  NewtonUserJointAddLinearRow( priv->joint, &globalMatrixChild.w_pos[0], &globalMatrixParent.w_pos[0], &globalMatrixChild.z_ax[0] );
263  NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
264 
265  //--- In order to constraint the rotation about X and Y axis of the joint
266  //--- we use LinearRow (that are stronger) getting a point far from objects along
267  //--- the Z axis. Doing this if the two object rotates about X or Y axes then
268  //--- the difference between qChild and qParent augments and then Newton Engine will apply
269  //--- a corresponding force (that applyied outside the centre of the object will become
270  //--- a torque) that will blocks any rotation about X and Y axis
271  real len = 50000.0;
272  wVector qChild( globalMatrixChild.w_pos + globalMatrixChild.z_ax.scale(len) );
273  wVector qParent( globalMatrixParent.w_pos + globalMatrixParent.z_ax.scale(len) );
274  NewtonUserJointAddLinearRow( priv->joint, &qChild[0], &qParent[0], &globalMatrixChild.x_ax[0] );
275  NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
276  NewtonUserJointAddLinearRow( priv->joint, &qChild[0], &qParent[0], &globalMatrixChild.y_ax[0] );
277  NewtonUserJointSetRowStiffness( priv->joint, 1.0 );
278 
279  if ( dof->isLimited() ) {
280  real lolimit;
281  real hilimit;
282  dof->limits( lolimit, hilimit );
283  if ( angle < lolimit ) {
284  real relAngle = lolimit - angle; // - lolimit;
285  //--- the command is opposite to the movement required to go away from the limit
286  // so, it will be overrided for going away from the limit
287  NewtonUserJointAddAngularRow( priv->joint, relAngle, &globalMatrixChild.z_ax[0] );
288  NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
289  NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 50000.0, 100 );
290  //--- this allow the joint to move back freely
291  NewtonUserJointSetRowMinimumFriction( priv->joint, 0.0 );
292  return;
293  } else if ( angle > hilimit ) {
294  real relAngle = hilimit - angle; // - hilimit;
295  //--- the command is opposite to the movement required to go away from the limit
296  // so, it will be overrided for going away from the limit
297  NewtonUserJointAddAngularRow( priv->joint, relAngle, &globalMatrixChild.z_ax[0] );
298  NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
299  NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 50000.0, 100 );
300  //--- this allow the joint to move forth freely
301  NewtonUserJointSetRowMaximumFriction( priv->joint, 0.0 );
302  return;
303  }
304  }
305 
306  //--- if it reach this point means that the joint if far from limits
307  //--- and we check for type of motion and we'll apply the corresponding entity
308  real force, accel, wishangle, wishvel, sign;
309  switch( dof->motion() ) {
310  case PhyDOF::force:
311  //--- force will be used to set-up max and min torque newton will use to
312  //--- resolve the constraint and accel to the double of appliedForce
313  //--- this means that Newton will apply exactly forse amount of torque around
314  //--- the axis
315  force = fabs( dof->appliedForce() );
316  accel = 2.0*dof->appliedForce();
317  NewtonUserJointAddAngularRow( priv->joint, 0.0, &globalMatrixChild.z_ax[0] );
318  NewtonUserJointSetRowMinimumFriction( priv->joint, -force );
319  NewtonUserJointSetRowMaximumFriction( priv->joint, +force );
320  NewtonUserJointSetRowAcceleration( priv->joint, accel );
321  NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
322  break;
323  case PhyDOF::vel:
324  //--- I'm not sure that this implementation is correct
325  accel = 0.8*( dof->desiredVelocity() - vel ) / ( timestep );
326  wishangle = (dof->desiredVelocity()*timestep);
327  NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
328  NewtonUserJointSetRowAcceleration( priv->joint, accel );
329  NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
330  break;
331  case PhyDOF::pos:
332  wishangle = dof->desiredPosition() - angle;
333  wishvel = wishangle / timestep;
334  if ( fabs(wishvel) > dof->maxVelocity() ) {
335  sign = (wishvel<0) ? -1.0 : +1.0;
336  wishvel = sign*dof->maxVelocity();
337  }
338  accel = 0.8*( wishvel - vel ) / ( timestep );
339  NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
340  NewtonUserJointSetRowAcceleration( priv->joint, accel );
341  NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
342  //NewtonUserJointAddAngularRow( priv->joint, wishangle, &globalMatrixChild.z_ax[0] );
343  //NewtonUserJointSetRowSpringDamperAcceleration( priv->joint, 30000.0, 2000 );
344  //NewtonUserJointSetRowStiffness( priv->joint, dof->stiffness() );
345  break;
346  case PhyDOF::off:
347  default:
348  break;
349  }
350 
351  //--- Retrive forces applied to the joint by the constraints
352  forceOnJoint.x = NewtonUserJointGetRowForce (priv->joint, 0);
353  forceOnJoint.y = NewtonUserJointGetRowForce (priv->joint, 1);
354  forceOnJoint.z = NewtonUserJointGetRowForce (priv->joint, 2);
355 #endif
356 
357 }
358 
359 } // end namespace farsa