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 "mathutils.h"
27 #include <cmath>
28 #include <QSet>
29 
30 namespace farsa {
31 
33  p_gain = 0;
34  i_gain = 0;
35  d_gain = 0;
36  acc_ff = 0;
37  fri_ff = 0;
38  vel_ff = 0;
39  bias = 0;
40  accel_r = 0;
41  setpt = 0;
42  min = 0;
43  max = 0;
44  slew = 0;
45  this_target = 0;
46  next_target = 0;
47  integral = 0;
48  last_error = 0;
49  last_output = 0;
50 }
51 
52 double wPID::pidloop( double PV ) {
53  double this_error;
54  double this_output;
55  double accel;
56  double deriv;
57  double friction;
58  /* the desired PV for this iteration is the value */
59  /* calculated as next_target during the last loop */
60  this_target = next_target;
61  /* test for acceleration, compute new target PV for */
62  /* the next pass through the loop */
63  if ( accel_r > 0 && this_target != setpt) {
64  if ( this_target < setpt ) {
65  next_target += accel_r;
66  if ( next_target > setpt ) {
67  next_target = setpt;
68  }
69  } else { /* this_target > setpoint */
70  next_target -= accel_r;
71  if ( next_target < setpt ) {
72  next_target = setpt;
73  }
74  }
75  } else {
76  next_target = setpt;
77  }
78  /* acceleration is the difference between the PV */
79  /* target on this pass and the next pass through the */
80  /* loop */
81  accel = next_target - this_target;
82  /* the error for the current pass is the difference */
83  /* between the current target and the current PV */
84  this_error = this_target - PV;
85  /* the derivative is the difference between the error */
86  /* for the current pass and the previous pass */
87  deriv = this_error - last_error;
88  /* a very simple determination of whether there is */
89  /* special friction to be overcome on the next pass, */
90  /* if the current PV is 0 and the target for the next */
91  /* pass is not 0, stiction could be a problem */
92  friction = ( PV == 0.0 && next_target != 0.0 );
93  /* the new error is added to the integral */
94  integral += this_target - PV;
95  /* now that all of the variable terms have been */
96  /* computed they can be multiplied by the appropriate */
97  /* coefficients and the resulting products summed */
98  this_output = p_gain * this_error
99  + i_gain * integral
100  + d_gain * deriv
101  + acc_ff * accel
102  + vel_ff * next_target
103  + fri_ff * friction
104  + bias;
105  last_error = this_error;
106  /* check for slew rate limiting on the output change */
107  if ( 0 != slew ) {
108  if ( this_output - last_output > slew ) {
109  this_output = last_output + slew;
110  } else if ( last_output - this_output > slew ) {
111  this_output = last_output - slew;
112  }
113  }
114  /* now check the output value for absolute limits */
115  if ( this_output < min ) {
116  this_output = min;
117  } else if ( this_output > max ) {
118  this_output = max;
119  }
120  /* store the new output value to be used as the old */
121  /* output value on the next loop pass */
122  return last_output = this_output;
123 }
124 
125 void wPID::reset() {
126  // Resetting status variables
127  this_target = 0;
128  next_target = 0;
129  integral = 0;
130  last_error = 0;
131  last_output = 0;
132 }
133 
135  enabled = true;
136  worldv = world;
137 }
138 
140 }
141 
142 void wheeledRobotsComputeKinematicMovement(wMatrix &mtr, real leftWheelVelocity, real rightWheelVelocity, real wheelr, real axletrack, real timestep)
143 {
144  const wMatrix origMtr = mtr;
145 
146  // To compute the actual movement of the robot I assume the movement is on the plane on which
147  // the two wheels lie. This means that I assume that both wheels are in contact with a plane
148  // at every time. Then I compute the instant centre of rotation and move the robot rotating
149  // it around the axis passing through the instant center of rotation and perpendicular to the
150  // plane on which the wheels lie (i.e. around the local XY plane)
151 
152  // First of all computing the linear speed of the two wheels
153  const real leftSpeed = leftWheelVelocity * wheelr;
154  const real rightSpeed = rightWheelVelocity * wheelr;
155 
156  // If the speed of the two wheels is very similar, simply moving the robot forward (doing the
157  // computations would probably lead to invalid matrices)
158  if (fabs(leftSpeed - rightSpeed) < 0.00001f) {
159  // The front of the robot is towards -y
160  mtr.w_pos += -mtr.y_ax.scale(rightSpeed * timestep);
161  } else {
162  // The first thing to do is to compute the instant centre of rotation. We do the
163  // calculation in the robot local frame of reference. We proceed as follows (with
164  // uppercase letters we indicate vectors and points):
165  //
166  // -------------------+------>
167  // | X axis
168  // ^ |
169  // |\ |
170  // | \A |
171  // | \ |
172  // L| ^ |
173  // | |\ |
174  // | | \ |
175  // | R| \ |
176  // | | \ |
177  // O+--->----+C |
178  // D |
179  // |
180  // |
181  // v Y axis (the robot moves forward towards -Y)
182  //
183  // In the picture L and R are the velocity vectors of the two wheels, D is the vector
184  // going from the center of the left wheel to the center of the right wheel, A is the
185  // vector going from the tip of L to the tip of R and C is the instant centre of
186  // rotation. D is fixed an parallel to the X axis, while L and R are always parallel
187  // to the Y axis. Also, for the moment, we take the frame of reference origin at O.
188  // The construction shown in the picture allows to find the instant center of rotation
189  // for any L and R except when they are equal. In this case C is "at infinite" and the
190  // robot moves forward of backward without rotation. All the vectors lie on the local
191  // XY plane. To find C we proceed in the following way. First of all we note that
192  // A = R - L. If a and b are two real numbers, then we can say that C is at the
193  // instersection of L + aA with bD, that is we need a and b such that:
194  // L + aA = bD.
195  // If we take the cross product on both sides we get:
196  // (L + aA) x D = bD x D => (L + aA) x D = 0
197  // This means that we need to find a such that L + aA and D are parallel. As D is parallel
198  // to the X axis, its Y component is 0, so we can impose that L + aA has a 0 Y component,
199  // too, that is:
200  // (Ly) + a(Ay) = 0 => a = -(Ly) / (Ay)
201  // Once we have a, we can easily compute C:
202  // C = L + aA
203  // In the following code we use the same names we have used in the description above. Also
204  // note that the actual origin of the frame of reference is not in O: it is in the middle
205  // between the left and right wheel (we need to take this into account when computing the
206  // point around which the robot rotates)
207  const wVector L(0.0, -leftSpeed, 0.0);
208  const wVector A(axletrack, -(rightSpeed - leftSpeed), 0.0);
209  const real a = -(L.y / A.y);
210  const wVector C = L + A.scale(a);
211 
212  // Now we have to compute the angular velocity of the robot. This is simply equal to v/r where
213  // v is the linear velocity at a point that is distant r from C. We use the velocity of one of
214  // the wheel: which weels depends on its distance from C. We take the wheel which is furthest from
215  // C to avoid having r = 0 (which would lead to an invalid value of the angular velocity). Distances
216  // are signed (they are taken along the X axis)
217  const real distLeftWheel = -C.x;
218  const real distRightWheel = -(C.x - A.x); // A.x is equal to D.x
219  const real angularVelocity = (fabs(distLeftWheel) > fabs(distRightWheel)) ? (-leftSpeed / distLeftWheel) : (-rightSpeed / distRightWheel);
220 
221  // The angular displacement is simply the angular velocity multiplied by the timeStep. Here we
222  // also compute the center of rotation in world coordinates (we also need to compute it in the frame
223  // of reference centered between the wheels, not in O) and the axis of rotation (that is simply the
224  // z axis of the robot in world coordinates)
225  const real angularDisplacement = angularVelocity * timestep;
226  const wVector centerOfRotation = origMtr.transformVector(C - wVector(axletrack / 2.0, 0.0, 0.0));
227  const wVector axisOfRotation = origMtr.z_ax;
228 
229  // Rotating the robot transformation matrix to obtain the new position
230  mtr = mtr.rotateAround(axisOfRotation, centerOfRotation, angularDisplacement);
231  }
232 }
233 
234 bool FARSA_WSIM_API wheeledRobotsComputeWheelsSpeed(const wMatrix& start, const wMatrix& end, real wheelr, real axletrack, real timestep, real& leftWheelVelocity, real& rightWheelVelocity)
235 {
236  bool ret = true;
237 
238  // What we need do to compute the speeds of the wheels is to compute the position of
239  // the instant rotation center. The hypothesis is that the wheels move at constant
240  // speed when bringing the robot from start to end. In this case the instant rotation
241  // center is constant and we can compute the distance travelled by each wheel (along an
242  // arc of a circumference). Once we have the distance it is easy to compute the speed.
243  // Under the hypothesis of constant wheel speed, the instant rotation center can be
244  // computed as the intersection of the local X axis at start and end (the wheels axis
245  // is along X). Moreover the distance from the intersection to the center of the robot
246  // must be the same at start and end. To compute the intersection we need to solve the
247  // following equation: Ps + k1*Xs = Pe + k2*Xe where Ps and Pe are the starting and ending
248  // positions of the robot, Xs and Xe are the local X axes at start and end and k1 and k2
249  // are two unknowns. If k1 == k2, the hypothesis of constant wheel speed holds, otherwise
250  // it doesn't. However, we must consider the side case of Xs and Xe parallel (see below)
251 
252  // Useful constants
253  const real epsilon = 0.0001f;
254  const wVector& Xs = start.x_ax; FARSA_DEBUG_TEST_INVALID(Xs.x) FARSA_DEBUG_TEST_INVALID(Xs.y) FARSA_DEBUG_TEST_INVALID(Xs.z)
255  const wVector& Xe = end.x_ax; FARSA_DEBUG_TEST_INVALID(Xe.x) FARSA_DEBUG_TEST_INVALID(Xe.y) FARSA_DEBUG_TEST_INVALID(Xe.z)
256  const real halfAxletrack = axletrack / 2.0f;
257  const wVector displacementVector = end.w_pos - start.w_pos; FARSA_DEBUG_TEST_INVALID(displacementVector.x) FARSA_DEBUG_TEST_INVALID(displacementVector.y) FARSA_DEBUG_TEST_INVALID(displacementVector.z)
258  const real displacement = displacementVector.norm(); FARSA_DEBUG_TEST_INVALID(displacement)
259  // If displacement is 0, direction will be invalid (the check for displacement != 0 is done below)
260  const wVector direction = displacementVector.scale(1.0f / displacement);
261 
262  // The space travelled by the left and right wheel (signed)
263  real leftWheelDistance = 0.0f;
264  real rightWheelDistance = 0.0f;
265 
266  // First of all checking if Xs and Xe are parallel. If they also have the same direction
267  // (Pe - Ps) should be along -Y (otherwise the hypothesis of constant wheel speed doesn't
268  // hold); if they have opposite directions (Pe - Ps) should be parallel to Xs and Xe,
269  // meaning that the robot has rotated of 180° (otherwise the hypothesis of constant wheel
270  // speed doesn't hold). In the latter case, however which wheel has positive velocity is not
271  // defined, here we always return the left wheel as having positive velocity
272  const farsa::real dotX = min(1.0f, max(-1.0f, Xs % Xe)); FARSA_DEBUG_TEST_INVALID(dotX)
273  if (fabs(1.0f - dotX) < epsilon) {
274  // If the displacement is 0, the robot hasn't moved at all
275  if (fabs(displacement) < epsilon) {
276  leftWheelDistance = 0.0f;
277  rightWheelDistance = 0.0f;
278  } else {
279  // Xs and Xe are parallel and have the same direction. Checking if Pe-Ps is along
280  // -Y or not (start.y_ax and end.y_ax are parallel because we just checked that
281  // Xs and Xe are parallel and the Z axes are parallel by hypothesis)
282  const farsa::real dotDirY = direction % (-start.y_ax); FARSA_DEBUG_TEST_INVALID(dotDirY)
283 
284  if (fabs(1.0f - dotDirY) < epsilon) {
285  // Positive speed
286  leftWheelDistance = displacement;
287  rightWheelDistance = displacement;
288  } else if (fabs(1.0f + dotDirY) < epsilon) {
289  // Negative speed
290  leftWheelDistance = -displacement;
291  rightWheelDistance = -displacement;
292  } else {
293  // Impossible movement, multiplying by dotDirY (i.e. the cos of the angle)
294  // to get the displacement along -Y
295  leftWheelDistance = dotDirY * displacement; FARSA_DEBUG_TEST_INVALID(leftWheelDistance)
296  rightWheelDistance = dotDirY * displacement; FARSA_DEBUG_TEST_INVALID(rightWheelDistance)
297  ret = false;
298  }
299  }
300  } else if (fabs(1.0f + dotX) < epsilon) {
301  // If the displacement is 0, the robot has rotated on the spot by 180°
302  if (fabs(displacement) < epsilon) {
303  leftWheelDistance = PI_GRECO * axletrack;
304  rightWheelDistance = -leftWheelDistance;
305  } else {
306  // Xs and Xe are parallel and have opposite directions. Checking if Pe-Ps is
307  // along X or not.
308  const farsa::real dotDirX = direction % Xs; FARSA_DEBUG_TEST_INVALID(dotDirX)
309 
310  const real distCenterOfRotationToCenterOfRobot = displacement / 2.0f; FARSA_DEBUG_TEST_INVALID(distCenterOfRotationToCenterOfRobot)
311  const real slowWheel = (distCenterOfRotationToCenterOfRobot - halfAxletrack) * PI_GRECO; FARSA_DEBUG_TEST_INVALID(slowWheel)
312  const real fastWheel = (distCenterOfRotationToCenterOfRobot + halfAxletrack) * PI_GRECO; FARSA_DEBUG_TEST_INVALID(fastWheel)
313 
314  if (fabs(1.0f - dotDirX) < epsilon) {
315  leftWheelDistance = slowWheel;
316  rightWheelDistance = fastWheel;
317  } else if (fabs(1.0f + dotDirX) < epsilon) {
318  leftWheelDistance = fastWheel;
319  rightWheelDistance = slowWheel;
320  } else {
321  // Impossible movement, multiplying by dotDirX (i.e. the cos of the angle)
322  // to get the displacement along X
323  if (dotDirX > 0.0f) {
324  leftWheelDistance = dotDirX * slowWheel; FARSA_DEBUG_TEST_INVALID(leftWheelDistance)
325  rightWheelDistance = dotDirX * fastWheel; FARSA_DEBUG_TEST_INVALID(rightWheelDistance)
326  } else {
327  leftWheelDistance = dotDirX * fastWheel; FARSA_DEBUG_TEST_INVALID(leftWheelDistance)
328  rightWheelDistance = dotDirX * slowWheel; FARSA_DEBUG_TEST_INVALID(rightWheelDistance)
329  }
330  ret = false;
331  }
332  }
333  } else {
334  // Xs and Xe are not parallel, computing k1 and k2
335  const real delta = (Xs.y * Xe.x) - (Xs.x * Xe.y);
336  const real invDelta = 1.0 / delta;
337  const real k1 = invDelta * (Xe.x * displacementVector.y - Xe.y * displacementVector.x);
338  const real k2 = invDelta * (Xs.x * displacementVector.y - Xs.y * displacementVector.x);
339 
340  // If k1 == k2, we have k == k1 == k2 and we can compute the correct velocities. If k1 != k2
341  // we use the average of k1 and k2 to compute velocities but return false. If k is positive the
342  // robot moved forward, if it is negative, it moved backward
343  const real k = (k1 + k2) / 2.0f;
344  if ((delta < epsilon) && (delta > -epsilon)) {
345  // We get here if the two vectors are parallel. Setting ret to false and wheels distance
346  // to displacement
347  ret = false;
348  leftWheelDistance = displacement;
349  rightWheelDistance = displacement;
350  } else {
351  if (fabs(k1 - k2) >= epsilon) {
352  ret = false;
353  }
354 
355  // First of all we need the angle between the two X axes (i.e. how much the robot has rotated).
356  // We need to know the sign of the rotation
357  const real rotationSign = (((Xs * Xe) % start.z_ax) < 0.0f) ? -1.0f : 1.0f; FARSA_DEBUG_TEST_INVALID(rotationSign)
358  const real angle = acos(dotX) * rotationSign; FARSA_DEBUG_TEST_INVALID(angle)
359 
360  // Now computing the distance of each wheel from the instant center of rotation. The distance
361  // is signed (positive along local X)
362  const real distLeftWheel = k + halfAxletrack; FARSA_DEBUG_TEST_INVALID(distLeftWheel)
363  const real distRightWheel = k - halfAxletrack; FARSA_DEBUG_TEST_INVALID(distRightWheel)
364 
365  // Now computing the arc travelled by each wheel (signed, positive for forward motion, negative
366  // for backward motion)
367  leftWheelDistance = distLeftWheel * angle; FARSA_DEBUG_TEST_INVALID(leftWheelDistance)
368  rightWheelDistance = distRightWheel * angle; FARSA_DEBUG_TEST_INVALID(rightWheelDistance)
369  }
370  }
371 
372  // Computing the wheel velocities
373  leftWheelVelocity = (leftWheelDistance / wheelr) / timestep; FARSA_DEBUG_TEST_INVALID(leftWheelVelocity)
374  rightWheelVelocity = (rightWheelDistance / wheelr) / timestep; FARSA_DEBUG_TEST_INVALID(rightWheelVelocity)
375 
376  return ret;
377 }
378 
380  MotorController(world),
381  motorsv(wheels) {
382  desiredVel.fill( 0.0, motorsv.size() );
383  minVel.fill( -1.0, motorsv.size() );
384  maxVel.fill( 1.0, motorsv.size() );
385 }
386 
388  /* nothing to do */
389 }
390 
392  for( int i=0; i<motorsv.size(); i++ ) {
393  motorsv[i]->setDesiredVelocity( desiredVel[i] );
394  }
395 }
396 
397 void WheelMotorController::setSpeeds( QVector<double> speeds ) {
398  for( int i=0; i<qMin(speeds.size(), desiredVel.size()); i++ ) {
399  if ( speeds[i] < minVel[i] ) {
400  desiredVel[i] = minVel[i];
401  } else if ( speeds[i] > maxVel[i] ) {
402  desiredVel[i] = maxVel[i];
403  } else {
404  desiredVel[i] = speeds[i];
405  }
406  }
407 }
408 
409 void WheelMotorController::setSpeeds( double sp1, double sp2 ) {
410  if ( sp1 < minVel[0] ) {
411  desiredVel[0] = minVel[0];
412  } else if ( sp1 > maxVel[0] ) {
413  desiredVel[0] = maxVel[0];
414  } else {
415  desiredVel[0] = sp1;
416  }
417 
418  if ( sp2 < minVel[1] ) {
419  desiredVel[1] = minVel[1];
420  } else if ( sp2 > maxVel[1] ) {
421  desiredVel[1] = maxVel[1];
422  } else {
423  desiredVel[1] = sp2;
424  }
425 }
426 
427 void WheelMotorController::getSpeeds( QVector<double>& speeds ) const {
428  speeds.clear();
429  for( int i=0; i<motorsv.size(); i++ ) {
430  speeds.append(motorsv[i]->velocity());
431  }
432 }
433 
434 void WheelMotorController::getSpeeds( double& sp1, double& sp2 ) const {
435  sp1 = motorsv[0]->velocity();
436  sp2 = motorsv[1]->velocity();
437 }
438 
439 void WheelMotorController::getDesiredSpeeds( QVector<double>& speeds ) const {
440  speeds = desiredVel;
441 }
442 
443 void WheelMotorController::getDesiredSpeeds( double& sp1, double& sp2 ) const {
444  sp1 = desiredVel[0];
445  sp2 = desiredVel[1];
446 }
447 
448 void WheelMotorController::setSpeedLimits( QVector<double> minSpeeds, QVector<double> maxSpeeds ) {
449  minVel = minSpeeds;
450  maxVel = maxSpeeds;
451 }
452 
453 void WheelMotorController::setSpeedLimits( double minSpeed1, double minSpeed2, double maxSpeed1, double maxSpeed2 ) {
454  minVel[0] = minSpeed1;
455  minVel[1] = minSpeed2;
456  maxVel[0] = maxSpeed1;
457  maxVel[1] = maxSpeed2;
458 }
459 
460 void WheelMotorController::getSpeedLimits( QVector<double>& minSpeeds, QVector<double>& maxSpeeds ) const {
461  minSpeeds = minVel;
462  maxSpeeds = maxVel;
463 }
464 
465 void WheelMotorController::getSpeedLimits( double& minSpeed1, double& minSpeed2, double& maxSpeed1, double& maxSpeed2 ) const {
466  minSpeed1 = minVel[0];
467  minSpeed2 = minVel[1];
468  maxSpeed1 = maxVel[0];
469  maxSpeed2 = maxVel[1];
470 }
471 
472 void WheelMotorController::setMaxTorque( double maxTorque ) {
473  for (int i = 0; i < motorsv.size(); ++i) {
474  motorsv[i]->setMaxForce( maxTorque );
475  }
476 }
477 
479  return motorsv[0]->maxForce();
480 }
481 
483  MotorController(robot->world()),
484  m_robot(robot),
485  m_status(Open),
486  m_desiredStatus(Open),
487  m_joint(NULL),
488  m_attachedRobot(NULL),
489  m_otherAttachedRobots()
490 {
491 }
492 
494 {
495  // Destroying the joint
496  delete m_joint;
497 }
498 
500 {
501  // If the desired status is equal to the current status, doing nothing
502  if (m_desiredStatus == m_status) {
503  return;
504  }
505 
506  // Updating the status
507  const Status previousStatus = m_status;
508  m_status = m_desiredStatus;
509  switch (m_status) {
510  case Open:
511  {
512  // Here we simply have to detach
513  delete m_joint;
514  m_joint = NULL;
515 
516  // Telling the other robot we are no longer attached
517  if (m_attachedRobot != NULL) {
518  int i = m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.indexOf(m_robot);
519  if (i != -1) {
520  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.remove(i);
521  }
522 
523  m_attachedRobot = NULL;
524  }
525  }
526  break;
527  case HalfClosed:
528  {
529  if (previousStatus == Open) {
530  // Trying to attach
531  m_attachedRobot = tryToAttach();
532  } else {
533  // If I was attached, the status was Closed for sure, so we have to change the kind
534  // of joint
535  if (m_attachedRobot != NULL) {
536  delete m_joint;
537  }
538  }
539 
540  if (m_attachedRobot != NULL) {
541  // Found a robot to which we can attach, creating the joint (a hinge). The parent
542  // here is the other robot because this way it is easier to specify the axis and
543  // center
544  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());
545  m_joint->dofs()[0]->disableLimits();
546  m_joint->dofs()[0]->switchOff(); // This way the joint rotates freely
547 
548  // Telling the other robot we are now attached (if we weren't already)
549  if (previousStatus == Open) {
550  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.append(m_robot);
551  }
552  }
553  }
554  break;
555  case Closed:
556  {
557  if (previousStatus == Open) {
558  // Trying to attach
559  m_attachedRobot = tryToAttach();
560  } else {
561  // If I was attached, the status was HalfClosed for sure, so we have to change the kind
562  // of joint
563  if (m_attachedRobot != NULL) {
564  delete m_joint;
565  }
566  }
567 
568  if (m_attachedRobot != NULL) {
569  // Found a robot to which we can attach, creating the joint (a fixed joint)
570  m_joint = new PhyFixed(m_robot->attachmentDevice(), m_attachedRobot->turret());
571 
572  // Telling the other robot we are now attached (if we weren't already)
573  if (previousStatus == Open) {
574  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.append(m_robot);
575  }
576  }
577  }
578  break;
579  }
580 }
581 
583 {
584  return m_robot->attachmentDeviceEnabled();
585 }
586 
588 {
589  if (attachmentDeviceEnabled()) {
590  m_robot->attachmentDeviceJoint()->dofs()[0]->setMaxVelocity(speed);
591  }
592 }
593 
595 {
596  if (attachmentDeviceEnabled()) {
597  return m_robot->attachmentDeviceJoint()->dofs()[0]->maxVelocity();
598  } else {
599  return 0.0;
600  }
601 }
602 
604 {
606  m_robot->attachmentDeviceJoint()->dofs()[0]->setDesiredPosition(pos);
607  }
608 }
609 
611 {
612  if (attachmentDeviceEnabled()) {
613  return m_robot->attachmentDeviceJoint()->dofs()[0]->desiredPosition();
614  } else {
615  return 0.0;
616  }
617 }
618 
620 {
621  if (attachmentDeviceEnabled()) {
622  return m_robot->attachmentDeviceJoint()->dofs()[0]->position();
623  } else {
624  return 0.0;
625  }
626 }
627 
629 {
631  m_robot->attachmentDeviceJoint()->dofs()[0]->setDesiredVelocity(vel);
632  }
633 }
634 
636 {
637  if (attachmentDeviceEnabled()) {
638  return m_robot->attachmentDeviceJoint()->dofs()[0]->desiredVelocity();
639  } else {
640  return 0.0;
641  }
642 }
643 
645 {
646  if (attachmentDeviceEnabled()) {
647  return m_robot->attachmentDeviceJoint()->dofs()[0]->velocity();
648  } else {
649  return 0.0;
650  }
651 }
652 
654 {
655  if (attachmentDeviceEnabled()) {
656  m_desiredStatus = status;
657  }
658 }
659 
660 PhyMarXbot* MarXbotAttachmentDeviceMotorController::tryToAttach() const
661 {
662  // Checking the collisions of the attachment device
663  const contactVec contacts = m_robot->world()->contacts()[m_robot->attachmentDevice()];
664 
665  // Now for each contact checking whether it is a turret of another PhyMarXbot and not
666  // the attachment device. We create two sets: one contains robots whose attachment
667  // device collides with our attachment device (we surely won't attach to these robots),
668  // the other the robots whose turret collides with our attachment device (we could attach
669  // to these robots)
670  QSet<PhyMarXbot*> discardedRobots;
671  QSet<PhyMarXbot*> candidateRobots;
672  foreach (const Contact& c, contacts) {
673  PhyMarXbot* otherRobot = dynamic_cast<PhyMarXbot*>(c.collide->owner());
674  if ((otherRobot != NULL) && (otherRobot->attachmentDeviceEnabled()) && (!discardedRobots.contains(otherRobot))) {
675  // Checking that the contact is the turret and not the attachment device
676  if (c.collide == otherRobot->turret()) {
677  // We have a candidate robot for attachment!
678  candidateRobots.insert(otherRobot);
679  } else if (c.collide == otherRobot->attachmentDevice()) {
680  // We have to discard this robot
681  discardedRobots.insert(otherRobot);
682  candidateRobots.remove(otherRobot);
683  }
684  }
685  }
686 
687  // Now we have a set of candidates. In practice we should always have at most one candidate due to
688  // physical constraints. In case we have more than one, we simply return the first. If we have none,
689  // we return NULL
690  if (candidateRobots.isEmpty()) {
691  return NULL;
692  } else {
693  return *(candidateRobots.begin());
694  }
695 }
696 
697 void MarXbotAttachmentDeviceMotorController::attachmentDeviceAboutToBeDestroyed()
698 {
699  // Deleting the joint
700  delete m_joint;
701  m_status = Open;
702  m_desiredStatus = Open;
703  m_joint = NULL;
704 
705  // Telling the other robot we are no longer attached
706  if (m_attachedRobot != NULL) {
707  int i = m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.indexOf(m_robot);
708  if (i != -1) {
709  m_attachedRobot->attachmentDeviceController()->m_otherAttachedRobots.remove(i);
710  }
711  m_attachedRobot = NULL;
712  }
713 
714  // Also detachting robots attached to us
715  foreach (PhyMarXbot* other, m_otherAttachedRobots) {
716  // Destroying their joint and setting the robot to which they are attached to NULL
717  delete other->attachmentDeviceController()->m_joint;
718  other->attachmentDeviceController()->m_joint = NULL;
719  other->attachmentDeviceController()->m_attachedRobot = NULL;
720  }
721 }
722 
723 } // end namespace farsa