1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Hand-eye calibration.
33  *
34  * Authors:
35  * Francois Chaumette
36  * Fabien Spindler
37  *
38  *****************************************************************************/
39 
40 #include <cmath>  // std::fabs
41 #include <limits> // numeric_limits
42 
43 #include <visp3/vision/vpHandEyeCalibration.h>
44 
45 #define DEBUG_LEVEL1 0
46 #define DEBUG_LEVEL2 0
47 
48 /*!
49   \brief Compute the distances of the data to the mean obtained.
50 
51   \param[in] cMo : Vector of homogeneous matrices representing the transformation
52   between the camera and the scene.
53   \param[in] rMe : Vector of homogeneous matrices representing the transformation
54   between the effector (where the camera is fixed) and the reference
55   coordinates (base of the manipulator). Must be the same size as cMo.
56   \param[in] eMc : Homogeneous matrix between the effector and the camera.
57 */
calibrationVerifrMo(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,const vpHomogeneousMatrix & eMc)58 void vpHandEyeCalibration::calibrationVerifrMo(const std::vector<vpHomogeneousMatrix> &cMo, const std::vector<vpHomogeneousMatrix> &rMe, const vpHomogeneousMatrix &eMc)
59 {
60   unsigned int nbPose = (unsigned int) cMo.size();
61   std::vector<vpTranslationVector> rTo(nbPose);
62   std::vector<vpRotationMatrix> rRo(nbPose);
63 
64   for (unsigned int i = 0; i < nbPose; i++) {
65     vpHomogeneousMatrix rMo = rMe[i] * eMc * cMo[i];
66     rRo[i] = rMo.getRotationMatrix();
67     rTo[i] = rMo.getTranslationVector();
68   }
69   vpRotationMatrix meanRot = vpRotationMatrix::mean(rRo);
70   vpTranslationVector meanTrans = vpTranslationVector::mean(rTo);
71 
72 #if DEBUG_LEVEL2
73   {
74     std::cout << "Mean  " << std::endl;
75     std::cout << "Translation: " << meanTrans.t() << std::endl;
76     vpThetaUVector P(meanRot);
77     std::cout << "Rotation : theta (deg) = " << vpMath::deg(sqrt(P.sumSquare())) << " Matrice : " << std::endl << meanRot  << std::endl;
78     std::cout << "theta U (deg): " << vpMath::deg(P[0]) << " " << vpMath::deg(P[1]) << " " << vpMath::deg(P[2]) << std::endl;
79   }
80 #endif
81 
82   // standard deviation, rotational part
83   double resRot = 0.0;
84   for (unsigned int i = 0; i < nbPose; i++) {
85     vpRotationMatrix R = meanRot.t() * rRo[i]; // Rm^T  Ri
86     vpThetaUVector P(R);
87     // theta = Riemannian distance d(Rm,Ri)
88     double theta = sqrt(P.sumSquare());
89     std::cout << "Distance theta between rMo(" << i << ") and mean (deg) = " << vpMath::deg(theta) << std::endl;
90     // Euclidean distance d(Rm,Ri) not used
91     // theta = 2.0*sqrt(2.0)*sin(theta/2.0);
92     resRot += theta*theta;
93   }
94   resRot = sqrt(resRot/nbPose);
95   std::cout << "Mean residual rMo(" << nbPose << ") - rotation (deg) = " << vpMath::deg(resRot) << std::endl;
96   // standard deviation, translational part
97   double resTrans = 0.0;
98   for (unsigned int i = 0; i < nbPose; i++) {
99     vpColVector errTrans = ((vpColVector) rTo[i]) - meanTrans;
100     resTrans += errTrans.sumSquare();
101     std::cout << "Distance d between rMo(" << i << ") and mean (m) = " << sqrt(errTrans.sumSquare()) << std::endl;
102   }
103   resTrans = sqrt(resTrans/nbPose);
104   std::cout << "Mean residual rMo(" << nbPose << ") - translation (m) = " << resTrans << std::endl;
105   double resPos = (resRot*resRot + resTrans*resTrans)*nbPose;
106   resPos = sqrt(resPos/(2*nbPose));
107   std::cout << "Mean residual rMo(" << nbPose << ") - global = " << resPos << std::endl;
108 }
109 
110 /*!
111   \brief Compute the rotation part (eRc) of hand-eye pose by solving a
112   Procrustes problem [... (theta u)_e ...] = eRc [ ... (theta u)_c ...]
113 
114   \param[in] cMo : Vector of homogeneous matrices representing the transformation
115   between the camera and the scene (input)
116   \param[in] rMe : Vector of homogeneous matrices representing the transformation
117   between the effector (where the camera is fixed) and the reference
118   coordinates (base of the manipulator) (input). Must be the same size as cMo.
119   \param[out] eRc : Rotation matrix  between the effector and the camera (output)
120 */
calibrationRotationProcrustes(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpRotationMatrix & eRc)121 int vpHandEyeCalibration::calibrationRotationProcrustes(const std::vector<vpHomogeneousMatrix> &cMo, const std::vector<vpHomogeneousMatrix> &rMe,vpRotationMatrix &eRc)
122 {
123   // Method by solving the orthogonal Procrustes problem
124   // [... (theta u)_e ...] = eRc [ ... (theta u)_c ...]
125   // similar to E^T = eRc C^T below
126 
127   vpMatrix Et,Ct;
128   vpMatrix A;
129   unsigned int k = 0;
130   unsigned int nbPose = (unsigned int) cMo.size();
131 
132   // for all couples ij
133   for (unsigned int i = 0; i < nbPose; i++) {
134     vpRotationMatrix rRei, ciRo;
135     rMe[i].extract(rRei);
136     cMo[i].extract(ciRo);
137     // std::cout << "rMei: " << std::endl << rMe[i] << std::endl;
138 
139     for (unsigned int j = 0; j < nbPose; j++) {
140       if (j > i) // we don't use two times same couples...
141       {
142         vpRotationMatrix rRej, cjRo;
143         rMe[j].extract(rRej);
144         cMo[j].extract(cjRo);
145         // std::cout << "rMej: " << std::endl << rMe[j] << std::endl;
146 
147         vpRotationMatrix ejRei = rRej.t() * rRei;
148         vpThetaUVector ejPei(ejRei);
149         vpColVector xe = ejPei;
150 
151         vpRotationMatrix cjRci = cjRo * ciRo.t();
152         vpThetaUVector cjPci(cjRci);
153         vpColVector xc = cjPci;
154 
155         if (k == 0) {
156           Et = xe.t();
157           Ct = xc.t();
158         } else {
159           Et.stack(xe.t());
160           Ct.stack(xc.t());
161         }
162         k++;
163       }
164     }
165   }
166   // std::cout << "Et "  << std::endl << Et << std::endl;
167   // std::cout << "Ct "  << std::endl << Ct << std::endl;
168 
169   // R obtained from the SVD of (E C^T) with all singular values equal to 1
170   A = Et.t() * Ct;
171   vpMatrix M, U, V;
172   vpColVector sv;
173   int rank = A.pseudoInverse(M, sv, 1e-6, U, V);
174   if (rank != 3) return -1;
175   A = U * V.t();
176   eRc = vpRotationMatrix(A);
177 
178 #if DEBUG_LEVEL2
179   {
180     vpThetaUVector ePc(eRc);
181     std::cout << "Rotation from Procrustes method " << std::endl;
182     std::cout << "theta U (deg): " << vpMath::deg(ePc[0]) << " " << vpMath::deg(ePc[1]) << " " << vpMath::deg(ePc[2]) << std::endl;
183     // Residual
184     vpMatrix residual;
185     residual = A * Ct.t() - Et.t();
186     //  std::cout << "Residual: " << std::endl << residual << std::endl;
187     double res = sqrt(residual.sumSquare()/(residual.getRows()*residual.getCols()));
188     printf("Mean residual (rotation) = %lf\n",res);
189   }
190 #endif
191   return 0;
192 }
193 
194 /*!
195   \brief Compute the rotation part (eRc) of hand-eye pose by solving a
196   linear system using R. Tsai and R. Lorenz method
197   \cite Tsai89a.
198 
199   \param[in] cMo : Vector of homogeneous matrices representing the transformation
200   between the camera and the scene (input)
201   \param[in] rMe : Vector of homogeneous matrices representing the transformation
202   between the effector (where the camera is fixed) and the reference
203   coordinates (base of the manipulator) (input). Must be the same size as cMo.
204   \param[out] eRc : Rotation matrix  between the effector and the camera (output)
205 */
calibrationRotationTsai(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpRotationMatrix & eRc)206 int vpHandEyeCalibration::calibrationRotationTsai(const std::vector<vpHomogeneousMatrix> &cMo, const std::vector<vpHomogeneousMatrix> &rMe,vpRotationMatrix &eRc)
207 {
208   vpMatrix A;
209   vpColVector B;
210   unsigned int nbPose = (unsigned int) cMo.size();
211   unsigned int k = 0;
212   // for all couples ij
213   for (unsigned int i = 0; i < nbPose; i++) {
214     vpRotationMatrix rRei, ciRo;
215     rMe[i].extract(rRei);
216     cMo[i].extract(ciRo);
217     // std::cout << "rMei: " << std::endl << rMe[i] << std::endl;
218 
219     for (unsigned int j = 0; j < nbPose; j++) {
220       if (j > i) // we don't use two times same couples...
221       {
222         vpRotationMatrix rRej, cjRo;
223         rMe[j].extract(rRej);
224         cMo[j].extract(cjRo);
225         // std::cout << "rMej: " << std::endl << rMe[j] << std::endl;
226 
227         vpRotationMatrix ejRei = rRej.t() * rRei;
228         vpThetaUVector ejPei(ejRei);
229 
230         vpRotationMatrix cjRci = cjRo * ciRo.t();
231         vpThetaUVector cjPci(cjRci);
232         // std::cout << "theta U (camera) " << cjPci.t() << std::endl;
233 
234         vpMatrix As;
235         vpColVector b(3);
236 
237         As = vpColVector::skew(vpColVector(ejPei) + vpColVector(cjPci));
238 
239         b =  (vpColVector)cjPci - (vpColVector) ejPei; // A.40
240 
241         if (k == 0) {
242           A = As;
243           B = b;
244         } else {
245           A = vpMatrix::stack(A, As);
246           B = vpColVector::stack(B, b);
247         }
248         k++;
249       }
250     }
251   }
252 #if DEBUG_LEVEL2
253   {
254     std::cout << "Tsai method: system A X = B "  << std::endl;
255     std::cout << "A "  << std::endl << A << std::endl;
256     std::cout << "B "  << std::endl << B << std::endl;
257   }
258 #endif
259   vpMatrix Ap;
260   // the linear system A x = B is solved
261   // using x = A^+ B
262 
263   int rank = A.pseudoInverse(Ap);
264   if (rank != 3) return -1;
265 
266   vpColVector x = Ap * B;
267 
268   // extraction of theta U
269 
270   // x = tan(theta/2) U
271   double norm =  x.sumSquare();
272   double c = 1 / sqrt(1 + norm);  // cos(theta/2)
273   double alpha = acos(c);         // theta/2
274   norm = 2.0*c/vpMath::sinc(alpha);  // theta / tan(theta/2)
275   for (unsigned int i = 0; i < 3; i++) x[i] *= norm;
276 
277   // Building of the rotation matrix eRc
278   vpThetaUVector xP(x[0], x[1], x[2]);
279   eRc = vpRotationMatrix(xP);
280 
281 #if DEBUG_LEVEL2
282   {
283     std::cout << "Rotation from Tsai method" << std::endl;
284     std::cout << "theta U (deg): " << vpMath::deg(x[0]) << " " << vpMath::deg(x[1]) << " " << vpMath::deg(x[2]) << std::endl;
285     // Residual
286     for (unsigned int i = 0; i < 3; i++) x[i] /= norm; /* original x */
287     vpColVector residual;
288     residual = A*x-B;
289     // std::cout << "Residual: " << std::endl << residual << std::endl;
290     double res = sqrt(residual.sumSquare()/residual.getRows());
291     printf("Mean residual (rotation) = %lf\n",res);
292   }
293 #endif
294   return 0;
295 }
296 
297 /*!
298   \brief Old ViSP implementation for computing the rotation part (eRc)
299   of hand-eye pose by solving a linear system using R. Tsai and R. Lorenz method
300   \cite Tsai89a.
301 
302   \param[in] cMo : Vector of homogeneous matrices representing the transformation
303   between the camera and the scene (input)
304   \param[in] rMe : Vector of homogeneous matrices representing the transformation
305   between the effector (where the camera is fixed) and the reference
306   coordinates (base of the manipulator) (input). Must be the same size as cMo.
307   \param[ou] eRc : Rotation matrix  between the effector and the camera (output)
308 */
calibrationRotationTsaiOld(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpRotationMatrix & eRc)309 int vpHandEyeCalibration::calibrationRotationTsaiOld(const std::vector<vpHomogeneousMatrix> &cMo, const std::vector<vpHomogeneousMatrix> &rMe,vpRotationMatrix &eRc)
310 {
311   unsigned int nbPose = (unsigned int) cMo.size();
312   vpMatrix A;
313   vpColVector B;
314   vpColVector x;
315   unsigned int k = 0;
316   // for all couples ij
317   for (unsigned int i = 0; i < nbPose; i++) {
318     vpRotationMatrix rRei, ciRo;
319     rMe[i].extract(rRei);
320     cMo[i].extract(ciRo);
321     // std::cout << "rMei: " << std::endl << rMe[i] << std::endl;
322 
323     for (unsigned int j = 0; j < nbPose; j++) {
324       if (j > i) { // we don't use two times same couples...
325         vpRotationMatrix rRej, cjRo;
326         rMe[j].extract(rRej);
327         cMo[j].extract(cjRo);
328         // std::cout << "rMej: " << std::endl << rMe[j] << std::endl;
329 
330         vpRotationMatrix rReij = rRej.t() * rRei;
331 
332         vpRotationMatrix cijRo = cjRo * ciRo.t();
333 
334         vpThetaUVector rPeij(rReij);
335 
336         double theta = sqrt(rPeij[0] * rPeij[0] + rPeij[1] * rPeij[1] + rPeij[2] * rPeij[2]);
337 
338         // std::cout << i << " " << j << " " << "ejRei: " << std::endl << rReij << std::endl;
339         // std::cout << "theta (robot) " << theta << std::endl;
340         // std::cout << "theta U (robot) " << rPeij << std::endl;
341         // std::cout << "cjRci: " << std::endl << cijRo.t() << std::endl;
342 
343         for (unsigned int m = 0; m < 3; m++) {
344           rPeij[m] = rPeij[m] * vpMath::sinc(theta / 2);
345         }
346 
347         vpThetaUVector cijPo(cijRo);
348         theta = sqrt(cijPo[0] * cijPo[0] + cijPo[1] * cijPo[1] + cijPo[2] * cijPo[2]);
349         for (unsigned int m = 0; m < 3; m++) {
350           cijPo[m] = cijPo[m] * vpMath::sinc(theta / 2);
351         }
352 
353         // std::cout << "theta (camera) " << theta << std::endl;
354         // std::cout << "theta U (camera) " << cijPo.t() << std::endl;
355 
356         vpMatrix As;
357         vpColVector b(3);
358 
359         As = vpColVector::skew(vpColVector(rPeij) + vpColVector(cijPo));
360 
361         b = (vpColVector)cijPo - (vpColVector)rPeij; // A.40
362 
363         if (k == 0) {
364           A = As;
365           B = b;
366         } else {
367           A = vpMatrix::stack(A, As);
368           B = vpColVector::stack(B, b);
369         }
370         k++;
371       }
372     }
373   }
374 
375   // std::cout << "A "  << std::endl << A << std::endl;
376   // std::cout << "B "  << std::endl << B << std::endl;
377 
378   // the linear system is defined
379   // x = AtA^-1AtB is solved
380   vpMatrix AtA = A.AtA();
381 
382   vpMatrix Ap;
383   int rank = AtA.pseudoInverse(Ap, 1e-6);
384   if (rank != 3) return -1;
385 
386   x = Ap * A.t() * B;
387   vpColVector x2 = x; /* pour calcul residu */
388 
389   //     {
390   //       // Residual
391   //       vpColVector residual;
392   //       residual = A*x-B;
393   //       std::cout << "Residual: " << std::endl << residual << std::endl;
394 
395   //       double res = 0;
396   //       for (int i=0; i < residual.getRows(); i++)
397   // 	res += residual[i]*residual[i];
398   //       res = sqrt(res/residual.getRows());
399   //       printf("Mean residual = %lf\n",res);
400   //     }
401 
402   // extraction of theta and U
403   double theta;
404   double d = x.sumSquare();
405   for (unsigned int i = 0; i < 3; i++)
406     x[i] = 2 * x[i] / sqrt(1 + d);
407   theta = sqrt(x.sumSquare()) / 2;
408   theta = 2 * asin(theta);
409   // if (theta !=0)
410   if (std::fabs(theta) > std::numeric_limits<double>::epsilon()) {
411     for (unsigned int i = 0; i < 3; i++)
412       x[i] *= theta / (2 * sin(theta / 2));
413   } else
414     x = 0;
415 
416   // Building of the rotation matrix eRc
417   vpThetaUVector xP(x[0], x[1], x[2]);
418   eRc = vpRotationMatrix(xP);
419 
420 #if DEBUG_LEVEL2
421   {
422     std::cout << "Rotation from Old Tsai method" << std::endl;
423     std::cout << "theta U (deg): " << vpMath::deg(x[0]) << " " << vpMath::deg(x[1]) << " " << vpMath::deg(x[2]) << std::endl;
424     // Residual
425     vpColVector residual;
426     residual = A*x2-B;
427     // std::cout << "Residual: " << std::endl << residual << std::endl;
428     double res = sqrt(residual.sumSquare()/residual.getRows());
429     printf("Mean residual (rotation) = %lf\n",res);
430   }
431 #endif
432   return 0;
433 }
434 
435 /*!
436   \brief Compute the translation part (eTc) of hand-eye pose by solving a
437   linear system (see for instance R. Tsai and R. Lorenz method)
438   \cite Tsai89a.
439 
440   \param[in] cMo : Vector of homogeneous matrices representing the transformation
441   between the camera and the scene (input)
442   \param[in] rMe : Vector of homogeneous matrices representing the transformation
443   between the effector (where the camera is fixed) and the reference
444   coordinates (base of the manipulator) (input). Must be the same size as cMo.
445   \param[out] eRc : Rotation matrix  between the effector and the camera (input)
446   \param[out] eTc : Translation  between the effector and the camera (output)
447 */
calibrationTranslation(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpRotationMatrix & eRc,vpTranslationVector & eTc)448 int vpHandEyeCalibration::calibrationTranslation(const std::vector<vpHomogeneousMatrix> &cMo,
449                                                  const std::vector<vpHomogeneousMatrix> &rMe,
450                                                  vpRotationMatrix &eRc,
451                                                  vpTranslationVector &eTc)
452 {
453   vpMatrix I3(3,3);
454   I3.eye();
455   unsigned int k = 0;
456   unsigned int nbPose = (unsigned int)cMo.size();
457   vpMatrix A(3*nbPose,3);
458   vpColVector B(3*nbPose);
459   // Building of the system for the translation estimation
460   // for all couples ij
461   for (unsigned int i = 0; i < nbPose; i++) {
462     for (unsigned int j = 0; j < nbPose; j++) {
463       if (j > i) { // we don't use two times same couples...
464         vpHomogeneousMatrix ejMei = rMe[j].inverse() * rMe[i];
465         vpHomogeneousMatrix cjMci = cMo[j] * cMo[i].inverse();
466 
467         vpRotationMatrix ejRei, cjRci;
468         vpTranslationVector ejTei, cjTci;
469 
470         ejMei.extract(ejRei);
471         ejMei.extract(ejTei);
472 
473         cjMci.extract(cjRci);
474         cjMci.extract(cjTci);
475 
476         vpMatrix a = vpMatrix(ejRei) - I3;
477         vpTranslationVector b = eRc * cjTci - ejTei;
478 
479         if (k == 0) {
480           A = a;
481           B = b;
482         } else {
483           A = vpMatrix::stack(A, a);
484           B = vpColVector::stack(B, b);
485         }
486         k++;
487       }
488     }
489   }
490 
491   // the linear system A x = B is solved
492   // using x = A^+ B
493   vpMatrix Ap;
494   int rank = A.pseudoInverse(Ap);
495   if (rank != 3) return -1;
496 
497   vpColVector x = Ap * B;
498   eTc = (vpTranslationVector) x;
499 
500 #if DEBUG_LEVEL2
501   {
502     printf("New Hand-eye calibration : ");
503     std::cout << "Translation: " << eTc[0] << " " << eTc[1] << " " << eTc[2] << std::endl;
504     // residual
505     vpColVector residual;
506     residual = A*x-B;
507     // std::cout << "Residual: " << std::endl << residual << std::endl;
508     double res = sqrt(residual.sumSquare()/residual.getRows());
509     printf("Mean residual (translation) = %lf\n",res);
510   }
511 #endif
512   return 0;
513 }
514 
515 /*!
516   \brief Old method to compute the translation part (eTc) of hand-eye pose
517   by solving a linear system (see for instance R. Tsai and R. Lorenz method)
518   \cite Tsai89a.
519 
520   \param[in] cMo : vector of homogeneous matrices representing the transformation
521   between the camera and the scene (input)
522   \param[in] rMe : vector of homogeneous matrices representing the transformation
523   between the effector (where the camera is fixed) and the reference
524   coordinates (base of the manipulator) (input). Must be the same size as cMo.
525   \param[out] eRc : rotation matrix  between the effector and the camera (input)
526   \param[out] eTc : translation  between the effector and the camera (output)
527 */
calibrationTranslationOld(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpRotationMatrix & eRc,vpTranslationVector & eTc)528 int vpHandEyeCalibration::calibrationTranslationOld(const std::vector<vpHomogeneousMatrix> &cMo,
529                                                     const std::vector<vpHomogeneousMatrix> &rMe,
530                                                     vpRotationMatrix &eRc,
531                                                     vpTranslationVector &eTc)
532 {
533   vpMatrix A;
534   vpColVector B;
535   // Building of the system for the translation estimation
536   // for all couples ij
537   vpRotationMatrix I3;
538   I3.eye();
539   int k = 0;
540   unsigned int nbPose = (unsigned int)cMo.size();
541 
542   for (unsigned int i = 0; i < nbPose; i++) {
543     vpRotationMatrix rRei, ciRo;
544     vpTranslationVector rTei, ciTo;
545     rMe[i].extract(rRei);
546     cMo[i].extract(ciRo);
547     rMe[i].extract(rTei);
548     cMo[i].extract(ciTo);
549 
550     for (unsigned int j = 0; j < nbPose; j++) {
551       if (j > i) // we don't use two times same couples...
552       {
553 
554         vpRotationMatrix rRej, cjRo;
555         rMe[j].extract(rRej);
556         cMo[j].extract(cjRo);
557 
558         vpTranslationVector rTej, cjTo;
559         rMe[j].extract(rTej);
560         cMo[j].extract(cjTo);
561 
562         vpRotationMatrix rReij = rRej.t() * rRei;
563 
564         vpTranslationVector rTeij = rTej + (-rTei);
565 
566         rTeij = rRej.t() * rTeij;
567 
568         vpMatrix a = vpMatrix(rReij) - vpMatrix(I3);
569 
570         vpTranslationVector b;
571         b = eRc * cjTo - rReij * eRc * ciTo + rTeij;
572 
573         if (k == 0) {
574           A = a;
575           B = b;
576         } else {
577           A = vpMatrix::stack(A, a);
578           B = vpColVector::stack(B, b);
579         }
580         k++;
581       }
582     }
583   }
584 
585   // the linear system is solved
586   // x = AtA^-1AtB is solved
587   vpMatrix AtA = A.AtA();
588   vpMatrix Ap;
589   vpColVector AeTc;
590   int rank = AtA.pseudoInverse(Ap, 1e-6);
591   if (rank != 3) return -1;
592 
593   AeTc = Ap * A.t() * B;
594   eTc = (vpTranslationVector) AeTc;
595 
596 #if DEBUG_LEVEL2
597   {
598     printf("Old Hand-eye calibration : ");
599     std::cout << "Translation: " << eTc[0] << " " << eTc[1] << " " << eTc[2] << std::endl;
600 
601     // residual
602     vpColVector residual;
603     residual = A*AeTc-B;
604     // std::cout << "Residual: " << std::endl << residual << std::endl;
605     double res = 0;
606     for (unsigned int i=0; i < residual.getRows(); i++)
607       res += residual[i]*residual[i];
608     res = sqrt(res/residual.getRows());
609     printf("Mean residual (translation) = %lf\n",res);
610   }
611 #endif
612   return 0;
613 }
614 
615 /*!
616   \brief Compute the set of errors minimised by VVS.
617 
618   \param[in] cMo : vector of homogeneous matrices representing the transformation
619   between the camera and the scene.
620   \param[in] rMe : vector of homogeneous matrices representing the transformation
621   between the effector (where the camera is fixed) and the reference
622   coordinates (base of the manipulator). Must be the same size as cMo.
623   \param[in] eMc : homogeneous matrix between the effector and the camera (input)
624   \param[out] err: set of errors minimised by VVS (3 for rotation, 3 for translation, etc.) (output)
625 */
626 
calibrationErrVVS(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,const vpHomogeneousMatrix & eMc,vpColVector & errVVS)627 double vpHandEyeCalibration::calibrationErrVVS(const std::vector<vpHomogeneousMatrix> &cMo, const std::vector<vpHomogeneousMatrix> &rMe,
628                                                const vpHomogeneousMatrix &eMc, vpColVector &errVVS)
629 {
630   unsigned int nbPose = (unsigned int) cMo.size();
631   vpMatrix I3(3,3);
632   I3.eye();
633   vpRotationMatrix eRc;
634   vpTranslationVector eTc;
635   eMc.extract(eRc);
636   eMc.extract(eTc);
637 
638   unsigned int k = 0;
639   for (unsigned int i = 0; i < nbPose; i++) {
640     for (unsigned int j = 0; j < nbPose; j++) {
641       if (j > i) // we don't use two times same couples...
642       {
643         vpColVector s(3);
644 
645         vpHomogeneousMatrix ejMei = rMe[j].inverse() * rMe[i];
646         vpHomogeneousMatrix cjMci = cMo[j] * cMo[i].inverse();
647 
648         vpRotationMatrix ejRei, cjRci;
649         vpTranslationVector ejTei, cjTci;
650 
651         ejMei.extract(ejRei);
652         vpThetaUVector ejPei(ejRei);
653         ejMei.extract(ejTei);
654 
655         cjMci.extract(cjRci);
656         vpThetaUVector cjPci(cjRci);
657         cjMci.extract(cjTci);
658         // terms due to rotation
659         s = vpMatrix(eRc) * vpColVector(cjPci) - vpColVector(ejPei);
660         if (k == 0) {
661           errVVS = s;
662         } else {
663           errVVS = vpColVector::stack(errVVS, s);
664         }
665         k++;
666         // terms due to translation
667         s = (vpMatrix(ejRei) - I3) * eTc - eRc * cjTci + ejTei;
668         errVVS = vpColVector::stack(errVVS, s);
669       } // enf if i > j
670     } // end for j
671   } // end for i
672 
673   double resRot, resTrans, resPos;
674   resRot = resTrans = resPos = 0.0;
675   for (unsigned int i=0; i < (unsigned int) errVVS.size() ; i += 6)
676   {
677     resRot += errVVS[i]*errVVS[i];
678     resRot += errVVS[i+1]*errVVS[i+1];
679     resRot += errVVS[i+2]*errVVS[i+2];
680     resTrans += errVVS[i+3]*errVVS[i+3];
681     resTrans += errVVS[i+4]*errVVS[i+4];
682     resTrans += errVVS[i+5]*errVVS[i+5];
683   }
684   resPos = resRot + resTrans;
685   resRot = sqrt(resRot*2/errVVS.size());
686   resTrans = sqrt(resTrans*2/errVVS.size());
687   resPos = sqrt(resPos/errVVS.size());
688 #if DEBUG_LEVEL1
689   {
690     printf("Mean VVS residual - rotation (deg) = %lf\n",vpMath::deg(resRot));
691     printf("Mean VVS residual - translation = %lf\n",resTrans);
692     printf("Mean VVS residual - global = %lf\n",resPos);
693   }
694 #endif
695   return resPos;
696 }
697 
698 /*!
699   \brief Hand-Eye Calibration by VVS.
700 
701   \param[in] cMo : vector of homogeneous matrices representing the transformation
702   between the camera and the scene.
703   \param[in] rMe : vector of homogeneous matrices representing the transformation
704   between the effector (where the camera is fixed) and the reference
705   coordinates (base of the manipulator). Must be the same size as cMo.
706   \param[in,out] eMc : homogeneous matrix between the effector and the camera.
707 */
708 #define NB_ITER_MAX 30
709 
calibrationVVS(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpHomogeneousMatrix & eMc)710 int vpHandEyeCalibration::calibrationVVS(const std::vector<vpHomogeneousMatrix> &cMo,
711                                          const std::vector<vpHomogeneousMatrix> &rMe,
712                                          vpHomogeneousMatrix &eMc)
713 {
714   unsigned int it = 0;
715   double res = 1.0;
716   unsigned int nbPose = (unsigned int) cMo.size();
717   vpColVector err;
718   vpMatrix L;
719   vpMatrix I3(3,3);
720   I3.eye();
721   vpRotationMatrix eRc;
722   vpTranslationVector eTc;
723   eMc.extract(eRc);
724   eMc.extract(eTc);
725 
726   /* FC : on recalcule 2 fois tous les ejMei et cjMci a chaque iteration
727     alors qu'ils sont constants. Ce serait sans doute mieux de les
728     calculer une seule fois et de les stocker. Pourraient alors servir
729     dans les autres fonctions HandEye. A voir si vraiment interessant vu la
730     combinatoire. Idem pour les theta u */
731   while ((res > 1e-7) && (it < NB_ITER_MAX))
732   {
733     /* compute s - s^* */
734     vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, err);
735     /* compute L_s */
736     unsigned int k = 0;
737     for (unsigned int i = 0; i < nbPose; i++) {
738       for (unsigned int j = 0; j < nbPose; j++) {
739         if (j > i) // we don't use two times same couples...
740         {
741           vpMatrix Ls(3,6),Lv(3,3),Lw(3,3);
742 
743           vpHomogeneousMatrix ejMei = rMe[j].inverse() * rMe[i];
744           vpHomogeneousMatrix cjMci = cMo[j] * cMo[i].inverse();
745 
746           vpRotationMatrix ejRei;
747           ejMei.extract(ejRei);
748           vpThetaUVector cjPci(cjMci);
749 
750           vpTranslationVector cjTci;
751 
752           cjMci.extract(cjTci);
753           // terms due to rotation
754           //Lv.diag(0.0); //
755           Lv = 0.0;
756           Lw = -vpMatrix(eRc) * vpColVector::skew(vpColVector(cjPci));
757           for (unsigned int m=0;m<3;m++)
758             for (unsigned int n=0;n<3;n++)
759             {
760               Ls[m][n] = Lv[m][n];
761               Ls[m][n+3] = Lw[m][n];
762             }
763           if (k == 0) {
764             L = Ls;
765           } else {
766             L = vpMatrix::stack(L,Ls);
767           }
768           k++;
769           // terms due to translation
770           Lv = (vpMatrix(ejRei) - I3) * vpMatrix(eRc);
771           Lw =  vpMatrix(eRc) * vpColVector::skew(vpColVector(cjTci));
772           for (unsigned int m=0;m<3;m++)
773             for (unsigned int n=0;n<3;n++)
774             {
775               Ls[m][n] = Lv[m][n];
776               Ls[m][n+3] = Lw[m][n];
777             }
778           L = vpMatrix::stack(L,Ls);
779 
780         } // enf if i > j
781       } // end for j
782     } // end for i
783     double lambda = 0.9;
784     vpMatrix Lp;
785     int rank = L.pseudoInverse(Lp);
786     if (rank != 6) return -1;
787 
788     vpColVector e = Lp * err;
789     vpColVector v = - e * lambda;
790     //  std::cout << "e: "  << e.t() << std::endl;
791     eMc = eMc * vpExponentialMap::direct(v);
792     eMc.extract(eRc);
793     eMc.extract(eTc);
794     res = sqrt(v.sumSquare()/v.getRows());
795     it++;
796   } // end while
797 #if DEBUG_LEVEL2
798   {
799     printf(" Iteration number for NL hand-eye minimisation : %d\n",it);
800     vpThetaUVector ePc(eRc);
801     std::cout << "theta U (deg): " << vpMath::deg(ePc[0]) << " " << vpMath::deg(ePc[1]) << " " << vpMath::deg(ePc[2]) << std::endl;
802     std::cout << "Translation: " << eTc[0] << " " << eTc[1] << " " << eTc[2] << std::endl;
803     // Residual
804     double res = err.sumSquare();
805     res = sqrt(res/err.getRows());
806     printf("Mean residual (rotation+translation) = %lf\n",res);
807   }
808 #endif
809   if (it == NB_ITER_MAX) return 1;  // VVS has not converged before NB_ITER_MAX
810   else return 0;
811 }
812 
813 #undef NB_ITER_MAX
814 
815 #define HE_I 0
816 #define HE_TSAI_OROT 1
817 #define HE_TSAI_ORNT 2
818 #define HE_TSAI_NROT 3
819 #define HE_TSAI_NRNT 4
820 #define HE_PROCRUSTES_OT 5
821 #define HE_PROCRUSTES_NT 6
822 
823 /*!
824   Compute extrinsic camera parameters : the constant transformation from
825   the effector to the camera frames (eMc).
826 
827   \param[in] cMo : vector of homogeneous matrices representing the transformation
828   between the camera and the scene.
829   \param[in] rMe : vector of homogeneous matrices representing the transformation
830   between the effector (where the camera is fixed) and the reference
831   coordinates (base of the manipulator). Must be the same size as cMo.
832   \param[out] eMc : homogeneous matrix representing the transformation
833   between the effector and the camera (output)
834 
835   \return 0 if calibration succeed, -1 if the system is not full rank, 1 if the algorithm doesn't converge.
836 */
calibrate(const std::vector<vpHomogeneousMatrix> & cMo,const std::vector<vpHomogeneousMatrix> & rMe,vpHomogeneousMatrix & eMc)837 int vpHandEyeCalibration::calibrate(const std::vector<vpHomogeneousMatrix> &cMo,
838                                     const std::vector<vpHomogeneousMatrix> &rMe, vpHomogeneousMatrix &eMc)
839 {
840   if (cMo.size() != rMe.size())
841     throw vpException(vpException::dimensionError, "cMo and rMe have different sizes");
842 
843   vpRotationMatrix eRc;
844   vpTranslationVector eTc;
845   vpColVector errVVS;
846   double resPos;
847 
848   /* initialisation of eMc to I in case all other methods fail */
849   eMc.eye();
850   resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
851   double vmin = resPos;  // will serve to determine the best method
852 #if DEBUG_LEVEL1
853   int He_method = HE_I;  // will serve to know which is the best method
854 #endif
855   vpHomogeneousMatrix eMcMin = eMc;  // best initial estimation for VSS
856   // Method using Old Tsai implementation
857   int err = vpHandEyeCalibration::calibrationRotationTsaiOld(cMo, rMe, eRc);
858   if (err != 0) printf("\n Problem in solving Hand-Eye Rotation by Old Tsai method \n");
859   else
860   {
861     eMc.insert(eRc);
862     err = vpHandEyeCalibration::calibrationTranslationOld(cMo, rMe, eRc, eTc);
863     if (err != 0) printf("\n Problem in solving Hand-Eye Translation by Old Tsai method after Old Tsai method for Rotation\n");
864     else
865     {
866       eMc.insert(eTc);
867 #if DEBUG_LEVEL1
868       {
869         printf("\nRotation by (old) Tsai, old implementation for translation\n");
870         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
871       }
872 #endif
873       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
874       if (resPos < vmin)
875       {
876         vmin = resPos;
877         eMcMin = eMc;
878 #if DEBUG_LEVEL1
879         He_method = HE_TSAI_OROT;
880 #endif
881       }
882     }
883     err = vpHandEyeCalibration::calibrationTranslation(cMo, rMe, eRc, eTc);
884     if (err != 0) printf("\n Problem in solving Hand-Eye Translation after Old Tsai method for Rotation\n");
885     else
886     {
887       eMc.insert(eTc);
888 #if DEBUG_LEVEL1
889       {
890         printf("\nRotation by (old) Tsai, new implementation for translation\n");
891         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
892       }
893 #endif
894       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
895       if (resPos < vmin)
896       {
897         vmin = resPos;
898         eMcMin = eMc;
899 #if DEBUG_LEVEL1
900         He_method = HE_TSAI_ORNT;
901 #endif
902       }
903     }
904   }
905   // First method using Tsai formulation
906   err = vpHandEyeCalibration::calibrationRotationTsai(cMo, rMe, eRc);
907   if (err != 0) printf("\n Problem in solving Hand-Eye Rotation by Tsai method \n");
908   else
909   {
910     eMc.insert(eRc);
911     err = vpHandEyeCalibration::calibrationTranslationOld(cMo, rMe, eRc, eTc);
912     if (err != 0) printf("\n Problem in solving Hand-Eye Translation by Old Tsai method after Tsai method for Rotation\n");
913     else
914     {
915       eMc.insert(eTc);
916 #if DEBUG_LEVEL1
917       {
918         printf("\nRotation by Tsai, old implementation for translation\n");
919         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
920       }
921 #endif
922       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
923       if (resPos < vmin)
924       {
925         vmin = resPos;
926         eMcMin = eMc;
927 #if DEBUG_LEVEL1
928         He_method = HE_TSAI_NROT;
929 #endif
930       }
931     }
932     err = vpHandEyeCalibration::calibrationTranslation(cMo, rMe, eRc, eTc);
933     if (err != 0) printf("\n Problem in solving Hand-Eye Translation after Tsai method for Rotation \n");
934     else
935     {
936       eMc.insert(eTc);
937 #if DEBUG_LEVEL1
938       {
939         printf("\nRotation by Tsai, new implementation for translation\n");
940         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
941       }
942 #endif
943       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
944       if (resPos < vmin)
945       {
946         vmin = resPos;
947         eMcMin = eMc;
948 #if DEBUG_LEVEL1
949         He_method = HE_TSAI_NRNT;
950 #endif
951       }
952     }
953   }
954   // Second method by solving the orthogonal Procrustes problem
955   err = vpHandEyeCalibration::calibrationRotationProcrustes(cMo, rMe, eRc);
956   if (err != 0) printf("\n Problem in solving Hand-Eye Rotation by Procrustes method \n");
957   else
958   {
959     eMc.insert(eRc);
960     err = vpHandEyeCalibration::calibrationTranslationOld(cMo, rMe, eRc, eTc);
961     if (err != 0) printf("\n Problem in solving Hand-Eye Translation by Old Tsai method after Procrustes method for Rotation\n");
962     else
963     {
964       eMc.insert(eTc);
965 #if DEBUG_LEVEL1
966       {
967         printf("\nRotation by Procrustes, old implementation for translation\n");
968         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
969       }
970 #endif
971       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
972       if (resPos < vmin)
973       {
974         vmin = resPos;
975         eMcMin = eMc;
976 #if DEBUG_LEVEL1
977         He_method = HE_PROCRUSTES_OT;
978 #endif
979       }
980     }
981     err = vpHandEyeCalibration::calibrationTranslation(cMo, rMe, eRc, eTc);
982     if (err != 0) printf("\n Problem in solving Hand-Eye Translation after Procrustes method for Rotation\n");
983     else
984     {
985       eMc.insert(eTc);
986 #if DEBUG_LEVEL1
987       {
988         printf("\nRotation by Procrustes, new implementation for translation\n");
989         vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
990       }
991 #endif
992       resPos = vpHandEyeCalibration::calibrationErrVVS(cMo, rMe, eMc, errVVS);
993       if (resPos < vmin)
994       {
995         eMcMin = eMc;
996 #if DEBUG_LEVEL1
997         vmin = resPos;
998         He_method = HE_PROCRUSTES_NT;
999 #endif
1000       }
1001     }
1002   }
1003 
1004   /* determination of the best method in case at least one succeeds */
1005   eMc = eMcMin;
1006 #if DEBUG_LEVEL1
1007   {
1008     if (He_method == HE_I) printf("Best method : I !!!, vmin = %lf\n",vmin);
1009     if (He_method == HE_TSAI_OROT) printf("Best method : TSAI_OROT, vmin = %lf\n",vmin);
1010     if (He_method == HE_TSAI_ORNT) printf("Best method : TSAI_ORNT, vmin = %lf\n",vmin);
1011     if (He_method == HE_TSAI_NROT) printf("Best method : TSAI_NROT, vmin = %lf\n",vmin);
1012     if (He_method == HE_TSAI_NRNT) printf("Best method : TSAI_NRNT, vmin = %lf\n",vmin);
1013     if (He_method == HE_PROCRUSTES_OT) printf("Best method : PROCRUSTES_OT, vmin = %lf\n",vmin);
1014     if (He_method == HE_PROCRUSTES_NT) printf("Best method : PROCRUSTES_NT, vmin = %lf\n",vmin);
1015     vpThetaUVector ePc(eMc);
1016     std::cout << "theta U (deg): " << vpMath::deg(ePc[0]) << " " << vpMath::deg(ePc[1]) << " " << vpMath::deg(ePc[2]) << std::endl;
1017     std::cout << "Translation: " << eMc[0][3] << " " << eMc[1][3] << " " << eMc[2][3] << std::endl;
1018   }
1019 #endif
1020 
1021   // Non linear iterative minimization to estimate simultaneouslty eRc and eTc
1022   err = vpHandEyeCalibration::calibrationVVS(cMo, rMe, eMc);
1023   // FC : err : 0 si tout OK, -1 si pb de rang, 1 si pas convergence
1024   if (err != 0) printf("\n Problem in solving Hand-Eye Calibration by VVS \n");
1025   else
1026   {
1027     printf("\nRotation and translation after VVS\n");
1028     vpHandEyeCalibration::calibrationVerifrMo(cMo, rMe, eMc);
1029   }
1030   return err;
1031 }
1032 
1033 #undef HE_I
1034 #undef HE_TSAI_OROT
1035 #undef HE_TSAI_ORNT
1036 #undef HE_TSAI_NROT
1037 #undef HE_TSAI_NRNT
1038 #undef HE_PROCRUSTES_OT
1039 #undef HE_PROCRUSTES_NT
1040 
1041 #undef DEBUG_LEVEL1
1042 #undef DEBUG_LEVEL2
1043