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
PhyHinge * attachmentDeviceJoint()
Returns the joint of the attachment device.
Definition: phymarxbot.h:232
void reset()
Resets the PID status.
double getMaxVelocity() const
Returns the maximum speed at which the attachment device can move.
MarXbotAttachmentDeviceMotorController * attachmentDeviceController()
returns the motor controller for the attachment device
Definition: phymarxbot.h:109
The MarXbot robot.
Definition: phymarxbot.h:55
PhyObject * collide
Collide Object.
Definition: world.h:167
MarXbotAttachmentDeviceMotorController(PhyMarXbot *robot)
Constructor.
World * world()
Return the world.
Definition: wobject.cpp:61
void setDesiredPosition(double pos)
Sets the desired position of the attachment device.
void setDesiredVelocity(double vel)
Sets the desired velocity of the attachment device.
void setSpeeds(QVector< double > speeds)
sets the desired speed for the wheels
double fri_ff
'F' friction feed forward
Status
The possible status of the attachment device. See class description for more information.
double p_gain
'P' proportional gain
PhyFixed class.
Definition: phyfixed.h:43
MotorController Class.
double pidloop(double currentvalue)
it compute a loop of PID algorithm
FARSA_UTIL_TEMPLATE const T max(const T &t1, const U &t2)
void setMaxTorque(double maxTorque)
sets the maximum allowed torque of wheels
World class.
Definition: world.h:223
Contact class.
Definition: world.h:162
double getDesiredVelocity() const
Returns the desired velocity of the attachment device.
virtual QVector< PhyDOF * > dofs()
Return descriptions of DOF.
Definition: phyjoint.h:391
void setSpeedLimits(QVector< double > minSpeeds, QVector< double > maxSpeeds)
sets the minimum and maximum velocities for each wheel
double getMaxTorque() const
gets the maximum allowed torque of wheels
double max
'M' maximum output value
wMatrix class
Definition: wmatrix.h:48
bool attachmentDeviceEnabled() const
Returns true if the attachment device is enabled.
Definition: phymarxbot.h:178
double acc_ff
'A' acceleration feed forward
void getSpeeds(QVector< double > &speeds) const
gets the current speed of the wheels
PhyObject * turret()
Returns the solid modelling the turret of the robot.
Definition: phymarxbot.h:209
void getSpeedLimits(QVector< double > &minSpeeds, QVector< double > &maxSpeeds) const
gets the minimum and maximum velocities for each wheel
World * world()
return the world
double getDesiredPosition() const
Returns the desired position of the attachment device.
void getDesiredSpeeds(QVector< double > &speeds) const
gets the desired speed of the wheels
bool attachmentDeviceEnabled() const
Returns true if the attachment device is enabled.
virtual ~WheelMotorController()
Destructor.
double getPosition() const
Returns the current position of the attachment device.
wMatrix rotateAround(const wVector &axis, const wVector &centre, real angle) const
Rotate this matrix around the axis, and position passed of radians specified.
Definition: wmatrix.h:301
wVectorT< false > scale(real s) const
return a new wVectorT with element scaled by s
Definition: wvector.h:305
const contactMap & contacts()
return the contact map
Definition: world.cpp:522
void setDesiredStatus(Status status)
Changes the status of the attachment device.
MotorController(World *world)
constructor
double accel_r
'R' acceleration rate
float real
wPID()
Constructor.
virtual void update()
apply the velocities on the wheels
double vel_ff
'V' velocity feed forward
double min
'N' minimum output value
double d_gain
'D' derivative gain
FARSA_UTIL_TEMPLATE const T min(const T &t1, const U &t2)
bool attachedToRobot() const
Returns true if we are attached to another robot.
double slew
'W' maximum slew rate
Ownable * owner() const
Returns the owner of this object.
Definition: ownable.h:115
PhyObject * attachmentDevice()
Returns the solid modelling the attachment device.
Definition: phymarxbot.h:221
wVector transformVector(const wVector &v) const
apply both rotation and translation to the vector v
Definition: wmatrix.h:364
PhyDOF class.
Definition: phyjoint.h:50
virtual void update()
The method updating the attachment device at each timestep.
Dedicated controller for wheeled robots.
virtual ~MotorController()
Destructor.
double i_gain
'I' integral gain
void setMaxVelocity(double speed)
Sets the maximum speed at which the attachment device can move.
double bias
'B' bias
friend class PhyMarXbot
The PhyMarXbot class is friend to call the attachmentDeviceAboutToBeDestroyed() function.
double setpt
'S' set point
double getVelocity() const
Returns the current velocity of the attachment device.
PhyHinge class.
Definition: phyhinge.h:43