motorcontrollers.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 "worldsimconfig.h"
21 #include "motorcontrollers.h"
22 #include "phyjoint.h"
23 #include "world.h"
24 #include "phymarxbot.h"
25 #include "phyfixed.h"
26 #include <cmath>
27 #include <QSet>
28 
29 #ifdef FARSA_USE_YARP_AND_ICUB
30 
31 #ifdef __GNUC__
32 #pragma GCC diagnostic ignored "-Wunused-parameter"
33 #endif
34  #include <yarp/os/Searchable.h>
35 #ifdef __GNUC__
36 #pragma GCC diagnostic warning "-Wunused-parameter"
37 #endif
38 
39 using namespace yarp::dev;
40 using namespace yarp::os;
41 #endif
42 
43 namespace farsa {
44 
45 wPID::wPID() {
46  p_gain = 0;
47  i_gain = 0;
48  d_gain = 0;
49  acc_ff = 0;
50  fri_ff = 0;
51  vel_ff = 0;
52  bias = 0;
53  accel_r = 0;
54  setpt = 0;
55  min = 0;
56  max = 0;
57  slew = 0;
58  this_target = 0;
59  next_target = 0;
60  integral = 0;
61  last_error = 0;
62  last_output = 0;
63 }
64 
65 double wPID::pidloop( double PV ) {
66  double this_error;
67  double this_output;
68  double accel;
69  double deriv;
70  double friction;
71  /* the desired PV for this iteration is the value */
72  /* calculated as next_target during the last loop */
73  this_target = next_target;
74  /* test for acceleration, compute new target PV for */
75  /* the next pass through the loop */
76  if ( accel_r > 0 && this_target != setpt) {
77  if ( this_target < setpt ) {
78  next_target += accel_r;
79  if ( next_target > setpt ) {
80  next_target = setpt;
81  }
82  } else { /* this_target > setpoint */
83  next_target -= accel_r;
84  if ( next_target < setpt ) {
85  next_target = setpt;
86  }
87  }
88  } else {
89  next_target = setpt;
90  }
91  /* acceleration is the difference between the PV */
92  /* target on this pass and the next pass through the */
93  /* loop */
94  accel = next_target - this_target;
95  /* the error for the current pass is the difference */
96  /* between the current target and the current PV */
97  this_error = this_target - PV;
98  /* the derivative is the difference between the error */
99  /* for the current pass and the previous pass */
100  deriv = this_error - last_error;
101  /* a very simple determination of whether there is */
102  /* special friction to be overcome on the next pass, */
103  /* if the current PV is 0 and the target for the next */
104  /* pass is not 0, stiction could be a problem */
105  friction = ( PV == 0.0 && next_target != 0.0 );
106  /* the new error is added to the integral */
107  integral += this_target - PV;
108  /* now that all of the variable terms have been */
109  /* computed they can be multiplied by the appropriate */
110  /* coefficients and the resulting products summed */
111  this_output = p_gain * this_error
112  + i_gain * integral
113  + d_gain * deriv
114  + acc_ff * accel
115  + vel_ff * next_target
116  + fri_ff * friction
117  + bias;
118  last_error = this_error;
119  /* check for slew rate limiting on the output change */
120  if ( 0 != slew ) {
121  if ( this_output - last_output > slew ) {
122  this_output = last_output + slew;
123  } else if ( last_output - this_output > slew ) {
124  this_output = last_output - slew;
125  }
126  }
127  /* now check the output value for absolute limits */
128  if ( this_output < min ) {
129  this_output = min;
130  } else if ( this_output > max ) {
131  this_output = max;
132  }
133  /* store the new output value to be used as the old */
134  /* output value on the next loop pass */
135  return last_output = this_output;
136 }
137 
138 void wPID::reset() {
139  // Resetting status variables
140  this_target = 0;
141  next_target = 0;
142  integral = 0;
143  last_error = 0;
144  last_output = 0;
145 }
146 
147 MotorController::MotorController( World* world ) {
148  enabled = true;
149  worldv = world;
150 }
151 
152 MotorController::~MotorController() {
153 }
154 
155 
156 MultiMotorController::MultiMotorController( QVector<PhyDOF*> dofs, World* world ) :
157  MotorController(world),
158 #ifdef FARSA_USE_YARP_AND_ICUB
159  IPositionControl(),
160  IVelocityControl(),
161 #endif
162  motorsv(dofs)
163 {
164  //--- common control variable initialization
165  controlModes.fill( 1, motorsv.size() );
166  refsacc.fill( 1.0, motorsv.size() );
167  //--- IPositionControl variable initialization
168  refs.fill( 0.0, motorsv.size() );
169  refsvel.fill( 1.0, motorsv.size() );
170  xfx0.fill( 0.0, motorsv.size() );
171  x0.fill( 0.0, motorsv.size() );
172  dx0.fill( 0.0, motorsv.size() );
173  t.fill( 0.0, motorsv.size() );
174  T.fill( 0.0, motorsv.size() );
175  stops.fill( true, motorsv.size() );
176  lastErrors.fill( 0.0, motorsv.size() );
177  lastIs.fill( 0.0, motorsv.size() );
178  lastTorques.fill( 0.0, motorsv.size() );
179  // create PIDs with default values
180  pos_pids.resize( motorsv.size() );
181  for( int i=0; i<motorsv.size(); i++ ) {
182  pos_pids[i] = new wPID();
183  pos_pids[i]->p_gain = 0.8;
184  pos_pids[i]->i_gain = 0.05;
185  pos_pids[i]->d_gain = 0.0;
186  pos_pids[i]->accel_r = 0;
187  pos_pids[i]->min = -PI_GRECO/2.0;
188  pos_pids[i]->max = +PI_GRECO/2.0;
189  }
190  //--- IVelocityControl variable initialization
191  desiredVel.fill( 0.0, motorsv.size() );
192  // create PIDs with default values
193  vel_pids.resize( motorsv.size() );
194  for( int i=0; i<motorsv.size(); i++ ) {
195  vel_pids[i] = new wPID();
196  vel_pids[i]->p_gain = 0.8;
197  vel_pids[i]->i_gain = 0.1;
198  vel_pids[i]->d_gain = 0.0;
199  //vel_pids[i]->accel_r = 3;
200  //vel_pids[i]->acc_ff = 0.3;
201  vel_pids[i]->min = -toRad( 120 ); // rads / second
202  vel_pids[i]->max = +toRad( 120 ); // rads / second
203  }
204  //--- IControlLimits variable initialization
205  loLimits.resize( motorsv.size() );
206  hiLimits.resize( motorsv.size() );
207  for( int i=0; i<motorsv.size(); i++ ) {
208  real fmin, fmax;
209  motorsv[i]->limits( fmin, fmax );
210  loLimits[i] = fmin;
211  hiLimits[i] = fmax;
212  }
213 }
214 
216  for( int i=0; i<motorsv.size(); i++ ) {
217  delete (vel_pids[i]);
218  delete (pos_pids[i]);
219  }
220 }
221 
223  real accum, /*wishangle,*/ wishvel;
224  for( int i=0; i<motorsv.size(); i++ ) {
225  switch( controlModes[i] ) {
226  case 1: /* Position Controller */
227  if ( ! stops[i] ) {
228  real tFracT = t[i] / T[i];
229  real tFracT3= tFracT * tFracT * tFracT;
230  real tFracT4= tFracT * tFracT3;
231  real tFracT5= tFracT * tFracT4;
232 
233  //--- code from p5f code from iCub/src/controller4Dc/Code/trajectory.c
234  //float xfx0 = _xf[jj] - _x0[jj]; --- already computed when positionMove has been called
235  //float dx0 = _dx0[jj]; --- computed when positionMove has been called
236  accum = dx0[i] * tFracT;
237  accum = accum + tFracT3 * (10.0*xfx0[i] - 6.0*dx0[i]);
238  accum = accum - tFracT4 * (15.0*xfx0[i] - 8.0*dx0[i]);
239  accum = accum + tFracT5 * (6.0*xfx0[i] - 3.0*dx0[i]);
240  //--- in p5f code are missing x0 because it's added inside step_trajectory
241  accum += x0[i];
242  } else {
243  accum = refs[i];
244  }
245  //--- accum is the angle at which the joint must be at current time
246  pos_pids[i]->setpt = accum;
247 // wishangle = pos_pids[i]->pidloop( fromRawPosition(i) );
248  motorsv[i]->setDesiredPosition( toRawPosition(accum, i) /*wishangle*/ );
249  //--- advance t
250  t[i] += world()->timeStep();
251  //--- stop if necessary
252  stops[i] = ( t[i] / T[i] > 1.0 );
253  break;
254  case 2: /* Velocity Controller */
255  //--- the velocity at which the joint must be at current time
256  vel_pids[i]->setpt = desiredVel[i];
257  wishvel = vel_pids[i]->pidloop( motorsv[i]->velocity() );
258  motorsv[i]->setDesiredVelocity( wishvel );
259 // motorsv[i]->setDesiredVelocity(desiredVel[i]); // This disables the PID
260  break;
261  default:
262  break;
263  }
264  }
265  return;
266 }
267 
268 #ifdef FARSA_USE_YARP_AND_ICUB
269 
270 bool MultiMotorController::open( yarp::os::Searchable & ) {
271  return true;
272 }
273 
275  return true;
276 }
277 
278 bool MultiMotorController::configure( yarp::os::Searchable & ) {
279  return true;
280 }
281 
282 #endif
283 
285  *ax = motorsv.size();
286  return true;
287 }
288 
290  //controlMode = 1;
291  return true;
292 }
293 
294 bool MultiMotorController::positionMove(int j, double ref) {
295  if ( j < 0 && j >= motorsv.size() ) {
296  return false;
297  }
298  if ( motorsv[j]->translate() ) {
299  refs[j] = ref;
300  } else {
301  refs[j] = toRad( ref );
302  }
303  if ( motorsv[j]->isLimited() ) {
304  //--- check the limits
305  //--- stay too near to the limits may create oscillation of the joint
306  //--- about 0.5% of the range of the joint
307  real offset = (fabs(hiLimits[j])-fabs(loLimits[j]))*0.005;
308  refs[j] = ramp( loLimits[j]+offset, hiLimits[j]-offset, refs[j] );
309  }
310  //--- calculate xfx0, dx0, T
311  x0[j] = fromRawPosition(j);
312  xfx0[j] = refs[j] - x0[j];
313  dx0[j] = motorsv[j]->velocity();
314  T[j] = fabs( xfx0[j] ) / refsvel[j];
315  //--- reset t to zero
316  t[j] = 0.0;
317  // if T[j] is less that 10 ms, then it simply stay there
318  stops[j] = ( T[j] < 0.010 );
319  controlModes[j] = 1;
320  return true;
321 }
322 
323 bool MultiMotorController::positionMove(const double *newrefs) {
324  for( int i=0; i<refs.size(); i++ ) {
325  if ( motorsv[i]->translate() ) {
326  refs[i] = newrefs[i];
327  } else {
328  refs[i] = toRad( newrefs[i] );
329  }
330  if ( motorsv[i]->isLimited() ) {
331  //--- check the limits
332  //--- stay too near to the limits may create oscillation of the joint
333  //--- about 0.5% of the range of the joint
334  real offset = (fabs(hiLimits[i])-fabs(loLimits[i]))*0.005;
335  refs[i] = ramp( loLimits[i]+offset, hiLimits[i]-offset, refs[i] );
336  }
337  //--- calculate xfx0, dx0, T
338  x0[i] = fromRawPosition( i );
339  xfx0[i] = refs[i] - x0[i];
340  dx0[i] = motorsv[i]->velocity();
341  T[i] = fabs( xfx0[i] ) / refsvel[i];
342  //--- reset t to zero
343  t[i] = 0.0;
344  // if T[j] is less that 10 ms, then it simply stay there
345  stops[i] = ( T[i] < 0.010 );
346  controlModes[i] = 1;
347  }
348  return true;
349 }
350 
351 bool MultiMotorController::relativeMove(int j, double delta) {
352  if ( j < 0 && j >= motorsv.size() ) {
353  return false;
354  }
355  if ( motorsv[j]->translate() ) {
356  refs[j] += delta;
357  } else {
358  refs[j] += toRad( delta );
359  }
360  if ( motorsv[j]->isLimited() ) {
361  //--- check the limits
362  //--- stay too near to the limits may create oscillation of the joint
363  //--- about 0.5% of the range of the joint
364  real offset = (fabs(hiLimits[j])-fabs(loLimits[j]))*0.005;
365  refs[j] = ramp( loLimits[j]+offset, hiLimits[j]-offset, refs[j] );
366  }
367  //--- calculate xfx0, dx0, T
368  x0[j] = fromRawPosition(j);
369  xfx0[j] = refs[j] - x0[j];
370  dx0[j] = motorsv[j]->velocity();
371  T[j] = fabs( xfx0[j] ) / refsvel[j];
372  //--- reset t to zero
373  t[j] = 0.0;
374  stops[j] = false;
375  controlModes[j] = 1;
376  return true;
377 }
378 
379 bool MultiMotorController::relativeMove(const double *deltas) {
380  for( int i=0; i<refs.size(); i++ ) {
381  if ( motorsv[i]->translate() ) {
382  refs[i] += deltas[i];
383  } else {
384  refs[i] += toRad( deltas[i] );
385  }
386  if ( motorsv[i]->isLimited() ) {
387  //--- check the limits
388  //--- stay too near to the limits may create oscillation of the joint
389  //--- about 0.5% of the range of the joint
390  real offset = (fabs(hiLimits[i])-fabs(loLimits[i]))*0.005;
391  refs[i] = ramp( loLimits[i]+offset, hiLimits[i]-offset, refs[i] );
392  }
393  //--- calculate xfx0, dx0, T
394  x0[i] = fromRawPosition( i );
395  xfx0[i] = refs[i] - x0[i];
396  dx0[i] = motorsv[i]->velocity();
397  T[i] = fabs( xfx0[i] ) / refsvel[i];
398  //--- reset t to zero
399  t[i] = 0.0;
400  stops[i] = false;
401  controlModes[i] = 1;
402  }
403  return true;
404 }
405 
406 bool MultiMotorController::checkMotionDone(int j, bool *flag) {
407  if ( j < 0 && j >= motorsv.size() ) {
408  return false;
409  }
410  //--- I should check the actual position or just if stops[j] is true ??
411  *flag = ( fabs(fromRawPosition(j) - refs[j]) < 0.01745329 );
412  //*flag = stops[j];
413  return true;
414 }
415 
417  bool ends = true;
418  for( int i=0; i<refs.size(); i++ ) {
419  //--- I should check the actual position or just if stops[j] is true ??
420  ends &= ( fabs(fromRawPosition(i) - refs[i]) < 0.01745329 );
421  //ends &= stops[i];
422  if ( !ends ) break;
423  }
424  *flag = ends;
425  return true;
426 }
427 
428 bool MultiMotorController::setRefSpeed(int j, double sp) {
429  if ( j < 0 && j >= motorsv.size() ) {
430  return false;
431  }
432  if ( motorsv[j]->translate() ) {
433  refsvel[j] = sp;
434  } else {
435  refsvel[j] = toRad(sp);
436  }
437  motorsv[j]->setMaxVelocity( refsvel[j] );
438  return true;
439 }
440 
441 bool MultiMotorController::setRefSpeeds(const double *spds) {
442  for( int i=0; i<refs.size(); i++ ) {
443  if ( motorsv[i]->translate() ) {
444  refsvel[i] = spds[i];
445  } else {
446  refsvel[i] = toRad(spds[i]);
447  }
448  motorsv[i]->setMaxVelocity( refsvel[i] );
449  }
450  return true;
451 }
452 
454  if ( j < 0 && j >= motorsv.size() ) {
455  return false;
456  }
457  if ( motorsv[j]->translate() ) {
458  refsacc[j] = acc;
459  } else {
460  refsacc[j] = toRad(acc);
461  }
462  return true;
463 }
464 
466  for( int i=0; i<refs.size(); i++ ) {
467  if ( motorsv[i]->translate() ) {
468  refsacc[i] = accs[i];
469  } else {
470  refsacc[i] = toRad(accs[i]);
471  }
472  }
473  return true;
474 }
475 
476 bool MultiMotorController::getRefSpeed(int j, double *ref) {
477  if ( j < 0 && j >= motorsv.size() ) {
478  return false;
479  }
480  if ( motorsv[j]->translate() ) {
481  *ref = refsvel[j];
482  } else {
483  *ref = toDegree(refsvel[j]);
484  }
485  return true;
486 }
487 
489  for( int i=0; i<refsvel.size(); i++ ) {
490  if ( motorsv[i]->translate() ) {
491  spds[i] = refsvel[i];
492  } else {
493  spds[i] = toDegree(refsvel[i]);
494  }
495  }
496  return true;
497 }
498 
500  if ( j < 0 && j >= motorsv.size() ) {
501  return false;
502  }
503  if ( motorsv[j]->translate() ) {
504  *acc = refsacc[j];
505  } else {
506  *acc = toDegree(refsacc[j]);
507  }
508  return true;
509 }
510 
512  for( int i=0; i<refsvel.size(); i++ ) {
513  if ( motorsv[i]->translate() ) {
514  accs[i] = refsacc[i];
515  } else {
516  accs[i] = toDegree(refsacc[i]);
517  }
518  }
519  return true;
520 }
521 
523  if ( j < 0 && j >= motorsv.size() ) {
524  return false;
525  }
526  refs[j] = fromRawPosition(j);
527  stops[j] = true;
528  desiredVel[j] = 0;
529  motorsv[j]->setVelocity(0.0);
530  controlModes[j] = 1;
531  vel_pids[j]->reset();
532  return true;
533 }
534 
536  for( int i=0; i<motorsv.size(); i++ ) {
537  refs[i] = fromRawPosition(i);
538  stops[i] = true;
539  desiredVel[i] = 0;
540  motorsv[i]->setVelocity(0.0);
541  controlModes[i] = 1;
542  vel_pids[i]->reset();
543  }
544  return true;
545 }
546 
548  //controlMode = 2;
549  return true;
550 }
551 
552 bool MultiMotorController::velocityMove(int j, double sp) {
553  if ( j < 0 && j >= motorsv.size() ) {
554  return false;
555  }
556  controlModes[j] = 2;
557  desiredVel[j] = toRad(sp);
558  return true;
559 }
560 
561 bool MultiMotorController::velocityMove(const double *sp) {
562  for( int i=0; i<desiredVel.size(); i++ ) {
563  desiredVel[i] = toRad(sp[i]);
564  controlModes[i] = 2;
565  }
566  return true;
567 }
568 
570  if ( j < 0 && j >= motorsv.size() ) {
571  return false;
572  }
573  //--- set the position to actual position and stop the joint
574  refs[j] = fromRawPosition(j);
575  stops[j] = true;
576  controlModes[j] = 1;
577  return true;
578 }
579 
581  if ( j < 0 && j >= motorsv.size() ) {
582  return false;
583  }
584  //--- set the desired velocity to zero and stop the joint
585  desiredVel[j] = 0;
586  stops[j] = true;
587  controlModes[j] = 2;
588  return true;
589 }
590 
592  if ( j < 0 && j >= motorsv.size() ) {
593  return false;
594  }
595  //--- torque mode not implemented yet
596  return false;
597 }
598 
599 bool MultiMotorController::getControlMode( int j, int* mode ) {
600  if ( j < 0 && j >= motorsv.size() ) {
601  return false;
602  }
603  *mode = controlModes[j];
604  return true;
605 }
606 
608  for( int i=0; i<motorsv.size(); i++ ) {
609  modes[i] = controlModes[i];
610  }
611  return true;
612 }
613 
615  return false;
616 }
617 
619  return true;
620 }
621 
623  return true;
624 }
625 
626 bool MultiMotorController::setEncoder( int, double ) {
627  return true;
628 }
629 
630 bool MultiMotorController::setEncoders( const double* ) {
631  return true;
632 }
633 
634 bool MultiMotorController::getEncoder( int j, double *v ) {
635  if ( j < 0 && j >= motorsv.size() ) {
636  return false;
637  }
638  if ( motorsv[j]->translate() ) {
639  (*v) = fromRawPosition(j);
640  } else {
641  (*v) = toDegree( fromRawPosition(j) );
642  }
643  return true;
644 }
645 
646 bool MultiMotorController::getEncoders( double *encs ) {
647  for( int i=0; i<motorsv.size(); i++ ) {
648  if ( motorsv[i]->translate() ) {
649  encs[i] = fromRawPosition(i);
650  } else {
651  encs[i] = toDegree( fromRawPosition(i) );
652  }
653  }
654  return true;
655 }
656 
657 bool MultiMotorController::getEncoderSpeed( int j, double *sp ) {
658  if ( j < 0 && j >= motorsv.size() ) {
659  return false;
660  }
661  if ( motorsv[j]->translate() ) {
662  (*sp) = motorsv[j]->velocity();
663  } else {
664  (*sp) = toDegree(motorsv[j]->velocity());
665  }
666  return true;
667 }
668 
670  for( int i=0; i<motorsv.size(); i++ ) {
671  if ( motorsv[i]->translate() ) {
672  spds[i] = motorsv[i]->velocity();
673  } else {
674  spds[i] = toDegree(motorsv[i]->velocity());
675  }
676  }
677  return true;
678 }
679 
681  return false;
682 }
683 
685  return false;
686 }
687 
688 bool MultiMotorController::setLimits( int axis, double min, double max ) {
689  if ( axis < 0 && axis >= motorsv.size() ) {
690  return false;
691  }
692  if ( motorsv[axis]->translate() ) {
693  loLimits[axis] = min;
694  hiLimits[axis] = max;
695  } else {
696  loLimits[axis] = toRad( min );
697  hiLimits[axis] = toRad( max );
698  }
699  return true;
700 }
701 
702 bool MultiMotorController::getLimits( int axis, double *min, double *max ) {
703  if ( axis < 0 && axis >= motorsv.size() ) {
704  return false;
705  }
706  if ( motorsv[axis]->translate() ) {
707  *min = loLimits[axis];
708  *max = hiLimits[axis];
709  } else {
710  *min = toDegree( loLimits[axis] );
711  *max = toDegree( hiLimits[axis] );
712  }
713  return true;
714 }
715 
716 bool MultiMotorController::setLimitsRaw( int axis, double min, double max ) {
717  if ( axis < 0 && axis >= motorsv.size() ) {
718  return false;
719  }
720  if ( motorsv[axis]->translate() ) {
721  motorsv[axis]->setLimits( min, max );
722  } else {
723  motorsv[axis]->setLimits( toRad(min), toRad(max) );
724  }
725  return true;
726 }
727 
728 bool MultiMotorController::getLimitsRaw( int axis, double *min, double *max ) {
729  if ( axis < 0 && axis >= motorsv.size() ) {
730  return false;
731  }
732  real fmin, fmax;
733  motorsv[axis]->limits( fmin, fmax );
734  if ( motorsv[axis]->translate() ) {
735  *min = fmin;
736  *max = fmax;
737  } else {
738  *min = toDegree( fmin );
739  *max = toDegree( fmax );
740  }
741  return true;
742 }
743 
744 bool MultiMotorController::setEnableLimitsRaw( int axis, bool enable ) {
745  if ( axis < 0 && axis >= motorsv.size() ) {
746  return false;
747  }
748  if ( enable ) {
749  motorsv[axis]->enableLimits();
750  } else {
751  motorsv[axis]->disableLimits();
752  }
753  return true;
754 }
755 
756 real MultiMotorController::fromRawPosition( int i ) {
757  real rmin, rmax;
758  motorsv[i]->limits( rmin, rmax );
759  return linearMap( motorsv[i]->position(), rmin, rmax, loLimits[i], hiLimits[i] );
760 }
761 
762 real MultiMotorController::toRawPosition( real norawpos, int i ) {
763  real rmin, rmax;
764  motorsv[i]->limits( rmin, rmax );
765  return linearMap( norawpos, loLimits[i], hiLimits[i], rmin, rmax );
766 }
767 
768 void wheeledRobotsComputeKinematicMovement(wMatrix &mtr, real leftWheelVelocity, real rightWheelVelocity, real wheelr, real axletrack, real timestep)
769 {
770  const wMatrix origMtr = mtr;
771 
772  // To compute the actual movement of the robot I assume the movement is on the plane on which
773  // the two wheels lie. This means that I assume that both wheels are in contact with a plane
774  // at every time. Then I compute the instant centre of rotation and move the robot rotating
775  // it around the axis passing through the instant center of rotation and perpendicular to the
776  // plane on which the wheels lie (i.e. around the local XY plane)
777 
778  // First of all computing the linear speed of the two wheels
779  const real leftSpeed = leftWheelVelocity * wheelr;
780  const real rightSpeed = rightWheelVelocity * wheelr;
781 
782  // If the speed of the two wheels is very similar, simply moving the robot forward (doing the
783  // computations would probably lead to invalid matrices)
784  if (fabs(leftSpeed - rightSpeed) < 0.00001f) {
785  // The front of the robot is towards -y
786  mtr.w_pos += -mtr.y_ax.scale(rightSpeed * timestep);
787  } else {
788  // The first thing to do is to compute the instant centre of rotation. We do the
789  // calculation in the robot local frame of reference. We proceed as follows (with
790  // uppercase letters we indicate vectors and points):
791  //
792  // +------------------------->
793  // | X axis
794  // | ^
795  // | |\
796  // | | \A
797  // | | \
798  // | L| ^
799  // | | |\
800  // | | | \
801  // | | R| \
802  // | | | \
803  // | O+--->----+C
804  // | D
805  // |
806  // |
807  // v Y axis (the robot moves forward towards -Y)
808  //
809  // In the picture L and R are the velocity vectors of the two wheels, D is the vector
810  // going from the center of the left wheel to the center of the right wheel, A is the
811  // vector going from the tip of L to the tip of R and C is the instant centre of
812  // rotation. D is fixed an parallel to the X axis, while L and R are always parallel
813  // to the Y axis. Also, for the moment, we take the frame of reference origin at O.
814  // The construction shown in the picture allows to find the instant center of rotation
815  // for any L and R except when they are equal. In this case C is "at infinite" and the
816  // robot moves forward of backward without rotation. All the vectors lie on the local
817  // XY plane. To find C we proceed in the following way. First of all we note that
818  // A = R - L. If a and b are two real numbers, then we can say that C is at the
819  // instersection of L + aA with bD, that is we need a and b such that:
820  // L + aA = bD.
821  // If we take the cross product on both sides we get:
822  // (L + aA) x D = bD x D => (L + aA) x D = 0
823  // This means that we need to find a such that L + aA and D are parallel. As D is parallel
824  // to the X axis, its Y component is 0, so we can impose that L + aA has a 0 Y component,
825  // too, that is:
826  // (Ly) + a(Ay) = 0 => a = -(Ly) / (Ay)
827  // Once we have a, we can easily compute C:
828  // C = L + aA
829  // In the following code we use the same names we have used in the description above. Also
830  // note that the actual origin of the frame of reference is not in O: it is in the middle
831  // between the left and right wheel (we need to take this into account when computing the
832  // point around which the robot rotates)
833  const wVector L(0.0, -leftSpeed, 0.0);
834  const wVector A(axletrack, -(rightSpeed - leftSpeed), 0.0);
835  const real a = -(L.y / A.y);
836  const wVector C = L + A.scale(a);
837 
838  // Now we have to compute the angular velocity of the robot. This is simply equal to v/r where
839  // v is the linear velocity at a point that is distant r from C. We use the velocity of one of
840  // the wheel: which weels depends on its distance from C. We take the wheel which is furthest from
841  // C to avoid having r = 0 (which would lead to an invalid value of the angular velocity). Distances
842  // are signed (they are taken along the X axis)
843  const real distLeftWheel = -C.x;
844  const real distRightWheel = -(C.x - A.x); // A.x is equal to D.x
845  const real angularVelocity = (fabs(distLeftWheel) > fabs(distRightWheel)) ? (-leftSpeed / distLeftWheel) : (-rightSpeed / distRightWheel);
846 
847  // The angular displacement is simply the angular velocity multiplied by the timeStep. Here we
848  // also compute the center of rotation in world coordinates (we also need to compute it in the frame
849  // of reference centered between the wheels, not in O) and the axis of rotation (that is simply the
850  // z axis of the robot in world coordinates)
851  const real angularDisplacement = angularVelocity * timestep;
852  const wVector centerOfRotation = origMtr.transformVector(C - wVector(axletrack / 2.0, 0.0, 0.0));
853  const wVector axisOfRotation = origMtr.z_ax;
854 
855  // Rotating the robot transformation matrix to obtain the new position
856  mtr = mtr.rotateAround(axisOfRotation, centerOfRotation, angularDisplacement);
857  }
858 }
859 
860 WheelMotorController::WheelMotorController( QVector<PhyDOF*> wheels, World* world ) :
861  MotorController(world),
862  motorsv(wheels) {
863  desiredVel.fill( 0.0, motorsv.size() );
864  minVel.fill( -1.0, motorsv.size() );
865  maxVel.fill( 1.0, motorsv.size() );
866 }
867 
869  /* nothing to do */
870 }
871 
873  for( int i=0; i<motorsv.size(); i++ ) {
874  motorsv[i]->setDesiredVelocity( desiredVel[i] );
875  }
876 }
877 
878 void WheelMotorController::setSpeeds( QVector<double> speeds ) {
879  for( int i=0; i<qMin(speeds.size(), desiredVel.size()); i++ ) {
880  if ( speeds[i] < minVel[i] ) {
881  desiredVel[i] = minVel[i];
882  } else if ( speeds[i] > maxVel[i] ) {
883  desiredVel[i] = maxVel[i];
884  } else {
885  desiredVel[i] = speeds[i];
886  }
887  }
888 }
889 
890 void WheelMotorController::setSpeeds( double sp1, double sp2 ) {
891  if ( sp1 < minVel[0] ) {
892  desiredVel[0] = minVel[0];
893  } else if ( sp1 > maxVel[0] ) {
894  desiredVel[0] = maxVel[0];
895  } else {
896  desiredVel[0] = sp1;
897  }
898 
899  if ( sp2 < minVel[1] ) {
900  desiredVel[1] = minVel[1];
901  } else if ( sp2 > maxVel[1] ) {
902  desiredVel[1] = maxVel[1];
903  } else {
904  desiredVel[1] = sp2;
905  }
906 }
907 
908 void WheelMotorController::getSpeeds( QVector<double>& speeds ) {
909  speeds.clear();
910  for( int i=0; i<motorsv.size(); i++ ) {
911  speeds.append(motorsv[i]->velocity());
912  }
913 }
914 
915 void WheelMotorController::getSpeeds( double& sp1, double& sp2 ) {
916  sp1 = motorsv[0]->velocity();
917  sp2 = motorsv[1]->velocity();
918 }
919 
920 void WheelMotorController::setSpeedLimits( QVector<double> minSpeeds, QVector<double> maxSpeeds ) {
921  minVel = minSpeeds;
922  maxVel = maxSpeeds;
923 }
924 
925 void WheelMotorController::setSpeedLimits( double minSpeed1, double minSpeed2, double maxSpeed1, double maxSpeed2 ) {
926  minVel[0] = minSpeed1;
927  minVel[1] = minSpeed2;
928  maxVel[0] = maxSpeed1;
929  maxVel[1] = maxSpeed2;
930 }
931 
932 void WheelMotorController::getSpeedLimits( QVector<double>& minSpeeds, QVector<double>& maxSpeeds ) {
933  minSpeeds = minVel;
934  maxSpeeds = maxVel;
935 }
936 
937 void WheelMotorController::getSpeedLimits( double& minSpeed1, double& minSpeed2, double& maxSpeed1, double& maxSpeed2 ) {
938  minSpeed1 = minVel[0];
939  minSpeed2 = minVel[1];
940  maxSpeed1 = maxVel[0];
941  maxSpeed2 = maxVel[1];
942 }
943 
945  MotorController(robot->world()),
946  m_robot(robot),
947  m_status(Open),
948  m_desiredStatus(Open),
949  m_joint(NULL),
950  m_attachedRobot(NULL),
951  m_otherAttachedRobots()
952 {
953 }
954 
956 {
957  // Destroying the joint
958  delete m_joint;
959 }
960 
962 {
963  // If the desired status is equal to the current status, doing nothing
964  if (m_desiredStatus == m_status) {
965  return;
966  }
967 
968  // Updating the status
969  const Status previousStatus = m_status;
970  m_status = m_desiredStatus;
971  switch (m_status) {
972  case Open:
973  {
974  // Here we simply have to detach
975  delete m_joint;
976  m_joint = NULL;
977 
978  // Telling the other robot we are no longer attached
979  if (m_attachedRobot != NULL) {
980  int i = m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.indexOf(m_robot);
981  if (i != -1) {
982  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.remove(i);
983  }
984 
985  m_attachedRobot = NULL;
986  }
987  }
988  break;
989  case HalfClosed:
990  {
991  if (previousStatus == Open) {
992  // Trying to attach
993  m_attachedRobot = tryToAttach();
994  } else {
995  // If I was attached, the status was Closed for sure, so we have to change the kind
996  // of joint
997  if (m_attachedRobot != NULL) {
998  delete m_joint;
999  }
1000  }
1001 
1002  if (m_attachedRobot != NULL) {
1003  // Found a robot to which we can attach, creating the joint (a hinge). The parent
1004  // here is the other robot because this way it is easier to specify the axis and
1005  // center
1006  m_joint = new PhyHinge(wVector(1.0, 0.0, 0.0), wVector(0.0, 0.0, 0.0), 0.0, m_attachedRobot->turret(), m_robot->attachmentDevice());
1007  m_joint->dofs()[0]->disableLimits();
1008  m_joint->dofs()[0]->switchOff(); // This way the joint rotates freely
1009 
1010  // Telling the other robot we are now attached (if we weren't already)
1011  if (previousStatus == Open) {
1012  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.append(m_robot);
1013  }
1014  }
1015  }
1016  break;
1017  case Closed:
1018  {
1019  if (previousStatus == Open) {
1020  // Trying to attach
1021  m_attachedRobot = tryToAttach();
1022  } else {
1023  // If I was attached, the status was HalfClosed for sure, so we have to change the kind
1024  // of joint
1025  if (m_attachedRobot != NULL) {
1026  delete m_joint;
1027  }
1028  }
1029 
1030  if (m_attachedRobot != NULL) {
1031  // Found a robot to which we can attach, creating the joint (a fixed joint)
1032  m_joint = new PhyFixed(m_robot->attachmentDevice(), m_attachedRobot->turret());
1033 
1034  // Telling the other robot we are now attached (if we weren't already)
1035  if (previousStatus == Open) {
1036  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.append(m_robot);
1037  }
1038  }
1039  }
1040  break;
1041  }
1042 }
1043 
1045 {
1046  return m_robot->attachmentDeviceEnabled();
1047 }
1048 
1050 {
1051  if (attachmentDeviceEnabled()) {
1052  m_robot->attachmentDeviceJoint()->dofs()[0]->setMaxVelocity(speed);
1053  }
1054 }
1055 
1057 {
1058  if (attachmentDeviceEnabled()) {
1059  return m_robot->attachmentDeviceJoint()->dofs()[0]->maxVelocity();
1060  } else {
1061  return 0.0;
1062  }
1063 }
1064 
1066 {
1067  if (attachmentDeviceEnabled() && (!attachedToRobot())) {
1068  m_robot->attachmentDeviceJoint()->dofs()[0]->setDesiredPosition(pos);
1069  }
1070 }
1071 
1073 {
1074  if (attachmentDeviceEnabled()) {
1075  return m_robot->attachmentDeviceJoint()->dofs()[0]->desiredPosition();
1076  } else {
1077  return 0.0;
1078  }
1079 }
1080 
1082 {
1083  if (attachmentDeviceEnabled()) {
1084  return m_robot->attachmentDeviceJoint()->dofs()[0]->position();
1085  } else {
1086  return 0.0;
1087  }
1088 }
1089 
1091 {
1092  if (attachmentDeviceEnabled() && (!attachedToRobot())) {
1093  m_robot->attachmentDeviceJoint()->dofs()[0]->setDesiredVelocity(vel);
1094  }
1095 }
1096 
1098 {
1099  if (attachmentDeviceEnabled()) {
1100  return m_robot->attachmentDeviceJoint()->dofs()[0]->desiredVelocity();
1101  } else {
1102  return 0.0;
1103  }
1104 }
1105 
1107 {
1108  if (attachmentDeviceEnabled()) {
1109  return m_robot->attachmentDeviceJoint()->dofs()[0]->velocity();
1110  } else {
1111  return 0.0;
1112  }
1113 }
1114 
1116 {
1117  if (attachmentDeviceEnabled()) {
1118  m_desiredStatus = status;
1119  }
1120 }
1121 
1122 PhyMarXbot* MarXbotAttachmentDeviceMotorController::tryToAttach() const
1123 {
1124  // Checking the collisions of the attachment device
1125  const contactVec contacts = m_robot->world()->contacts()[m_robot->attachmentDevice()];
1126 
1127  // Now for each contact checking whether it is a turret of another PhyMarXbot and not
1128  // the attachment device. We create two sets: one contains robots whose attachment
1129  // device collides with our attachment device (we surely won't attach to these robots),
1130  // the other the robots whose turret collides with our attachment device (we could attach
1131  // to these robots)
1132  QSet<PhyMarXbot*> discardedRobots;
1133  QSet<PhyMarXbot*> candidateRobots;
1134  foreach (const Contact& c, contacts) {
1135  PhyMarXbot* otherRobot = dynamic_cast<PhyMarXbot*>(c.collide->owner());
1136  if ((otherRobot != NULL) && (otherRobot->attachmentDeviceEnabled()) && (!discardedRobots.contains(otherRobot))) {
1137  // Checking that the contact is the turret and not the attachment device
1138  if (c.collide == otherRobot->turret()) {
1139  // We have a candidate robot for attachment!
1140  candidateRobots.insert(otherRobot);
1141  } else if (c.collide == otherRobot->attachmentDevice()) {
1142  // We have to discard this robot
1143  discardedRobots.insert(otherRobot);
1144  candidateRobots.remove(otherRobot);
1145  }
1146  }
1147  }
1148 
1149  // Now we have a set of candidates. In practice we should always have at most one candidate due to
1150  // physical constraints. In case we have more than one, we simply return the first. If we have none,
1151  // we return NULL
1152  if (candidateRobots.isEmpty()) {
1153  return NULL;
1154  } else {
1155  return *(candidateRobots.begin());
1156  }
1157 }
1158 
1159 void MarXbotAttachmentDeviceMotorController::attachmentDeviceAboutToBeDestroyed()
1160 {
1161  // Deleting the joint
1162  delete m_joint;
1163  m_status = Open;
1164  m_desiredStatus = Open;
1165  m_joint = NULL;
1166 
1167  // Telling the other robot we are no longer attached
1168  if (m_attachedRobot != NULL) {
1169  int i = m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.indexOf(m_robot);
1170  if (i != -1) {
1171  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.remove(i);
1172  }
1173  m_attachedRobot = NULL;
1174  }
1175 
1176  // Also detachting robots attached to us
1177  foreach (PhyMarXbot* other, m_otherAttachedRobots) {
1178  // Destroying their joint and setting the robot to which they are attached to NULL
1179  delete other->attachmentDeviceController()->m_joint;
1180  other->attachmentDeviceController()->m_joint = NULL;
1181  other->attachmentDeviceController()->m_attachedRobot = NULL;
1182  }
1183 }
1184 
1185 } // end namespace farsa