00001 /* 00002 This class performs a pivot calibration providing a defined translation 00003 between a tool tip and its sensor system. 00004 00005 Copyright (C) 2010 - 2016 Christoph Haenisch 00006 00007 Chair of Medical Engineering (mediTEC) 00008 RWTH Aachen University 00009 Pauwelsstr. 20 00010 52074 Aachen 00011 Germany 00012 00013 Version 1.0.0 (2016-07-26) 00014 */ 00015 00022 #ifndef PIVOTCALIBRATION_HPP_3123933941 00023 #define PIVOTCALIBRATION_HPP_3123933941 00024 00025 #include <utility> 00026 #include <vector> 00027 00028 #include <Eigen/Dense> 00029 #include <Eigen/Eigenvalues> 00030 00031 #include "ErrorObj.hpp" 00032 #include "Coordinate.hpp" 00033 #include "FitSphere.hpp" 00034 #include "Range.hpp" 00035 #include "Transform3D.hpp" 00036 00037 00038 namespace TRTK 00039 { 00040 00041 00043 // Interface // 00045 00046 00148 template <class T> 00149 class PivotCalibration 00150 { 00151 public: 00152 enum Error { 00153 NOT_ENOUGH_INPUT_DATA, 00154 UNEQUAL_CARDINALITY_OF_INPUT_SETS, 00155 UNKNOWN_ERROR 00156 }; 00157 00158 typedef T value_type; 00159 typedef Eigen::Matrix<T, 3, 1> Vector3T; 00160 typedef Eigen::Matrix<T, 4, 1> Vector4T; 00161 typedef Eigen::Matrix<T, Eigen::Dynamic, 1> VectorXT; 00162 typedef Eigen::Matrix<T, 3, 3> Matrix3T; 00163 typedef Eigen::Matrix<T, 4, 4> Matrix4T; 00164 typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> MatrixXT; 00165 typedef Coordinate<T> Point; 00166 typedef std::pair<Vector3T, Matrix3T> DataType; 00167 00168 00169 PivotCalibration() {}; 00170 virtual ~PivotCalibration() {}; 00171 00172 virtual size_t getNumberItemsRequired() const = 0; 00173 virtual void setLocations(Range<Vector3T> locations) = 0; 00174 virtual void setRotations(Range<Matrix3T> rotations) = 0; 00175 virtual T compute() = 0; 00176 virtual T getRMSE() const = 0; 00177 virtual const Vector3T & getLocalPivotPoint() const = 0; 00178 virtual const Vector3T & getPivotPoint() const = 0; 00179 }; 00180 00181 00183 // PivotCalibrationCombinatorialApproach // 00185 00186 00276 template <class T> 00277 class PivotCalibrationCombinatorialApproach : public PivotCalibration<T> 00278 { 00279 private: 00280 typedef PivotCalibration<T> super; 00281 00282 public: 00283 typedef T value_type; 00284 typedef typename super::Vector3T Vector3T; 00285 typedef typename super::Vector4T Vector4T; 00286 typedef typename super::VectorXT VectorXT; 00287 typedef typename super::Matrix3T Matrix3T; 00288 typedef typename super::Matrix4T Matrix4T; 00289 typedef typename super::MatrixXT MatrixXT; 00290 typedef typename super::Point Point; 00291 typedef typename super::DataType DataType; 00292 00293 PivotCalibrationCombinatorialApproach(); 00294 ~PivotCalibrationCombinatorialApproach(); 00295 00296 size_t getNumberItemsRequired() const; 00297 void setLocations(Range<Vector3T> locations); 00298 void setRotations(Range<Matrix3T> rotations); 00299 T compute(); 00300 T getRMSE() const; 00301 const Vector3T & getPivotPoint() const; 00302 const Vector3T & getLocalPivotPoint() const; 00303 00304 private: 00305 std::vector<Vector3T> locations; 00306 std::vector<Matrix3T> rotations; 00307 Vector3T global_pivot_point; 00308 Vector3T local_pivot_point; 00309 T rmse; 00310 }; 00311 00312 00313 template <class T> 00314 PivotCalibrationCombinatorialApproach<T>::PivotCalibrationCombinatorialApproach() : rmse(T(0)) 00315 { 00316 } 00317 00318 00319 template <class T> 00320 PivotCalibrationCombinatorialApproach<T>::~PivotCalibrationCombinatorialApproach() 00321 { 00322 } 00323 00324 00325 template <class T> 00326 T PivotCalibrationCombinatorialApproach<T>::compute() 00327 { 00328 /* 00329 00330 Explanation of the algorithm 00331 00332 If a tool touches the pivot point with its tip, the pivot point's location M 00333 in global coordinates is 00334 00335 M = R_i * t + t_i 00336 00337 where 'R_i' is the rotation and 't_i' the location of the tool sensor in the world or 00338 tracking coordinate system. 't' is the sought location of the tool tip in the sensor's 00339 local coordinate system. Note, that 't' remains the same for all measurements! Now, 00340 using two different measurements, the pivot point M can be eliminated 00341 00342 R_i * t + t_i = R_j * t + t_j 00343 00344 This can be rearranged to 00345 00346 (R_i - R_j) * t = (t_j - t_i) 00347 00348 Doing this for all combinations of all measurements yields an over-determined system of 00349 equations Ax = b (with x = t) which can be solved for the sought translation vector 't' 00350 via the method of least squares. 00351 00352 | R1 - R2 | | t2 - t1 | 00353 | R1 - R3 | | t3 - t1 | 00354 | R1 - R4 | * t = | t4 - t1 | <==> At = b 00355 | ... | | ... | 00356 | Rn - Rn-1 | | tn-1 - tn | 00357 00358 t = [A^T * A]^(-1) * A^T * B (Moore-Penrose pseudoinverse) 00359 00360 Note: Not all combinations are taken into account. For instance, the case i == j 00361 is omitted. 00362 00363 */ 00364 00365 const int n = rotations.size(); 00366 const int max_number_of_combinations = n * (n - 1); 00367 int current_index = 0; // i-th entry (or "row") in A and b 00368 00369 // Build up A and b and solve the system of equations 00370 00371 MatrixXT A(3 * max_number_of_combinations, 3); 00372 MatrixXT b(3 * max_number_of_combinations, 1); 00373 00374 for (int i = 0; i < n; ++i) 00375 { 00376 for (int j = 0; j < n; j++) 00377 { 00378 if (i == j || rotations[i].isApprox(rotations[j])) 00379 { 00380 continue; 00381 } 00382 00383 A.block(current_index * 3, 0, 3, 3) = rotations[i] - rotations[j]; 00384 b.block(current_index * 3, 0, 3, 1) = locations[j] - locations[i]; 00385 00386 ++current_index; 00387 } 00388 00389 } 00390 00391 A.resize(current_index * 3, 3); 00392 b.resize(current_index * 3, 1); 00393 00394 local_pivot_point = (A.transpose() * A).inverse() * (A.transpose() * b); 00395 00396 // Compute an averaged global pivot point 00397 00398 global_pivot_point = Vector3T(0, 0, 0); 00399 00400 for (int i = 0; i < n; ++i) 00401 { 00402 global_pivot_point += rotations[i] * local_pivot_point + locations[i]; 00403 } 00404 00405 global_pivot_point /= n; 00406 00407 // Compute the RMSE 00408 00409 using std::sqrt; 00410 rmse = T(0); 00411 00412 for (int i = 0; i < n; ++i) 00413 { 00414 Vector3T noisy_center_point = rotations[i] * local_pivot_point + locations[i]; 00415 rmse += (noisy_center_point - global_pivot_point).squaredNorm(); 00416 } 00417 00418 rmse = sqrt(rmse / n); 00419 00420 return rmse; 00421 } 00422 00423 00424 template <class T> 00425 const typename PivotCalibrationCombinatorialApproach<T>::Vector3T & PivotCalibrationCombinatorialApproach<T>::getLocalPivotPoint() const 00426 { 00427 return local_pivot_point; 00428 } 00429 00430 00431 template <class T> 00432 size_t PivotCalibrationCombinatorialApproach<T>::getNumberItemsRequired() const 00433 { 00434 return 4; 00435 } 00436 00437 00438 template <class T> 00439 const typename PivotCalibrationCombinatorialApproach<T>::Vector3T & PivotCalibrationCombinatorialApproach<T>::getPivotPoint() const 00440 { 00441 return global_pivot_point; 00442 } 00443 00444 00445 template <class T> 00446 T PivotCalibrationCombinatorialApproach<T>::getRMSE() const 00447 { 00448 return rmse; 00449 } 00450 00451 00452 template <class T> 00453 void PivotCalibrationCombinatorialApproach<T>::setLocations(Range<Vector3T> locations) 00454 { 00455 this->locations.resize(locations.size()); 00456 size_t i = 0; 00457 00458 while (!locations.isDone()) 00459 { 00460 this->locations[i++] = locations.currentItem(); 00461 locations.next(); 00462 } 00463 } 00464 00465 00466 template <class T> 00467 void PivotCalibrationCombinatorialApproach<T>::setRotations(Range<Matrix3T> rotations) 00468 { 00469 this->rotations.resize(rotations.size()); 00470 size_t i = 0; 00471 00472 while (!rotations.isDone()) 00473 { 00474 this->rotations[i++] = rotations.currentItem(); 00475 rotations.next(); 00476 } 00477 } 00478 00479 00481 // PivotCalibrationLeastSquares // 00483 00484 00590 template <class T> 00591 class PivotCalibrationLeastSquares : public PivotCalibration<T> 00592 { 00593 private: 00594 typedef PivotCalibration<T> super; 00595 00596 public: 00597 typedef T value_type; 00598 typedef typename super::Vector3T Vector3T; 00599 typedef typename super::Vector4T Vector4T; 00600 typedef typename super::VectorXT VectorXT; 00601 typedef typename super::Matrix3T Matrix3T; 00602 typedef typename super::Matrix4T Matrix4T; 00603 typedef typename super::MatrixXT MatrixXT; 00604 typedef typename super::Point Point; 00605 typedef typename super::DataType DataType; 00606 00607 PivotCalibrationLeastSquares(); 00608 ~PivotCalibrationLeastSquares(); 00609 00610 size_t getNumberItemsRequired() const; 00611 void setLocations(Range<Vector3T> locations); 00612 void setRotations(Range<Matrix3T> rotations); 00613 void setRemoveOutliers(bool value); 00614 T compute(); 00615 T getRMSE() const; 00616 const Vector3T & getPivotPoint() const; 00617 const Vector3T & getLocalPivotPoint() const; 00618 00619 private: 00620 std::vector<Vector3T> locations; 00621 std::vector<Matrix3T> rotations; 00622 bool remove_outliers; 00623 Vector3T global_pivot_point; 00624 Vector3T local_pivot_point; 00625 T rmse; 00626 }; 00627 00628 00629 template <class T> 00630 PivotCalibrationLeastSquares<T>::PivotCalibrationLeastSquares() : remove_outliers(false), rmse(T(0)) 00631 { 00632 } 00633 00634 00635 template <class T> 00636 PivotCalibrationLeastSquares<T>::~PivotCalibrationLeastSquares() 00637 { 00638 } 00639 00640 00641 template <class T> 00642 T PivotCalibrationLeastSquares<T>::compute() 00643 { 00644 /* 00645 00646 Explanation of the algorithm 00647 00648 If a tool touches the pivot point with its tip, the pivot point's location M 00649 in global coordinates is 00650 00651 M = R_i * t + t_i 00652 00653 where 'R_i' is the rotation and 't_i' the location of the tool sensor in the world or 00654 tracking coordinate system. 't' is the sought location of the tool tip in the sensor's 00655 local coordinate system. Note, that 't' and 'M' remain the same for all measurements. 00656 The above equation can be rearranged to 00657 00658 R_i * t - M = -t_i 00659 00660 or in matrix notation 00661 00662 | | | t | 00663 | R_i -I | * | | = -t_i 00664 | | | M | 00665 00666 Combining all measurements this yields 00667 00668 | R_1 -I | | -t_1 | 00669 | R_2 -I | * | t | = | -t_2 | <==> Ax = b 00670 | ... ... | | M | | ... | 00671 | R_n -I | | -t_n | 00672 00673 The solution of the least square problem x = argmin_x | Ax - b |^2 is 00674 00675 x = [A^T * A]^(-1) * A^T * b (Moore-Penrose pseudoinverse) 00676 00677 If requested, two passes are run. The first run with all samples and the 00678 second run only with those that are within 3 standard deviations. 00679 00680 */ 00681 00682 // Build up A and b and solve the system of equations 00683 00684 using std::sqrt; 00685 00686 const int n = rotations.size(); 00687 00688 MatrixXT A(3 * n, 6); 00689 MatrixXT b(3 * n, 1); 00690 Matrix3T negative_identity = -Matrix3T::Identity(); 00691 00692 for (int i = 0; i < n; ++i) 00693 { 00694 A.block(i * 3, 0, 3, 3) = rotations[i]; 00695 A.block(i * 3, 3, 3, 3) = negative_identity; 00696 b.block(i * 3, 0, 3, 1) = - locations[i]; 00697 } 00698 00699 VectorXT x = (A.transpose() * A).inverse() * (A.transpose() * b); 00700 00701 local_pivot_point = x.head(3); 00702 global_pivot_point = x.tail(3); 00703 00704 // Remove outliers and recompute the solution 00705 00706 VectorXT inlier_mask = VectorXT::Constant(n, 1); 00707 00708 if (remove_outliers) 00709 { 00710 // Consider all measurements that lie within three standard deviations 00711 // of the distribution of all samples as inliers. Disregard outliers 00712 // by solving weighted least squares and setting the weights for outliers 00713 // to zero. 00714 00715 typedef Eigen::Array<T, Eigen::Dynamic, 1> ArrayXT; 00716 00717 VectorXT residuals = (A * x - b); 00718 ArrayXT deviations(n); 00719 00720 for (int i = 0; i < n; ++i) 00721 { 00722 deviations(i) = residuals.block(3*i, 0, 3, 1).norm(); 00723 } 00724 00725 T mean = deviations.mean(); 00726 T sd = sqrt((deviations - mean).square().matrix().sum() / (n - 1)); // standard deviation 00727 00728 VectorXT weights(3 * n); 00729 00730 for (int i = 0; i < n; ++i) 00731 { 00732 if (std::abs(deviations[i] - mean) < 3 * sd) 00733 { 00734 weights(3 * i + 0) = 1; // inliers 00735 weights(3 * i + 1) = 1; // inliers 00736 weights(3 * i + 2) = 1; // inliers 00737 } 00738 else 00739 { 00740 weights(3 * i + 0) = 0; // outliers 00741 weights(3 * i + 1) = 0; // outliers 00742 weights(3 * i + 2) = 0; // outliers 00743 00744 inlier_mask(i) = 0; 00745 } 00746 } 00747 00748 // Recompute the solution 00749 00750 VectorXT x = (A.transpose() * weights.asDiagonal() * A).inverse() * (A.transpose() * (weights.asDiagonal() * b)); 00751 00752 local_pivot_point = x.head(3); 00753 global_pivot_point = x.tail(3); 00754 } 00755 00756 // Compute the RMSE 00757 00758 rmse = T(0); 00759 00760 for (int i = 0; i < n; ++i) 00761 { 00762 Vector3T noisy_center_point = rotations[i] * local_pivot_point + locations[i]; 00763 rmse += (noisy_center_point - global_pivot_point).squaredNorm() * inlier_mask(i); 00764 } 00765 00766 rmse = sqrt(rmse / inlier_mask.sum()); // inlier_mask.sum() == n if outliers are not removed 00767 00768 return rmse; 00769 } 00770 00771 00772 template <class T> 00773 const typename PivotCalibrationLeastSquares<T>::Vector3T & PivotCalibrationLeastSquares<T>::getLocalPivotPoint() const 00774 { 00775 return local_pivot_point; 00776 } 00777 00778 00779 template <class T> 00780 size_t PivotCalibrationLeastSquares<T>::getNumberItemsRequired() const 00781 { 00782 return 3; 00783 } 00784 00785 00786 template <class T> 00787 const typename PivotCalibrationLeastSquares<T>::Vector3T & PivotCalibrationLeastSquares<T>::getPivotPoint() const 00788 { 00789 return global_pivot_point; 00790 } 00791 00792 00793 template <class T> 00794 T PivotCalibrationLeastSquares<T>::getRMSE() const 00795 { 00796 return rmse; 00797 } 00798 00799 00800 template <class T> 00801 void PivotCalibrationLeastSquares<T>::setLocations(Range<Vector3T> locations) 00802 { 00803 this->locations.resize(locations.size()); 00804 size_t i = 0; 00805 00806 while (!locations.isDone()) 00807 { 00808 this->locations[i++] = locations.currentItem(); 00809 locations.next(); 00810 } 00811 } 00812 00813 00814 template <class T> 00815 void PivotCalibrationLeastSquares<T>::setRemoveOutliers(bool value) 00816 { 00817 remove_outliers = value; 00818 } 00819 00820 00821 template <class T> 00822 void PivotCalibrationLeastSquares<T>::setRotations(Range<Matrix3T> rotations) 00823 { 00824 this->rotations.resize(rotations.size()); 00825 size_t i = 0; 00826 00827 while (!rotations.isDone()) 00828 { 00829 this->rotations[i++] = rotations.currentItem(); 00830 rotations.next(); 00831 } 00832 } 00833 00834 00836 // PivotCalibrationPATM // 00838 00839 00919 template <class T> 00920 class PivotCalibrationPATM : public PivotCalibration<T> 00921 { 00922 private: 00923 typedef PivotCalibration<T> super; 00924 00925 public: 00926 typedef T value_type; 00927 typedef typename super::Vector3T Vector3T; 00928 typedef typename super::Vector4T Vector4T; 00929 typedef typename super::VectorXT VectorXT; 00930 typedef typename super::Matrix3T Matrix3T; 00931 typedef typename super::Matrix4T Matrix4T; 00932 typedef typename super::MatrixXT MatrixXT; 00933 typedef typename super::Point Point; 00934 typedef typename super::DataType DataType; 00935 00936 typedef Eigen::Matrix<std::complex<T>, 3, 3> Matrix3cT; 00937 typedef Eigen::Matrix<std::complex<T>, 4, 4> Matrix4cT; 00938 00939 PivotCalibrationPATM(); 00940 ~PivotCalibrationPATM(); 00941 00942 size_t getNumberItemsRequired() const; 00943 void setLocations(Range<Vector3T> locations); 00944 void setRotations(Range<Matrix3T> rotations); 00945 void setNumberIterations(int count); 00946 T compute(); 00947 T getRMSE() const; 00948 const Vector3T & getPivotPoint() const; 00949 const Vector3T & getLocalPivotPoint() const; 00950 00951 private: 00952 unsigned number_of_iterations; 00953 std::vector<Vector3T> locations; 00954 std::vector<Matrix3T> rotations; 00955 Vector3T global_pivot_point; 00956 Vector3T local_pivot_point; 00957 T rmse; 00958 }; 00959 00960 00961 template <class T> 00962 PivotCalibrationPATM<T>::PivotCalibrationPATM() : number_of_iterations(500), rmse(T(0)) 00963 { 00964 } 00965 00966 00967 template <class T> 00968 PivotCalibrationPATM<T>::~PivotCalibrationPATM() 00969 { 00970 } 00971 00972 00973 template <class T> 00974 T PivotCalibrationPATM<T>::compute() 00975 { 00976 /* 00977 00978 Explanation of the algorithm 00979 00980 If a tool touches the pivot point with its tip, the pivot point's location M 00981 in global coordinates is 00982 00983 M = R_i * t + t_i 00984 00985 where 'R_i' is the rotation and 't_i' the location of the tool sensor in the world or 00986 tracking coordinate system. 't' is the sought location of the tool tip in the sensor's 00987 local coordinate system. Note, that 't' remains the same for all measurements! Now, 00988 using two different measurements, the pivot point M can be eliminated 00989 00990 R_i * t + t_i = R_j * t + t_j 00991 00992 This can be rearranged to 00993 00994 t = R_i^T * R_j * t + R_i^T * (t_j - t_i) =: phi(t) (*) 00995 00996 This is actually the form of a fixed-point iteration, i.e. t_i+1 = phi(t_i). However, 00997 in this form phi is not a contraction mapping since the Lipschitz constant L is not 00998 strictly less than 1: 00999 01000 || phi(x) - phi(y) || = || R_i^T * R_j * (x - y) || = L * || x - y||, L = 1 01001 01002 This can be changed by averaging at least two equations (*) with different 01003 measurements. Thus we construct phi(t) as follows 01004 01005 phi(t) := R * t + p (**) 01006 01007 where R := 1/n * sum_i R_i^T * R_(i+1), p := 1/n * sum_i R_i^T * (t_(i+1) - t_i), 01008 and n is the number of measurements. Since the sum goes up to n, R_(n+1) and t_(n1+1) 01009 are set to R_1 and t_1, respectively. 01010 01011 The equation (**) can also rewritten as 01012 01013 | R p | 01014 phi(t) = | | * t = A * t 01015 | 0 1 | 01016 01017 and thus t_i+1 = phi(t_i) = A * t_i = A^i * t0. 01018 01019 */ 01020 01021 // Build up R and p 01022 01023 Matrix3T R = Matrix3T::Zero(); 01024 Vector3T p = Vector3T::Zero(); 01025 01026 const int n = rotations.size(); 01027 01028 for (int i = 0; i < n; ++i) 01029 { 01030 int k = i % n; // 0, 1, 2, ..., n-1, n 01031 int l = (i + 1) % n; // 1, 2, 3, ..., n, 0 01032 01033 R += rotations[k].transpose() * rotations[l]; 01034 p += rotations[k].transpose() * (locations[l] - locations[k]); 01035 } 01036 01037 R /= n; 01038 p /= n; 01039 01040 Matrix4T A = Matrix4T::Identity(); 01041 //A.block<3, 3>(0, 0) = R; 01042 A.topLeftCorner(3,3) = R; 01043 //A.block<3, 1>(0, 3) = p; 01044 A.topRightCorner(3,1) = p; 01045 01046 // Carry out the fixed-point iterations 01047 01048 Vector4T t = Vector4T(1, 1, 1, 1); 01049 01050 if (number_of_iterations > 10) 01051 { 01052 // Compute the power of A 01053 Eigen::EigenSolver<MatrixXT> solver(A); 01054 Matrix4cT D = solver.eigenvalues().asDiagonal(); 01055 Matrix4cT V = solver.eigenvectors(); 01056 A = (V * D.array().pow(number_of_iterations).matrix() * V.inverse()).real(); 01057 t = A * t; 01058 } 01059 else 01060 { 01061 for (unsigned i = 0; i < number_of_iterations; ++i) 01062 { 01063 t = A * t; 01064 } 01065 } 01066 01067 // local_pivot_point = t.head<3>(); 01068 local_pivot_point << t(0),t(1),t(2); 01069 01070 // Compute an averaged global pivot point 01071 01072 global_pivot_point = Vector3T(0, 0, 0); 01073 01074 for (int i = 0; i < n; ++i) 01075 { 01076 global_pivot_point += rotations[i] * local_pivot_point + locations[i]; 01077 } 01078 01079 global_pivot_point /= n; 01080 01081 // Compute the RMSE 01082 01083 using std::sqrt; 01084 rmse = T(0); 01085 01086 for (int i = 0; i < n; ++i) 01087 { 01088 Vector3T noisy_center_point = rotations[i] * local_pivot_point + locations[i]; 01089 rmse += (noisy_center_point - global_pivot_point).squaredNorm(); 01090 } 01091 01092 rmse = sqrt(rmse / n); 01093 01094 return rmse; 01095 } 01096 01097 01098 template <class T> 01099 const typename PivotCalibrationPATM<T>::Vector3T & PivotCalibrationPATM<T>::getLocalPivotPoint() const 01100 { 01101 return local_pivot_point; 01102 } 01103 01104 01105 template <class T> 01106 size_t PivotCalibrationPATM<T>::getNumberItemsRequired() const 01107 { 01108 return 3; 01109 } 01110 01111 01112 template <class T> 01113 const typename PivotCalibrationPATM<T>::Vector3T & PivotCalibrationPATM<T>::getPivotPoint() const 01114 { 01115 return global_pivot_point; 01116 } 01117 01118 01119 template <class T> 01120 T PivotCalibrationPATM<T>::getRMSE() const 01121 { 01122 return rmse; 01123 } 01124 01125 01126 template <class T> 01127 void PivotCalibrationPATM<T>::setLocations(Range<Vector3T> locations) 01128 { 01129 this->locations.resize(locations.size()); 01130 size_t i = 0; 01131 01132 while (!locations.isDone()) 01133 { 01134 this->locations[i++] = locations.currentItem(); 01135 locations.next(); 01136 } 01137 } 01138 01139 01140 template <class T> 01141 void PivotCalibrationPATM<T>::setNumberIterations(int count) 01142 { 01143 number_of_iterations = count; 01144 } 01145 01146 01147 template <class T> 01148 void PivotCalibrationPATM<T>::setRotations(Range<Matrix3T> rotations) 01149 { 01150 this->rotations.resize(rotations.size()); 01151 size_t i = 0; 01152 01153 while (!rotations.isDone()) 01154 { 01155 this->rotations[i++] = rotations.currentItem(); 01156 rotations.next(); 01157 } 01158 } 01159 01160 01162 // PivotCalibrationTwoStep // 01164 01165 01223 template <class T> 01224 class PivotCalibrationTwoStep : public PivotCalibration<T> 01225 { 01226 private: 01227 typedef PivotCalibration<T> super; 01228 01229 public: 01230 typedef T value_type; 01231 typedef typename super::Vector3T Vector3T; 01232 typedef typename super::Vector4T Vector4T; 01233 typedef typename super::VectorXT VectorXT; 01234 typedef typename super::Matrix3T Matrix3T; 01235 typedef typename super::MatrixXT MatrixXT; 01236 typedef typename super::Point Point; 01237 typedef typename super::DataType DataType; 01238 01239 PivotCalibrationTwoStep(); 01240 ~PivotCalibrationTwoStep(); 01241 01242 size_t getNumberItemsRequired() const; 01243 void setLocations(Range<Vector3T> locations); 01244 void setRotations(Range<Matrix3T> rotations); 01245 T compute(); 01246 T getRMSE() const; 01247 const Vector3T & getPivotPoint() const; 01248 const Vector3T & getLocalPivotPoint() const; 01249 01250 private: 01251 std::vector<Vector3T> locations; 01252 std::vector<Matrix3T> rotations; 01253 Vector3T global_pivot_point; 01254 Vector3T local_pivot_point; 01255 T rmse; 01256 }; 01257 01258 01259 template <class T> 01260 PivotCalibrationTwoStep<T>::PivotCalibrationTwoStep() : rmse(T(0)) 01261 { 01262 } 01263 01264 01265 template <class T> 01266 PivotCalibrationTwoStep<T>::~PivotCalibrationTwoStep() 01267 { 01268 } 01269 01270 01271 template <class T> 01272 T PivotCalibrationTwoStep<T>::compute() 01273 { 01274 /* 01275 01276 Explanation of the algorithm 01277 01278 As described in the details section, during the calibration procedure, 01279 an object is moved around a pivot point. For example a tool is moved in 01280 such a way that the tool tip remains stationary at a particular position 01281 (which is the pivot point). While moving the object (e.g. the tool) the 01282 object's sensor moves along a circular arc (or in three dimensions on a 01283 calotte of a sphere). Now, in a first step, the center point (= pivot 01284 point) as well as the radius of this sphere are estimated by a simple 01285 least square scheme (that's done in global coordinates). 01286 01287 Provided the object is still positioned as described above (e.g. the tool 01288 tip still is stationary), using a single measurement (R_i, t_i) the 01289 global pivot point p' can be easily transformed into a local coordinate 01290 p via the relation 01291 01292 p' = R * p + t ==> p = R^(-1) * (p' - t) 01293 01294 where R is the rotation and t the location of the sensor in the global 01295 coordinate system. However, due to noise, various results for p should 01296 be averaged. 01297 01298 Implementation details: 01299 01300 Instead of recording a new set of pairs of (R_i, t_i) after having 01301 determined the virtual sphere, the former measurement is used. 01302 01303 */ 01304 01305 // Estimate sphere parameters 01306 01307 FitSphere<T> fitSphere(locations); 01308 fitSphere.compute(); 01309 global_pivot_point = fitSphere.getCenterPoint().toArray(); 01310 01311 // Compute the local pivot point 01312 01313 local_pivot_point = Vector3T(0, 0, 0); 01314 01315 const int n = rotations.size(); 01316 01317 for (int i = 0; i < n; ++i) 01318 { 01319 Vector3T differenceVectorLocal = rotations[i].inverse() * (global_pivot_point - locations[i]); 01320 local_pivot_point += differenceVectorLocal; // average (part 1) 01321 } 01322 01323 local_pivot_point = local_pivot_point / n; // average (part 2) 01324 01325 // Compute the RMSE 01326 01327 using std::sqrt; 01328 rmse = T(0); 01329 01330 for (int i = 0; i < n; ++i) 01331 { 01332 Vector3T noisy_center_point = rotations[i] * local_pivot_point + locations[i]; 01333 rmse += (noisy_center_point - global_pivot_point).squaredNorm(); 01334 } 01335 01336 rmse = sqrt(rmse / n); 01337 01338 return rmse; 01339 } 01340 01341 01342 template <class T> 01343 const typename PivotCalibrationTwoStep<T>::Vector3T & PivotCalibrationTwoStep<T>::getLocalPivotPoint() const 01344 { 01345 return local_pivot_point; 01346 } 01347 01348 01349 template <class T> 01350 size_t PivotCalibrationTwoStep<T>::getNumberItemsRequired() const 01351 { 01352 return 4; 01353 } 01354 01355 01356 template <class T> 01357 const typename PivotCalibrationTwoStep<T>::Vector3T & PivotCalibrationTwoStep<T>::getPivotPoint() const 01358 { 01359 return global_pivot_point; 01360 } 01361 01362 01363 template <class T> 01364 T PivotCalibrationTwoStep<T>::getRMSE() const 01365 { 01366 return rmse; 01367 } 01368 01369 01370 template <class T> 01371 void PivotCalibrationTwoStep<T>::setLocations(Range<Vector3T> locations) 01372 { 01373 this->locations.resize(locations.size()); 01374 size_t i = 0; 01375 01376 while(!locations.isDone()) 01377 { 01378 this->locations[i++] = locations.currentItem(); 01379 locations.next(); 01380 } 01381 } 01382 01383 01384 template <class T> 01385 void PivotCalibrationTwoStep<T>::setRotations(Range<Matrix3T> rotations) 01386 { 01387 this->rotations.resize(rotations.size()); 01388 size_t i = 0; 01389 01390 while(!rotations.isDone()) 01391 { 01392 this->rotations[i++] = rotations.currentItem(); 01393 rotations.next(); 01394 } 01395 } 01396 01397 01398 } // namespace TRTK 01399 01400 01401 #endif // PIVOTCALIBRATION_HPP_3123933941
Documentation generated by Doxygen