00001
00002
00003
00004
00005
00006 #include "synchronization/semaphore.h"
00007 #include "Kinematics\DH_Link.h"
00008 #include "Kinematics\Jacobian.h"
00009 #include "assert.h"
00010 #include <math/matrix4x4.h>
00011 #include <math/Matrixmxn.h>
00012 #include <math/vector4.h>
00013 #include "CollisionDetectors\CD_Swiftpp.h"
00014 #include "IK_Jacobian.h"
00015
00016 #if 1
00017 #define LogMatrix(a, b, c)
00018 #define LogVector(a, b, c)
00019 #define LogMessage(a, b)
00020 #endif
00021
00022 static const int FRAMEDOF = 6;
00023 static const int SAMPLING_PERIOD = 1;
00024
00025 static const double INFINITY = LONG_MAX;
00026 static const double DEFAULT_OBS_TOL = 0.01;
00027
00028 static const double DEFAULT_PATH_TOL = 0.5;
00029
00030 static const double DEFAULT_ANG_TOL = 2;
00031
00032 static const double DEFAULT_VEL = 1;
00033
00034 static const double DEFAULT_OBS_GAIN = 0.1;
00035
00036 static const double DEFAULT_REL_IMP = 1;
00037
00038
00039 static const double MAX_OBS_VEL = 0.5;
00040 static const double MAX_JOINT_VEL = (M_PI/10.);
00041 static const double CALCULATION_FREQ = 100;
00042 static const double UNITY_GAIN_DISTANCE = 0.5;
00043 static const double SOI_GAIN_DISTANCE = 1.5;
00044 static const double DEFAULT_HOMO_GAIN = 0.25;
00045
00046
00047 IK_Jacobian::IK_Jacobian()
00048 {
00049 if (collisionDetector != NULL)
00050 {
00051 m_nDof = collisionDetector->DOF();
00052 }
00053 else
00054 {
00055 m_nDof = FRAMEDOF;
00056 }
00057
00058
00059 m_nObsPoint = 2;
00060 m_dObsTol = DEFAULT_OBS_TOL;
00061 m_dObsDistGain = DEFAULT_OBS_GAIN;
00062 m_dHomoGain = DEFAULT_HOMO_GAIN;
00063 m_dPathTol = DEFAULT_PATH_TOL;
00064 m_dAngTol = DEFAULT_ANG_TOL;
00065
00066 path.Clear();
00067 m_vClosestPoints.clear();
00068 }
00069
00070
00071 IK_Jacobian::~IK_Jacobian()
00072 {
00073 path.Clear();
00074 m_vClosestPoints.clear();
00075
00076 }
00077
00078
00079
00080
00081
00082
00083
00084 void IK_Jacobian::SetCollisionDetector(CD_BasicStyle* collisionDetector)
00085 {
00086 this->collisionDetector = collisionDetector;
00087 m_nDof = collisionDetector->DOF();
00088 m_pRobot = dynamic_cast<R_OpenChain*>(collisionDetector->GetRobot(0));
00089 m_nToolFrame = m_pRobot->GetToolFrame(0);
00090 m_frEndEffector = Matrix4x4::Identity();
00091 if (m_nToolFrame != -1)
00092 {
00093 FrameManager* frameManager = collisionDetector->GetFrameManager();
00094 m_frEndEffector = *(frameManager->GetFrameRef(m_nToolFrame));
00095 }
00096 }
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 VectorN IK_Jacobian::ComputeEndEffVel(Frame &frStart, Frame &frEnd)
00116 {
00117 VectorN returnMe;
00118
00119 if (m_bOrientation && m_bPosition)
00120 returnMe.SetLength(6);
00121 else if (m_bOrientation || m_bPosition)
00122 returnMe.SetLength(3);
00123 else
00124 {
00125 assert(false);
00126 return returnMe;
00127 }
00128
00129 int index = 0;
00130 if (m_bPosition)
00131 {
00132 returnMe[index++] = frEnd(0, 3) - frStart(0, 3);
00133 returnMe[index++] = frEnd(1, 3) - frStart(1, 3);
00134 returnMe[index++] = frEnd(2, 3) - frStart(2, 3);
00135 }
00136
00137
00138
00139 if (m_bOrientation)
00140 {
00141
00142 Frame rotated = frEnd * frStart.Inverse();
00143
00144
00145 double temp = rotated.values[0][0] + rotated.values[1][1] + rotated.values[2][2] - 1;
00146 assert(fabs(temp) <= (2+ROUNDING_ERROR));
00147 if (temp>2.0)
00148 temp = 2.0;
00149 else if (temp<-2.0)
00150 temp = -2.0;
00151
00152 double angle = acos(temp/2.);
00153 Vector4 axis;
00154 axis[0] = rotated.values[2][1] - rotated.values[1][2];
00155 axis[1] = rotated.values[0][2] - rotated.values[2][0];
00156 axis[2] = rotated.values[1][0] - rotated.values[0][1];
00157
00158 if (sin(angle) != 0)
00159 axis /= (2*sin(angle));
00160
00161 axis *= angle;
00162
00163 returnMe[index++] = axis[0];
00164 returnMe[index++] = axis[1];
00165 returnMe[index++] = axis[2];
00166 }
00167
00168 return returnMe;
00169 }
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196 VectorN IK_Jacobian::ComputeHomogeneousTerm(CJacobian &jacobian, Matrixmxn &mJEnd, Matrixmxn &mJEndInv, VectorN &vEndEffVel)
00197 {
00198 assert(m_vClosestPoints.size() == m_nObsPoint);
00199
00200 VectorN vHomogeneousSolns;
00201 VectorN vSingleTerm(vHomogeneousSolns);
00202
00203 vHomogeneousSolns.SetLength(m_nDof);
00204 for (int j = 0; j < m_nObsPoint; j++)
00205 {
00206
00207 int robFrame;
00208 Frame worldTObsFrame;
00209 Frame frObsPtsInLink;
00210 Frame frObsPtsInLinkWorld;
00211 VectorN vObsPtsInLink(0, 0, 0);
00212
00213
00214 robFrame = m_vClosestPoints[j].robFrame;
00215
00216
00217
00218
00219 worldTObsFrame = jacobian.GetJointWorldFrame(robFrame-1);
00220 vObsPtsInLink[0] = m_vClosestPoints[j].rob_pts[0];
00221 vObsPtsInLink[1] = m_vClosestPoints[j].rob_pts[1];
00222 vObsPtsInLink[2] = m_vClosestPoints[j].rob_pts[2];
00223 Vector4 vTranslate(vObsPtsInLink[0], vObsPtsInLink[1], vObsPtsInLink[2]);
00224 frObsPtsInLink.Translate(vTranslate);
00225 frObsPtsInLinkWorld = worldTObsFrame * frObsPtsInLink;
00226 vTranslate = frObsPtsInLinkWorld.GetTranslationVector();
00227 vObsPtsInLink[0] = vTranslate[0];
00228 vObsPtsInLink[1] = vTranslate[1];
00229 vObsPtsInLink[2] = vTranslate[2];
00230
00231
00232 VectorN vObsPtWorld(m_vClosestPoints[j].obs_pts[0],m_vClosestPoints[j].obs_pts[1], m_vClosestPoints[j].obs_pts[2]);
00233
00234 VectorN vObsEscapeVel = vObsPtsInLink - vObsPtWorld;
00235 vObsEscapeVel /= vObsEscapeVel.Magnitude();
00236
00237
00238
00239 Matrixmxn mJObs, mJObsInv;
00240 if (robFrame == m_nDof)
00241 {
00242 frObsPtsInLink = Matrix4x4::Identity();
00243 frObsPtsInLink.Translate(jacobian.GetJointFrame(robFrame-1).GetTranslationVector());
00244 jacobian.SetInterestPoint(robFrame-2, frObsPtsInLink, true, false);
00245 }
00246 else
00247 {
00248 jacobian.SetInterestPoint(robFrame-1, frObsPtsInLink, true, false);
00249 }
00250 mJObs = *(jacobian.GetMatrix());
00251 mJObs.Inverse(mJObsInv);
00252 LogMatrix("ikJacobian.log", mJObs, "Jacobian Matrix for Obstale point: ");
00253 LogMatrix("ikJacobian.log", mJObsInv, "Inverse of Jobs: ");
00254
00255 Matrixmxn mTemp(m_nDof, m_nDof);
00256 Matrixmxn mTemp2(mTemp);
00257 Matrixmxn mTemp3(mTemp);
00258 Matrixmxn mTemp4(mTemp);
00259
00260 mTemp -= mJEndInv * mJEnd;
00261 LogMatrix("ikJacobian.log", mTemp, "Temp: I-Je^+ Je");
00262 mTemp2 -= mJObsInv * mJObs;
00263 LogMatrix("ikJacobian.log", mTemp2, "Temp2: I-Jo^+ Jo");
00264 mTemp3 = mJObs * mTemp;
00265 LogMatrix("ikJacobian.log", mTemp3, "Temp3: Jo (I-Je^+ Je)");
00266 mTemp3 = mTemp3.Inverse();
00267 LogMatrix("ikJacobian.log", mTemp3, "Inverse of Temp3:");
00268 mTemp4 = mJObs * mJEndInv;
00269 LogMatrix("ikJacobian.log", mTemp3, "Temp4: Jo Je^+");
00270
00271 double solnGain;
00272 double velcGain;
00273 if (m_vClosestPoints[j].distance < UNITY_GAIN_DISTANCE)
00274 {
00275 solnGain = m_dHomoGain;
00276 }
00277 else if (m_vClosestPoints[j].distance > SOI_GAIN_DISTANCE)
00278 {
00279 solnGain = 0;
00280 }
00281 else
00282 {
00283 solnGain = m_dHomoGain*m_vClosestPoints[m_nObsPoint-1-j].distance/m_dTotalObsDist;
00284 }
00285
00286 if ((m_dObsDistGain/(m_vClosestPoints[j].distance)) > MAX_OBS_VEL)
00287 {
00288 velcGain = MAX_OBS_VEL;
00289 }
00290 else
00291 {
00292 velcGain = m_dObsDistGain / (m_vClosestPoints[j].distance);
00293 }
00294
00295 mTemp3 *= solnGain;
00296 VectorN vTemp = vObsEscapeVel * velcGain;
00297 vTemp -= mTemp4 * vEndEffVel;
00298 vSingleTerm = mTemp3 * vTemp;
00299
00300 vHomogeneousSolns += vSingleTerm;
00301
00302 LogVector("ikJacobian.log", vSingleTerm, "Single Term:");
00303 }
00304
00305 return vHomogeneousSolns;
00306 }
00307
00308 bool IK_Jacobian::Plan()
00309 {
00310
00311 StartTimer();
00312
00313
00314 const CD_Swiftpp* pSwiftpp = dynamic_cast< const CD_Swiftpp* >( collisionDetector );
00315 if (pSwiftpp == NULL)
00316 {
00317 return false;
00318 }
00319
00320
00321 if ( collisionDetector->IsInterfering( GetStartConfig() ) )
00322 {
00323
00324
00325 return false;
00326 }
00327
00328 Configuration currentConfig, nextConfig, jointVelocity;
00329 currentConfig.SetNumDOF(m_nDof);
00330 nextConfig.SetNumDOF(m_nDof);
00331 jointVelocity.SetNumDOF(m_nDof);
00332 jointVelocity *= 0;
00333 currentConfig = GetStartConfig();
00334
00335 path.Clear();
00336 path.AppendPoint(currentConfig);
00337
00338
00339 CJacobian jacobian(*m_pRobot);
00340 jacobian.SetConfiguration(currentConfig);
00341
00342 bool abort = false;
00343 for (int iViaPts = 0; iViaPts < (poses.size()-1); iViaPts++)
00344 {
00345
00346 VectorN vEndEffVel;
00347
00348 Frame frCurrentEndEff = jacobian.GetJointWorldFrame(m_nDof-1)*m_frEndEffector;
00349
00350 double dist = Distance(frCurrentEndEff, poses[iViaPts]);
00351 if (dist > m_dPathTol)
00352 {
00353 LogMessage("ikJacobian.log", "Aborting.... Motion too large!");
00354 abort = true;
00355 break;
00356 }
00357 if (m_bOrientation)
00358 {
00359 double x1, y1, z1, yaw1, roll1, pitch1;
00360 double x2, y2, z2, yaw2, roll2, pitch2;
00361 GetRotAngles(frCurrentEndEff, x1, y1, z1, roll1, pitch1, yaw1);
00362 GetRotAngles(poses[iViaPts], x2, y2, z2, roll2, pitch2, yaw2);
00363 if ( (fabs(roll1-roll2)>m_dAngTol) || (fabs(pitch1-pitch2)>m_dAngTol) || (fabs(yaw1-yaw2)>m_dAngTol) )
00364 {
00365 LogMessage("ikJacobian.log", "Aborting.... Motion too large!");
00366 abort = true;
00367 break;
00368 }
00369 }
00370
00371
00372 Matrix4x4 logmat;
00373 Matrixmxn logmatmn;
00374 logmat = poses[iViaPts];
00375 logmatmn = logmat;
00376 LogMatrix("ikJacobian.log", logmatmn , "Expected pose in Trajectory:");
00377 logmat = frCurrentEndEff;
00378 logmatmn = logmat;
00379 LogMatrix("ikJacobian.log", logmatmn, "Actually Pose got:");
00380 logmat = jacobian.GetJointWorldFrame(m_nDof-1)*m_frEndEffector;
00381 logmatmn = logmat;
00382 LogMatrix("ikJacobian.log", logmatmn, "End-effecot Frame:");
00383
00384
00385 vEndEffVel = ComputeEndEffVel(frCurrentEndEff, poses[iViaPts+1]);
00386 LogVector("ikJacobian.log", vEndEffVel, "Endeffector Velocity");
00387
00388
00389 jacobian.SetInterestPoint(m_nDof-1, m_frEndEffector, m_bPosition, m_bOrientation);
00390 jacobian.GetJointVelocity(jointVelocity, vEndEffVel);
00391 Matrixmxn mJEnd, mJEndInv;
00392 mJEnd = *(jacobian.GetMatrix());
00393 mJEnd.Inverse(mJEndInv);
00394 LogMatrix("ikJacobian.log", mJEnd, "Jacobian matrix of end-effector:");
00395 LogMatrix("ikJacobian.log", mJEndInv, "Jacobian matrix Inverse:");
00396
00397
00398
00399
00400 if (GetClosestValues(currentConfig) != false)
00401 {
00402 LogMessage("ikJacobian.log", "Aborting.... Colliding when getting collision points!");
00403 abort = true;
00404 break;
00405 }
00406
00407 double obsDirMag = 0;
00408
00409 if (m_vClosestPoints[0].distance <= m_dObsTol)
00410 {
00411
00412 LogMessage("ikJacobian.log", "Aborting.... Colliding, too close to obstacles!");
00413 abort = true;
00414 break;
00415 }
00416
00417 else
00418 {
00419
00420 VectorN vHomogeneousSolns;
00421 vHomogeneousSolns = ComputeHomogeneousTerm(jacobian, mJEnd, mJEndInv, vEndEffVel);
00422 LogVector("ikJacobian.log", vHomogeneousSolns, "Homogeneous Term:");
00423
00424
00425 jointVelocity += vHomogeneousSolns;
00426 }
00427
00428
00429
00430 CheckJointVelocities(jointVelocity);
00431 for (int i=0; i<m_nDof; i++)
00432 {
00433 double dValue = jointVelocity[i];
00434 jointVelocity[i] = Rad2Deg(dValue);
00435 }
00436 LogVector("ikJacobian.log", jointVelocity, "Joint Velocity");
00437
00438
00439 nextConfig = currentConfig + jointVelocity;
00440 CheckJointLimits(nextConfig);
00441 LogVector("ikJacobian.log", nextConfig, "Next Configuration");
00442
00443
00444
00445 if (collisionDetector->IsInterfering(nextConfig) == false)
00446 {
00447
00448 if (currentConfig != nextConfig)
00449 {
00450 currentConfig = nextConfig;
00451 path.AppendPoint(currentConfig);
00452
00453 }
00454 jacobian.SetConfiguration(currentConfig);
00455 }
00456 else
00457 {
00458
00459 LogMessage("ikJacobian.log", "Aborting.... Colliding when checking next configuration!");
00460 abort = true;
00461 break;
00462 }
00463
00464 if (HasTimeLimitExpired() != false)
00465 {
00466
00467 LogMessage("ikJacobian.log", "Aborting.... Time up!");
00468 abort = true;
00469 break;
00470 }
00471 }
00472
00473
00474 if (abort)
00475 {
00476 LogMessage("ikJacobian.log", "### Planning Failure");
00477 }
00478 else
00479 {
00480 LogMessage("ikJacobian.log", "### Planning Success");
00481 }
00482 return (abort == false);
00483 }
00484
00485
00486 void IK_Jacobian::InitNewSearch()
00487 {
00488 }
00489
00490 void IK_Jacobian::SetObstacleTolerance(double tol)
00491 {
00492 m_dObsTol = tol;
00493 ((CD_Swiftpp*) collisionDetector)->SetObstacleTolerance(tol);
00494 }
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 bool IK_Jacobian::GetClosestValues(const Configuration& config)
00516 {
00517 double *query_d, *query_npts;
00518 int iLink1, iLink2, query_np, *query_oids, *query_num_contacts,
00519 index = -1;
00520
00521 m_vClosestPoints.clear();
00522 closeValues init;
00523 init.distance = INFINITY;
00524 m_vClosestPoints.push_back(init);
00525
00526 bool collision = ((CD_Swiftpp*) collisionDetector)->QueryContactDetermination( config,
00527 true, INFINITY, query_np, &query_oids, &query_num_contacts, &query_d, &query_npts);
00528
00529 if (collision != false)
00530 {
00531 return true;
00532 }
00533
00534 int j, k, min;
00535
00536
00537
00538
00539 for (int i = 0; i < query_np; i++)
00540 {
00541 index += query_num_contacts[i];
00542 if (m_vClosestPoints.size() >= m_nObsPoint)
00543 {
00544 if (query_d[index] > m_vClosestPoints[m_nObsPoint-1].distance)
00545 {
00546 continue;
00547 }
00548
00549 }
00550
00551
00552 iLink1 = ((CD_Swiftpp*) collisionDetector)->GetRobotLinkBaseFrame(query_oids[2*i]);
00553 iLink2 = ((CD_Swiftpp*) collisionDetector)->GetRobotLinkBaseFrame(query_oids[2*i+1]);
00554 if ((iLink1 == -1) || (iLink2 == -1))
00555 {
00556 return false;
00557 }
00558 if (((iLink1 == 0)^(iLink2 == 0)) == false)
00559 {
00560 continue;
00561 }
00562
00563
00564 min = min(query_num_contacts[i], m_nObsPoint);
00565 k = 0;
00566 for (j = 0; j < min; j++)
00567 {
00568 while (k < m_nObsPoint)
00569 {
00570 if(m_vClosestPoints[k].distance > query_d[index-j])
00571 {
00572 closeValues newEntry;
00573 newEntry.distance = query_d[index-j];
00574 if (iLink1 == 0)
00575 {
00576 newEntry.obs_pts[0] = query_npts[6*index];
00577 newEntry.obs_pts[1] = query_npts[6*index+1];
00578 newEntry.obs_pts[2] = query_npts[6*index+2];
00579 newEntry.robFrame = iLink2;
00580 newEntry.rob_pts[0] = query_npts[6*index+3];
00581 newEntry.rob_pts[1] = query_npts[6*index+4];
00582 newEntry.rob_pts[2] = query_npts[6*index+5];
00583 }
00584 else
00585 {
00586 newEntry.obs_pts[0] = query_npts[6*index+3];
00587 newEntry.obs_pts[1] = query_npts[6*index+4];
00588 newEntry.obs_pts[2] = query_npts[6*index+5];
00589 newEntry.robFrame = iLink1;
00590 newEntry.rob_pts[0] = query_npts[6*index];
00591 newEntry.rob_pts[1] = query_npts[6*index+1];
00592 newEntry.rob_pts[2] = query_npts[6*index+2];
00593 }
00594 m_vClosestPoints.insert(m_vClosestPoints.begin()+k, newEntry);
00595 k++;
00596 break;
00597 }
00598 else if(&m_vClosestPoints[k] == m_vClosestPoints.end())
00599 {
00600 closeValues newEntry;
00601 newEntry.distance = query_d[index-j];
00602 if (iLink1 == 0)
00603 {
00604 newEntry.obs_pts[0] = query_npts[6*index];
00605 newEntry.obs_pts[1] = query_npts[6*index+1];
00606 newEntry.obs_pts[2] = query_npts[6*index+2];
00607 newEntry.robFrame = iLink2;
00608 newEntry.rob_pts[0] = query_npts[6*index+3];
00609 newEntry.rob_pts[1] = query_npts[6*index+4];
00610 newEntry.rob_pts[2] = query_npts[6*index+5];
00611 }
00612 else
00613 {
00614 newEntry.obs_pts[0] = query_npts[6*index+3];
00615 newEntry.obs_pts[1] = query_npts[6*index+4];
00616 newEntry.obs_pts[2] = query_npts[6*index+5];
00617 newEntry.robFrame = iLink1;
00618 newEntry.rob_pts[0] = query_npts[6*index];
00619 newEntry.rob_pts[1] = query_npts[6*index+1];
00620 newEntry.rob_pts[2] = query_npts[6*index+2];
00621 }
00622 m_vClosestPoints.push_back(newEntry);
00623 k++;
00624 break;
00625 }
00626 k++;
00627 }
00628 if (k >= m_nObsPoint) {break;}
00629 }
00630 }
00631 if ((m_vClosestPoints[0].distance == INFINITY)||(query_np==0))
00632 {
00633 closeValues defaults;
00634 defaults.distance = INFINITY;
00635 defaults.robFrame = 1;
00636 defaults.obs_pts[0] = INFINITY;
00637 defaults.obs_pts[1] = INFINITY;
00638 defaults.obs_pts[2] = INFINITY;
00639 defaults.rob_pts[0] = 1;
00640 defaults.rob_pts[1] = 0;
00641 defaults.rob_pts[2] = 0;
00642
00643 m_vClosestPoints.clear();
00644 for (i = 0; i < m_nObsPoint; i++)
00645 {
00646 m_vClosestPoints.push_back(defaults);
00647 }
00648
00649 }
00650 else if (query_np > 0)
00651 {
00652 m_vClosestPoints.erase(m_vClosestPoints.begin() + m_nObsPoint, m_vClosestPoints.end());
00653 m_dTotalObsDist = 0;
00654 for (i = 0; i < m_vClosestPoints.size(); i++)
00655 {
00656 m_dTotalObsDist += m_vClosestPoints[i].distance;
00657 }
00658 }
00659 else
00660 {
00661 return true;
00662 }
00663
00664 return false;
00665 }
00666
00667 void IK_Jacobian::CheckJointLimits(Configuration& config)
00668 {
00669 for (int i = 0; i<m_nDof; i++)
00670 {
00671 if ( config[i] < collisionDetector->JointMin(i) )
00672 {
00673 config[i] = collisionDetector->JointMin(i);
00674 }
00675 else if ( config[i] > collisionDetector->JointMax(i) )
00676 {
00677 config[i] = collisionDetector->JointMax(i);
00678 }
00679 }
00680 }
00681
00682 void IK_Jacobian::CheckJointVelocities(Configuration& config)
00683 {
00684 for (int i = 0; i < m_nDof; i++)
00685 {
00686 if (config[i] < -MAX_JOINT_VEL)
00687 {
00688 config[i] = -MAX_JOINT_VEL;
00689 }
00690 else if (config[i] > MAX_JOINT_VEL)
00691 {
00692 config[i] = MAX_JOINT_VEL;
00693 }
00694
00695 }
00696 }
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720 void IK_Jacobian::SetTrajectory(std::vector<Frame> &traj)
00721 {
00722 poses.clear();
00723 for (int i=0; i<traj.size(); i++)
00724 {
00725 poses.push_back(traj[i]);
00726
00727 char szMsg[100];
00728 Matrixmxn matLog = traj[i];
00729 sprintf(szMsg, "%d(th) pose:\n", i);
00730 LogMatrix("ikJacobian.log", matLog, szMsg);
00731 }
00732
00733 }
00734
00735
00736 double IK_Jacobian::Distance(Frame &frame1, Frame &frame2)
00737 {
00738 Vector4 vec1 = frame1.GetTranslationVector();
00739 Vector4 vec2 = frame2.GetTranslationVector();
00740 vec1 -= vec2;
00741 return vec1.Magnitude();
00742 }
00743