Visual Servoing Platform  version 3.6.1 under development (2024-12-17)
vpServo.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
4  *
5  * This software 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  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Visual servoing control law.
32  */
33 
34 
40 #include <sstream>
41 
42 #include <visp3/core/vpException.h>
43 #include <visp3/core/vpDebug.h>
44 #include <visp3/vs/vpServo.h>
45 
46 BEGIN_VISP_NAMESPACE
48  : L(), error(), J1(), J1p(), s(), sStar(), e1(), e(), q_dot(), v(), servoType(vpServo::NONE), rankJ1(0),
49  featureList(), desiredFeatureList(), featureSelectionList(), lambda(), signInteractionMatrix(1),
50  interactionMatrixType(DESIRED), inversionType(PSEUDO_INVERSE), cVe(), init_cVe(false), cVf(), init_cVf(false),
51  fVe(), init_fVe(false), eJe(), init_eJe(false), fJe(), init_fJe(false), errorComputed(false),
52  interactionMatrixComputed(false), dim_task(0), taskWasKilled(false), forceInteractionMatrixComputation(false),
53  WpW(), I_WpW(), P(), sv(), mu(4.), e1_initial(), iscJcIdentity(true), cJc(6, 6), m_first_iteration(true),
54  m_pseudo_inverse_threshold(1e-6)
55 {
56  cJc.eye();
57 }
58 
60  : L(), error(), J1(), J1p(), s(), sStar(), e1(), e(), q_dot(), v(), servoType(servo_type), rankJ1(0), featureList(),
61  desiredFeatureList(), featureSelectionList(), lambda(), signInteractionMatrix(1), interactionMatrixType(DESIRED),
62  inversionType(PSEUDO_INVERSE), cVe(), init_cVe(false), cVf(), init_cVf(false), fVe(), init_fVe(false), eJe(),
63  init_eJe(false), fJe(), init_fJe(false), errorComputed(false), interactionMatrixComputed(false), dim_task(0),
64  taskWasKilled(false), forceInteractionMatrixComputation(false), WpW(), I_WpW(), P(), sv(), mu(4), e1_initial(),
65  iscJcIdentity(true), cJc(6, 6), m_first_iteration(true)
66 {
67  cJc.eye();
68 }
69 
71 
73 {
74  // type of visual servoing
76 
77  // Twist transformation matrix
78  init_cVe = false;
79  init_cVf = false;
80  init_fVe = false;
81  // Jacobians
82  init_eJe = false;
83  init_fJe = false;
84 
85  dim_task = 0;
86 
87  featureList.clear();
88  desiredFeatureList.clear();
89  featureSelectionList.clear();
90 
92 
95 
97  errorComputed = false;
98 
99  taskWasKilled = false;
100 
102 
103  rankJ1 = 0;
104 
105  m_first_iteration = true;
106 }
107 
109 {
110  if (taskWasKilled == false) {
111  // kill the current and desired feature lists
112 
113  // current list
114  for (std::list<vpBasicFeature *>::iterator it = featureList.begin(); it != featureList.end(); ++it) {
115  if ((*it)->getDeallocate() == vpBasicFeature::vpServo) {
116  delete (*it);
117  (*it) = nullptr;
118  }
119  }
120  // desired list
121  for (std::list<vpBasicFeature *>::iterator it = desiredFeatureList.begin(); it != desiredFeatureList.end(); ++it) {
122  if ((*it)->getDeallocate() == vpBasicFeature::vpServo) {
123  delete (*it);
124  (*it) = nullptr;
125  }
126  }
127 
128  featureList.clear();
129  desiredFeatureList.clear();
130  taskWasKilled = true;
131  }
132 }
133 
134 void vpServo::setServo(const vpServoType &servo_type)
135 {
136  this->servoType = servo_type;
137 
140  else
142 
143  // when the control is directly compute in the camera frame
144  // we relieve the end-user to initialize cVa and aJe
145  if (servoType == EYEINHAND_CAMERA) {
147  set_cVe(_cVe);
148 
149  vpMatrix _eJe;
150  _eJe.eye(6);
151  set_eJe(_eJe);
152  };
153 }
154 
156 {
157  if (dof.size() == 6) {
158  iscJcIdentity = true;
159  for (unsigned int i = 0; i < 6; i++) {
160  if (std::fabs(dof[i]) > std::numeric_limits<double>::epsilon()) {
161  cJc[i][i] = 1.0;
162  }
163  else {
164  cJc[i][i] = 0.0;
165  iscJcIdentity = false;
166  }
167  }
168  }
169 }
170 
171 void vpServo::print(const vpServo::vpServoPrintType displayLevel, std::ostream &os)
172 {
173  switch (displayLevel) {
174  case vpServo::ALL: {
175  os << "Visual servoing task: " << std::endl;
176 
177  os << "Type of control law " << std::endl;
178  switch (servoType) {
179  case NONE:
180  os << "Type of task have not been chosen yet ! " << std::endl;
181  break;
182  case EYEINHAND_CAMERA:
183  os << "Eye-in-hand configuration " << std::endl;
184  os << "Control in the camera frame " << std::endl;
185  break;
186  case EYEINHAND_L_cVe_eJe:
187  os << "Eye-in-hand configuration " << std::endl;
188  os << "Control in the articular frame " << std::endl;
189  break;
190  case EYETOHAND_L_cVe_eJe:
191  os << "Eye-to-hand configuration " << std::endl;
192  os << "s_dot = _L_cVe_eJe q_dot " << std::endl;
193  break;
195  os << "Eye-to-hand configuration " << std::endl;
196  os << "s_dot = _L_cVe_fVe_eJe q_dot " << std::endl;
197  break;
198  case EYETOHAND_L_cVf_fJe:
199  os << "Eye-to-hand configuration " << std::endl;
200  os << "s_dot = _L_cVf_fJe q_dot " << std::endl;
201  break;
202  }
203 
204  os << "List of visual features : s" << std::endl;
205  std::list<vpBasicFeature *>::const_iterator it_s;
206  std::list<vpBasicFeature *>::const_iterator it_s_star;
207  std::list<unsigned int>::const_iterator it_select;
208 
209  for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end();
210  ++it_s, ++it_select) {
211  os << "";
212  (*it_s)->print((*it_select));
213  }
214 
215  os << "List of desired visual features : s*" << std::endl;
216  for (it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin();
217  it_s_star != desiredFeatureList.end(); ++it_s_star, ++it_select) {
218  os << "";
219  (*it_s_star)->print((*it_select));
220  }
221 
222  os << "Interaction Matrix Ls " << std::endl;
224  os << L << std::endl;
225  }
226  else {
227  os << "not yet computed " << std::endl;
228  }
229 
230  os << "Error vector (s-s*) " << std::endl;
231  if (errorComputed) {
232  os << error.t() << std::endl;
233  }
234  else {
235  os << "not yet computed " << std::endl;
236  }
237 
238  os << "Gain : " << lambda << std::endl;
239 
240  break;
241  }
242 
243  case vpServo::CONTROLLER: {
244  os << "Type of control law " << std::endl;
245  switch (servoType) {
246  case NONE:
247  os << "Type of task have not been chosen yet ! " << std::endl;
248  break;
249  case EYEINHAND_CAMERA:
250  os << "Eye-in-hand configuration " << std::endl;
251  os << "Control in the camera frame " << std::endl;
252  break;
253  case EYEINHAND_L_cVe_eJe:
254  os << "Eye-in-hand configuration " << std::endl;
255  os << "Control in the articular frame " << std::endl;
256  break;
257  case EYETOHAND_L_cVe_eJe:
258  os << "Eye-to-hand configuration " << std::endl;
259  os << "s_dot = _L_cVe_eJe q_dot " << std::endl;
260  break;
262  os << "Eye-to-hand configuration " << std::endl;
263  os << "s_dot = _L_cVe_fVe_eJe q_dot " << std::endl;
264  break;
265  case EYETOHAND_L_cVf_fJe:
266  os << "Eye-to-hand configuration " << std::endl;
267  os << "s_dot = _L_cVf_fJe q_dot " << std::endl;
268  break;
269  }
270  break;
271  }
272 
274  os << "List of visual features : s" << std::endl;
275 
276  std::list<vpBasicFeature *>::const_iterator it_s;
277  std::list<unsigned int>::const_iterator it_select;
278 
279  for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end();
280  ++it_s, ++it_select) {
281  os << "";
282  (*it_s)->print((*it_select));
283  }
284  break;
285  }
287  os << "List of desired visual features : s*" << std::endl;
288 
289  std::list<vpBasicFeature *>::const_iterator it_s_star;
290  std::list<unsigned int>::const_iterator it_select;
291 
292  for (it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin();
293  it_s_star != desiredFeatureList.end(); ++it_s_star, ++it_select) {
294  os << "";
295  (*it_s_star)->print((*it_select));
296  }
297  break;
298  }
299  case vpServo::GAIN: {
300  os << "Gain : " << lambda << std::endl;
301  break;
302  }
304  os << "Interaction Matrix Ls " << std::endl;
306  os << L << std::endl;
307  }
308  else {
309  os << "not yet computed " << std::endl;
310  }
311  break;
312  }
313 
315  case vpServo::MINIMUM:
316 
317  {
318  os << "Error vector (s-s*) " << std::endl;
319  if (errorComputed) {
320  os << error.t() << std::endl;
321  }
322  else {
323  os << "not yet computed " << std::endl;
324  }
325 
326  break;
327  }
328  }
329 }
330 
331 void vpServo::addFeature(vpBasicFeature &s_cur, vpBasicFeature &s_star, unsigned int select)
332 {
333  featureList.push_back(&s_cur);
334  desiredFeatureList.push_back(&s_star);
335  featureSelectionList.push_back(select);
336 }
337 
338 void vpServo::addFeature(vpBasicFeature &s_cur, unsigned int select)
339 {
340  featureList.push_back(&s_cur);
341 
342  // in fact we have a problem with s_star that is not defined
343  // by the end user.
344 
345  // If the same user want to compute the interaction at the desired position
346  // this "virtual feature" must exist
347 
348  // a solution is then to duplicate the current feature (s* = s)
349  // and reinitialized s* to 0
350 
351  // it remains the deallocation issue therefore a flag that stipulates that
352  // the feature has been allocated in vpServo is set
353 
354  // vpServo must deallocate the memory (see ~vpServo and kill() )
355 
356  vpBasicFeature *s_star;
357  s_star = s_cur.duplicate();
358 
359  s_star->init();
361 
362  desiredFeatureList.push_back(s_star);
363  featureSelectionList.push_back(select);
364 }
365 
366 unsigned int vpServo::getDimension() const
367 {
368  unsigned int dim = 0;
369  std::list<vpBasicFeature *>::const_iterator it_s;
370  std::list<unsigned int>::const_iterator it_select;
371 
372  for (it_s = featureList.begin(), it_select = featureSelectionList.begin(); it_s != featureList.end();
373  ++it_s, ++it_select) {
374  dim += (*it_s)->getDimension(*it_select);
375  }
376 
377  return dim;
378 }
379 
381  const vpServoInversionType &interactionMatrixInversion)
382 {
383  this->interactionMatrixType = interactionMatrix_type;
384  this->inversionType = interactionMatrixInversion;
385 }
386 
387 static void computeInteractionMatrixFromList(const std::list<vpBasicFeature *> &featureList,
388  const std::list<unsigned int> &featureSelectionList, vpMatrix &L)
389 {
390  if (featureList.empty()) {
391  vpERROR_TRACE("feature list empty, cannot compute Ls");
392  throw(vpServoException(vpServoException::noFeatureError, "feature list empty, cannot compute Ls"));
393  }
394 
395  /* The matrix dimension is not known before the affectation loop.
396  * It thus should be allocated on the flight, in the loop.
397  * The first assumption is that the size has not changed. A double
398  * reallocation (realloc(dim*2)) is done if necessary. In particular,
399  * [log_2(dim)+1] reallocations are done for the first matrix computation.
400  * If the allocated size is too large, a correction is done after the loop.
401  * The algorithmic cost is linear in affectation, logarithmic in allocation
402  * numbers and linear in allocation size.
403  */
404 
405  /* First assumption: matrix dimensions have not changed. If 0, they are
406  * initialized to dim 1.*/
407  unsigned int rowL = L.getRows();
408  const unsigned int colL = 6;
409  if (0 == rowL) {
410  rowL = 1;
411  L.resize(rowL, colL);
412  }
413 
414  /* vectTmp is used to store the return values of functions get_s() and
415  * error(). */
416  vpMatrix matrixTmp;
417 
418  /* The cursor are the number of the next case of the vector array to
419  * be affected. A memory reallocation should be done when cursor
420  * is out of the vector-array range.*/
421  unsigned int cursorL = 0;
422 
423  std::list<vpBasicFeature *>::const_iterator it;
424  std::list<unsigned int>::const_iterator it_select;
425 
426  for (it = featureList.begin(), it_select = featureSelectionList.begin(); it != featureList.end(); ++it, ++it_select) {
427  /* Get s. */
428  matrixTmp = (*it)->interaction(*it_select);
429  unsigned int rowMatrixTmp = matrixTmp.getRows();
430  unsigned int colMatrixTmp = matrixTmp.getCols();
431 
432  /* Check the matrix L size, and realloc if needed. */
433  while (rowMatrixTmp + cursorL > rowL) {
434  rowL *= 2;
435  L.resize(rowL, colL, false);
436  vpDEBUG_TRACE(15, "Realloc!");
437  }
438 
439  /* Copy the temporarily matrix into L. */
440  for (unsigned int k = 0; k < rowMatrixTmp; ++k, ++cursorL) {
441  for (unsigned int j = 0; j < colMatrixTmp; ++j) {
442  L[cursorL][j] = matrixTmp[k][j];
443  }
444  }
445  }
446 
447  L.resize(cursorL, colL, false);
448 
449  return;
450 }
451 
453 {
454  try {
455 
456  switch (interactionMatrixType) {
457  case CURRENT: {
458  try {
459  computeInteractionMatrixFromList(this->featureList, this->featureSelectionList, L);
460  dim_task = L.getRows();
462  }
463 
464  catch (...) {
465  throw;
466  }
467  } break;
468  case DESIRED: {
469  try {
471  computeInteractionMatrixFromList(this->desiredFeatureList, this->featureSelectionList, L);
472 
473  dim_task = L.getRows();
475  }
476 
477  }
478  catch (...) {
479  throw;
480  }
481  } break;
482  case MEAN: {
483  vpMatrix Lstar(L.getRows(), L.getCols());
484  try {
485  computeInteractionMatrixFromList(this->featureList, this->featureSelectionList, L);
486  computeInteractionMatrixFromList(this->desiredFeatureList, this->featureSelectionList, Lstar);
487  }
488  catch (...) {
489  throw;
490  }
491  L = (L + Lstar) / 2;
492 
493  dim_task = L.getRows();
495  } break;
496  case USER_DEFINED:
497  // dim_task = L.getRows() ;
499  break;
500  }
501 
502  }
503  catch (...) {
504  throw;
505  }
506  return L;
507 }
508 
510 {
511  if (featureList.empty()) {
512  vpERROR_TRACE("feature list empty, cannot compute Ls");
513  throw(vpServoException(vpServoException::noFeatureError, "feature list empty, cannot compute Ls"));
514  }
515  if (desiredFeatureList.empty()) {
516  vpERROR_TRACE("feature list empty, cannot compute Ls");
517  throw(vpServoException(vpServoException::noFeatureError, "feature list empty, cannot compute Ls"));
518  }
519 
520  try {
521  vpBasicFeature *current_s;
522  vpBasicFeature *desired_s;
523 
524  /* The vector dimensions are not known before the affectation loop.
525  * They thus should be allocated on the flight, in the loop.
526  * The first assumption is that the size has not changed. A double
527  * reallocation (realloc(dim*2)) is done if necessary. In particular,
528  * [log_2(dim)+1] reallocations are done for the first error computation.
529  * If the allocated size is too large, a correction is done after the
530  * loop. The algorithmic cost is linear in affectation, logarithmic in
531  * allocation numbers and linear in allocation size. No assumptions are
532  * made concerning size of each vector: they are not said equal, and could
533  * be different.
534  */
535 
536  /* First assumption: vector dimensions have not changed. If 0, they are
537  * initialized to dim 1.*/
538  unsigned int dimError = error.getRows();
539  unsigned int dimS = s.getRows();
540  unsigned int dimSStar = sStar.getRows();
541  if (0 == dimError) {
542  dimError = 1;
543  error.resize(dimError);
544  }
545  if (0 == dimS) {
546  dimS = 1;
547  s.resize(dimS);
548  }
549  if (0 == dimSStar) {
550  dimSStar = 1;
551  sStar.resize(dimSStar);
552  }
553 
554  /* vectTmp is used to store the return values of functions get_s() and
555  * error(). */
556  vpColVector vectTmp;
557 
558  /* The cursor are the number of the next case of the vector array to
559  * be affected. A memory reallocation should be done when cursor
560  * is out of the vector-array range.*/
561  unsigned int cursorS = 0;
562  unsigned int cursorSStar = 0;
563  unsigned int cursorError = 0;
564 
565  /* For each cell of the list, copy value of s, s_star and error. */
566  std::list<vpBasicFeature *>::const_iterator it_s;
567  std::list<vpBasicFeature *>::const_iterator it_s_star;
568  std::list<unsigned int>::const_iterator it_select;
569 
570  for (it_s = featureList.begin(), it_s_star = desiredFeatureList.begin(), it_select = featureSelectionList.begin();
571  it_s != featureList.end(); ++it_s, ++it_s_star, ++it_select) {
572  current_s = (*it_s);
573  desired_s = (*it_s_star);
574  unsigned int select = (*it_select);
575 
576  /* Get s, and store it in the s vector. */
577  vectTmp = current_s->get_s(select);
578  unsigned int dimVectTmp = vectTmp.getRows();
579  while (dimVectTmp + cursorS > dimS) {
580  dimS *= 2;
581  s.resize(dimS, false);
582  vpDEBUG_TRACE(15, "Realloc!");
583  }
584  for (unsigned int k = 0; k < dimVectTmp; ++k) {
585  s[cursorS++] = vectTmp[k];
586  }
587 
588  /* Get s_star, and store it in the s vector. */
589  vectTmp = desired_s->get_s(select);
590  dimVectTmp = vectTmp.getRows();
591  while (dimVectTmp + cursorSStar > dimSStar) {
592  dimSStar *= 2;
593  sStar.resize(dimSStar, false);
594  }
595  for (unsigned int k = 0; k < dimVectTmp; ++k) {
596  sStar[cursorSStar++] = vectTmp[k];
597  }
598 
599  /* Get error, and store it in the s vector. */
600  vectTmp = current_s->error(*desired_s, select);
601  dimVectTmp = vectTmp.getRows();
602  while (dimVectTmp + cursorError > dimError) {
603  dimError *= 2;
604  error.resize(dimError, false);
605  }
606  for (unsigned int k = 0; k < dimVectTmp; ++k) {
607  error[cursorError++] = vectTmp[k];
608  }
609  }
610 
611  /* If too much memory has been allocated, realloc. */
612  s.resize(cursorS, false);
613  sStar.resize(cursorSStar, false);
614  error.resize(cursorError, false);
615 
616  /* Final modifications. */
617  dim_task = error.getRows();
618  errorComputed = true;
619  }
620  catch (...) {
621  throw;
622  }
623  return error;
624 }
625 
627 {
628  switch (servoType) {
629  case NONE:
630  vpERROR_TRACE("No control law have been yet defined");
631  throw(vpServoException(vpServoException::servoError, "No control law have been yet defined"));
632  break;
633  case EYEINHAND_CAMERA:
634  return true;
635  break;
636  case EYEINHAND_L_cVe_eJe:
637  case EYETOHAND_L_cVe_eJe:
638  if (!init_cVe)
639  vpERROR_TRACE("cVe not initialized");
640  if (!init_eJe)
641  vpERROR_TRACE("eJe not initialized");
642  return (init_cVe && init_eJe);
643  break;
645  if (!init_cVf)
646  vpERROR_TRACE("cVf not initialized");
647  if (!init_fVe)
648  vpERROR_TRACE("fVe not initialized");
649  if (!init_eJe)
650  vpERROR_TRACE("eJe not initialized");
651  return (init_cVf && init_fVe && init_eJe);
652  break;
653 
654  case EYETOHAND_L_cVf_fJe:
655  if (!init_cVf)
656  vpERROR_TRACE("cVf not initialized");
657  if (!init_fJe)
658  vpERROR_TRACE("fJe not initialized");
659  return (init_cVf && init_fJe);
660  break;
661  }
662 
663  return false;
664 }
665 
667 {
668  switch (servoType) {
669  case NONE:
670  vpERROR_TRACE("No control law have been yet defined");
671  throw(vpServoException(vpServoException::servoError, "No control law have been yet defined"));
672  break;
673  case EYEINHAND_CAMERA:
674  return true;
675  case EYEINHAND_L_cVe_eJe:
676  if (!init_eJe)
677  vpERROR_TRACE("eJe not updated");
678  return (init_eJe);
679  break;
680  case EYETOHAND_L_cVe_eJe:
681  if (!init_cVe)
682  vpERROR_TRACE("cVe not updated");
683  if (!init_eJe)
684  vpERROR_TRACE("eJe not updated");
685  return (init_cVe && init_eJe);
686  break;
688  if (!init_fVe)
689  vpERROR_TRACE("fVe not updated");
690  if (!init_eJe)
691  vpERROR_TRACE("eJe not updated");
692  return (init_fVe && init_eJe);
693  break;
694 
695  case EYETOHAND_L_cVf_fJe:
696  if (!init_fJe)
697  vpERROR_TRACE("fJe not updated");
698  return (init_fJe);
699  break;
700  }
701 
702  return false;
703 }
704 
706 {
707  vpVelocityTwistMatrix cVa; // Twist transformation matrix
708  vpMatrix aJe; // Jacobian
709 
710  if (m_first_iteration) {
711  if (testInitialization() == false) {
712  vpERROR_TRACE("All the matrices are not correctly initialized");
713  throw(vpServoException(vpServoException::servoError, "Cannot compute control law. "
714  "All the matrices are not correctly"
715  "initialized."));
716  }
717  }
718  if (testUpdated() == false) {
719  vpERROR_TRACE("All the matrices are not correctly updated");
720  }
721 
722  // test if all the required initialization have been done
723  switch (servoType) {
724  case NONE:
725  vpERROR_TRACE("No control law have been yet defined");
726  throw(vpServoException(vpServoException::servoError, "No control law have been yet defined"));
727  break;
728  case EYEINHAND_CAMERA:
729  case EYEINHAND_L_cVe_eJe:
730  case EYETOHAND_L_cVe_eJe:
731 
732  cVa = cVe;
733  aJe = eJe;
734 
735  init_cVe = false;
736  init_eJe = false;
737  break;
739  cVa = cVf * fVe;
740  aJe = eJe;
741  init_fVe = false;
742  init_eJe = false;
743  break;
744  case EYETOHAND_L_cVf_fJe:
745  cVa = cVf;
746  aJe = fJe;
747  init_fJe = false;
748  break;
749  }
750 
752  computeError();
753 
754  // compute task Jacobian
755  if (iscJcIdentity)
756  J1 = L * cVa * aJe;
757  else
758  J1 = L * cJc * cVa * aJe;
759 
760  // handle the eye-in-hand eye-to-hand case
762 
763  // pseudo inverse of the task Jacobian
764  // and rank of the task Jacobian
765  // the image of J1 is also computed to allows the computation
766  // of the projection operator
767  vpMatrix imJ1t, imJ1;
768  bool imageComputed = false;
769 
770  if (inversionType == PSEUDO_INVERSE) {
772 
773  imageComputed = true;
774  }
775  else
776  J1p = J1.t();
777 
778  if (rankJ1 == J1.getCols()) {
779  /* if no degrees of freedom remains (rank J1 = ndof)
780  WpW = I, multiply by WpW is useless
781  */
782  e1 = J1p * error; // primary task
783 
784  WpW.eye(J1.getCols(), J1.getCols());
785  }
786  else {
787  if (imageComputed != true) {
788  vpMatrix Jtmp;
789  // image of J1 is computed to allows the computation
790  // of the projection operator
791  rankJ1 = J1.pseudoInverse(Jtmp, sv, m_pseudo_inverse_threshold, imJ1, imJ1t);
792  }
793  WpW = imJ1t.AAt();
794 
795 #ifdef DEBUG
796  std::cout << "rank J1: " << rankJ1 << std::endl;
797  imJ1t.print(std::cout, 10, "imJ1t");
798  imJ1.print(std::cout, 10, "imJ1");
799 
800  WpW.print(std::cout, 10, "WpW");
801  J1.print(std::cout, 10, "J1");
802  J1p.print(std::cout, 10, "J1p");
803 #endif
804  e1 = WpW * J1p * error;
805  }
806  e = -lambda(e1) * e1;
807 
808  I.eye(J1.getCols());
809 
810  // Compute classical projection operator
811  I_WpW = (I - WpW);
812 
813  m_first_iteration = false;
814  return e;
815 }
816 
818 {
819  vpVelocityTwistMatrix cVa; // Twist transformation matrix
820  vpMatrix aJe; // Jacobian
821 
822  if (m_first_iteration) {
823  if (testInitialization() == false) {
824  vpERROR_TRACE("All the matrices are not correctly initialized");
825  throw(vpServoException(vpServoException::servoError, "Cannot compute control law "
826  "All the matrices are not correctly"
827  "initialized"));
828  }
829  }
830  if (testUpdated() == false) {
831  vpERROR_TRACE("All the matrices are not correctly updated");
832  }
833 
834  // test if all the required initialization have been done
835  switch (servoType) {
836  case NONE:
837  vpERROR_TRACE("No control law have been yet defined");
838  throw(vpServoException(vpServoException::servoError, "No control law have been yet defined"));
839  break;
840  case EYEINHAND_CAMERA:
841  case EYEINHAND_L_cVe_eJe:
842  case EYETOHAND_L_cVe_eJe:
843 
844  cVa = cVe;
845  aJe = eJe;
846 
847  init_cVe = false;
848  init_eJe = false;
849  break;
851  cVa = cVf * fVe;
852  aJe = eJe;
853  init_fVe = false;
854  init_eJe = false;
855  break;
856  case EYETOHAND_L_cVf_fJe:
857  cVa = cVf;
858  aJe = fJe;
859  init_fJe = false;
860  break;
861  }
862 
864  computeError();
865 
866  // compute task Jacobian
867  J1 = L * cVa * aJe;
868 
869  // handle the eye-in-hand eye-to-hand case
871 
872  // pseudo inverse of the task Jacobian
873  // and rank of the task Jacobian
874  // the image of J1 is also computed to allows the computation
875  // of the projection operator
876  vpMatrix imJ1t, imJ1;
877  bool imageComputed = false;
878 
879  if (inversionType == PSEUDO_INVERSE) {
881 
882  imageComputed = true;
883  }
884  else
885  J1p = J1.t();
886 
887  if (rankJ1 == J1.getCols()) {
888  /* if no degrees of freedom remains (rank J1 = ndof)
889  WpW = I, multiply by WpW is useless
890  */
891  e1 = J1p * error; // primary task
892 
893  WpW.eye(J1.getCols());
894  }
895  else {
896  if (imageComputed != true) {
897  vpMatrix Jtmp;
898  // image of J1 is computed to allows the computation
899  // of the projection operator
900  rankJ1 = J1.pseudoInverse(Jtmp, sv, m_pseudo_inverse_threshold, imJ1, imJ1t);
901  }
902  WpW = imJ1t.AAt();
903 
904 #ifdef DEBUG
905  std::cout << "rank J1 " << rankJ1 << std::endl;
906  std::cout << "imJ1t" << std::endl << imJ1t;
907  std::cout << "imJ1" << std::endl << imJ1;
908 
909  std::cout << "WpW" << std::endl << WpW;
910  std::cout << "J1" << std::endl << J1;
911  std::cout << "J1p" << std::endl << J1p;
912 #endif
913  e1 = WpW * J1p * error;
914  }
915 
916  // memorize the initial e1 value if the function is called the first time
917  // or if the time given as parameter is equal to 0.
918  if (m_first_iteration || std::fabs(t) < std::numeric_limits<double>::epsilon()) {
919  e1_initial = e1;
920  }
921  // Security check. If size of e1_initial and e1 differ, that means that
922  // e1_initial was not set
923  if (e1_initial.getRows() != e1.getRows())
924  e1_initial = e1;
925 
926  e = -lambda(e1) * e1 + lambda(e1) * e1_initial * exp(-mu * t);
927 
928  I.eye(J1.getCols());
929 
930  // Compute classical projection operator
931  I_WpW = (I - WpW);
932 
933  m_first_iteration = false;
934  return e;
935 }
936 
938 {
939  vpVelocityTwistMatrix cVa; // Twist transformation matrix
940  vpMatrix aJe; // Jacobian
941 
942  if (m_first_iteration) {
943  if (testInitialization() == false) {
944  vpERROR_TRACE("All the matrices are not correctly initialized");
945  throw(vpServoException(vpServoException::servoError, "Cannot compute control law "
946  "All the matrices are not correctly"
947  "initialized"));
948  }
949  }
950  if (testUpdated() == false) {
951  vpERROR_TRACE("All the matrices are not correctly updated");
952  }
953 
954  // test if all the required initialization have been done
955  switch (servoType) {
956  case NONE:
957  vpERROR_TRACE("No control law have been yet defined");
958  throw(vpServoException(vpServoException::servoError, "No control law have been yet defined"));
959  break;
960  case EYEINHAND_CAMERA:
961  case EYEINHAND_L_cVe_eJe:
962  case EYETOHAND_L_cVe_eJe:
963 
964  cVa = cVe;
965  aJe = eJe;
966 
967  init_cVe = false;
968  init_eJe = false;
969  break;
971  cVa = cVf * fVe;
972  aJe = eJe;
973  init_fVe = false;
974  init_eJe = false;
975  break;
976  case EYETOHAND_L_cVf_fJe:
977  cVa = cVf;
978  aJe = fJe;
979  init_fJe = false;
980  break;
981  }
982 
984  computeError();
985 
986  // compute task Jacobian
987  J1 = L * cVa * aJe;
988 
989  // handle the eye-in-hand eye-to-hand case
991 
992  // pseudo inverse of the task Jacobian
993  // and rank of the task Jacobian
994  // the image of J1 is also computed to allows the computation
995  // of the projection operator
996  vpMatrix imJ1t, imJ1;
997  bool imageComputed = false;
998 
999  if (inversionType == PSEUDO_INVERSE) {
1001 
1002  imageComputed = true;
1003  }
1004  else
1005  J1p = J1.t();
1006 
1007  if (rankJ1 == J1.getCols()) {
1008  /* if no degrees of freedom remains (rank J1 = ndof)
1009  WpW = I, multiply by WpW is useless
1010  */
1011  e1 = J1p * error; // primary task
1012 
1013  WpW.eye(J1.getCols());
1014  }
1015  else {
1016  if (imageComputed != true) {
1017  vpMatrix Jtmp;
1018  // image of J1 is computed to allows the computation
1019  // of the projection operator
1020  rankJ1 = J1.pseudoInverse(Jtmp, sv, m_pseudo_inverse_threshold, imJ1, imJ1t);
1021  }
1022  WpW = imJ1t.AAt();
1023 
1024 #ifdef DEBUG
1025  std::cout << "rank J1 " << rankJ1 << std::endl;
1026  std::cout << "imJ1t" << std::endl << imJ1t;
1027  std::cout << "imJ1" << std::endl << imJ1;
1028 
1029  std::cout << "WpW" << std::endl << WpW;
1030  std::cout << "J1" << std::endl << J1;
1031  std::cout << "J1p" << std::endl << J1p;
1032 #endif
1033  e1 = WpW * J1p * error;
1034  }
1035 
1036  // memorize the initial e1 value if the function is called the first time
1037  // or if the time given as parameter is equal to 0.
1038  if (m_first_iteration || std::fabs(t) < std::numeric_limits<double>::epsilon()) {
1039  e1_initial = e1;
1040  }
1041  // Security check. If size of e1_initial and e1 differ, that means that
1042  // e1_initial was not set
1043  if (e1_initial.getRows() != e1.getRows())
1044  e1_initial = e1;
1045 
1046  e = -lambda(e1) * e1 + (e_dot_init + lambda(e1) * e1_initial) * exp(-mu * t);
1047 
1048  I.eye(J1.getCols());
1049 
1050  // Compute classical projection operator
1051  I_WpW = (I - WpW);
1052 
1053  m_first_iteration = false;
1054  return e;
1055 }
1056 
1057 void vpServo::computeProjectionOperators(const vpMatrix &J1_, const vpMatrix &I_, const vpMatrix &I_WpW_,
1058  const vpColVector &error_, vpMatrix &P_) const
1059 {
1060  // Initialization
1061  unsigned int n = J1_.getCols();
1062  P_.resize(n, n);
1063 
1064  // Compute gain depending by the task error to ensure a smooth change
1065  // between the operators.
1066  double e0_ = 0.1;
1067  double e1_ = 0.7;
1068  double sig = 0.0;
1069 
1070  double norm_e = error_.frobeniusNorm();
1071  if (norm_e > e1_)
1072  sig = 1.0;
1073  else if (e0_ <= norm_e && norm_e <= e1_)
1074  sig = 1.0 / (1.0 + exp(-12.0 * ((norm_e - e0_) / ((e1_ - e0_))) + 6.0));
1075  else
1076  sig = 0.0;
1077 
1078  vpMatrix eT_J = static_cast<vpMatrix>(error_.t()) * J1_;
1079  vpMatrix eT_J_JT_e = eT_J.AAt();
1080  double pp = eT_J_JT_e[0][0];
1081 
1082  vpMatrix P_norm_e = I_ - (1.0 / pp) * eT_J.AtA();
1083 
1084  P_ = sig * P_norm_e + (1 - sig) * I_WpW_;
1085 
1086  return;
1087 }
1088 
1089 vpColVector vpServo::secondaryTask(const vpColVector &de2dt, const bool &useLargeProjectionOperator)
1090 {
1091  vpColVector sec;
1092 
1093  if (!useLargeProjectionOperator) {
1094  if (rankJ1 == J1.getCols()) {
1095  vpERROR_TRACE("no degree of freedom is free, cannot use secondary task");
1096  throw(vpServoException(vpServoException::noDofFree, "no degree of freedom is free, cannot use secondary task"));
1097  }
1098  else {
1099 #if 0
1100  // computed in computeControlLaw()
1101  vpMatrix I;
1102 
1103  I.resize(J1.getCols(), J1.getCols());
1104  I.setIdentity();
1105  I_WpW = (I - WpW);
1106 #endif
1107  // std::cout << "I-WpW" << std::endl << I_WpW <<std::endl ;
1108  sec = I_WpW * de2dt;
1109  }
1110  }
1111 
1112  else {
1114 
1115  sec = P * de2dt;
1116  }
1117 
1118  return sec;
1119 }
1120 
1122  const bool &useLargeProjectionOperator)
1123 {
1124  vpColVector sec;
1125 
1126  if (!useLargeProjectionOperator) {
1127  if (rankJ1 == J1.getCols()) {
1128  vpERROR_TRACE("no degree of freedom is free, cannot use secondary task");
1129  throw(vpServoException(vpServoException::noDofFree, "no degree of freedom is free, cannot use secondary task"));
1130  }
1131  else {
1132 
1133 #if 0
1134  // computed in computeControlLaw()
1135  vpMatrix I;
1136 
1137  I.resize(J1.getCols(), J1.getCols());
1138  I.setIdentity();
1139 
1140  I_WpW = (I - WpW);
1141 #endif
1142 
1143  // To be coherent with the primary task the gain must be the same
1144  // between primary and secondary task.
1145  sec = -lambda(e1) * I_WpW * e2 + I_WpW * de2dt;
1146  }
1147  }
1148  else {
1150 
1151  sec = -lambda(e1) * P * e2 + P * de2dt;
1152  }
1153 
1154  return sec;
1155 }
1156 
1158  const vpColVector &qmin, const vpColVector &qmax,
1159  const double &rho, const double &rho1, const double &lambda_tune)
1160 {
1161  unsigned int const n = J1.getCols();
1162 
1163  if (qmin.size() != n || qmax.size() != n) {
1164  std::stringstream msg;
1165  msg << "Dimension vector qmin (" << qmin.size()
1166  << ") or qmax () does not correspond to the number of jacobian "
1167  "columns";
1168  msg << "qmin size: " << qmin.size() << std::endl;
1170  }
1171  if (q.size() != n || dq.size() != n) {
1172  vpERROR_TRACE("Dimension vector q or dq does not correspont to the "
1173  "number of jacobian columns");
1174  throw(vpServoException(vpServoException::dimensionError, "Dimension vector q or dq does not correspont to "
1175  "the number of jacobian columns"));
1176  }
1177 
1178  double lambda_l = 0.0;
1179 
1180  vpColVector q2(n);
1181 
1182  vpColVector q_l0_min(n);
1183  vpColVector q_l0_max(n);
1184  vpColVector q_l1_min(n);
1185  vpColVector q_l1_max(n);
1186 
1187  // Computation of gi ([nx1] vector) and lambda_l ([nx1] vector)
1188  vpMatrix g(n, n);
1189  vpColVector q2_i(n);
1190 
1192 
1193  for (unsigned int i = 0; i < n; i++) {
1194  q_l0_min[i] = qmin[i] + rho * (qmax[i] - qmin[i]);
1195  q_l0_max[i] = qmax[i] - rho * (qmax[i] - qmin[i]);
1196 
1197  q_l1_min[i] = q_l0_min[i] - rho * rho1 * (qmax[i] - qmin[i]);
1198  q_l1_max[i] = q_l0_max[i] + rho * rho1 * (qmax[i] - qmin[i]);
1199 
1200  if (q[i] < q_l0_min[i])
1201  g[i][i] = -1;
1202  else if (q[i] > q_l0_max[i])
1203  g[i][i] = 1;
1204  else
1205  g[i][i] = 0;
1206  }
1207 
1208  for (unsigned int i = 0; i < n; i++) {
1209  if (q[i] > q_l0_min[i] && q[i] < q_l0_max[i])
1210  q2_i = 0 * q2_i;
1211 
1212  else {
1213  vpColVector Pg_i(n);
1214  Pg_i = (P * g.getCol(i));
1215  double b = (vpMath::abs(dq[i])) / (vpMath::abs(Pg_i[i]));
1216 
1217  if (b < 1.) // If the ratio b is big we don't activate the joint
1218  // avoidance limit for the joint.
1219  {
1220  if (q[i] < q_l1_min[i] || q[i] > q_l1_max[i])
1221  q2_i = -(1 + lambda_tune) * b * Pg_i;
1222 
1223  else {
1224  if (q[i] >= q_l0_max[i] && q[i] <= q_l1_max[i])
1225  lambda_l = 1 / (1 + exp(-12 * ((q[i] - q_l0_max[i]) / (q_l1_max[i] - q_l0_max[i])) + 6));
1226 
1227  else if (q[i] >= q_l1_min[i] && q[i] <= q_l0_min[i])
1228  lambda_l = 1 / (1 + exp(-12 * ((q[i] - q_l0_min[i]) / (q_l1_min[i] - q_l0_min[i])) + 6));
1229 
1230  q2_i = -lambda_l * (1 + lambda_tune) * b * Pg_i;
1231  }
1232  }
1233  }
1234  q2 = q2 + q2_i;
1235  }
1236  return q2;
1237 }
1238 END_VISP_NAMESPACE
unsigned int getCols() const
Definition: vpArray2D.h:337
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:362
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
unsigned int getRows() const
Definition: vpArray2D.h:347
class that defines what is a visual feature
virtual vpColVector error(const vpBasicFeature &s_star, unsigned int select=FEATURE_ALL)
virtual void init()=0
void setDeallocate(vpBasicFeatureDeallocatorType d)
vpColVector get_s(unsigned int select=FEATURE_ALL) const
Get the feature vector .
virtual vpBasicFeature * duplicate() const =0
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
vpRowVector t() const
double frobeniusNorm() const
void resize(unsigned int i, bool flagNullify=true)
Definition: vpColVector.h:1143
@ dimensionError
Bad dimension.
Definition: vpException.h:71
static Type abs(const Type &x)
Definition: vpMath.h:269
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
int print(std::ostream &s, unsigned int length, const std::string &intro="") const
Definition: vpMatrix.cpp:824
vpMatrix AtA() const
vpMatrix pseudoInverse(double svThreshold=1e-6) const
vpColVector getCol(unsigned int j) const
Definition: vpMatrix.cpp:548
vpMatrix AAt() const
vpMatrix t() const
Error that can be emitted by the vpServo class and its derivatives.
@ servoError
Other exception.
@ noDofFree
No degree of freedom is available to achieve the secondary task.
@ noFeatureError
Current or desired feature list is empty.
unsigned int rankJ1
Rank of the task Jacobian.
Definition: vpServo.h:1198
vpMatrix eJe
Jacobian expressed in the end-effector frame (e).
Definition: vpServo.h:1262
int signInteractionMatrix
Definition: vpServo.h:1213
vpMatrix WpW
Projection operators .
Definition: vpServo.h:1295
vpVelocityTwistMatrix cVf
Twist transformation matrix between camera frame (c) and robot base frame (f).
Definition: vpServo.h:1238
vpMatrix J1
Task Jacobian .
Definition: vpServo.h:1171
bool testUpdated()
Definition: vpServo.cpp:666
void setCameraDoF(const vpColVector &dof)
Definition: vpServo.cpp:155
bool init_cVe
Definition: vpServo.h:1236
bool errorComputed
true if the error has been computed.
Definition: vpServo.h:1282
vpMatrix fJe
Jacobian expressed in the robot base frame (f).
Definition: vpServo.h:1270
void setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrixType, const vpServoInversionType &interactionMatrixInversion=PSEUDO_INVERSE)
Definition: vpServo.cpp:380
vpServoType
Definition: vpServo.h:151
@ EYETOHAND_L_cVe_eJe
Definition: vpServo.h:175
@ EYEINHAND_CAMERA
Definition: vpServo.h:161
@ EYETOHAND_L_cVf_fJe
Definition: vpServo.h:189
@ EYEINHAND_L_cVe_eJe
Definition: vpServo.h:168
@ NONE
Definition: vpServo.h:155
@ EYETOHAND_L_cVf_fVe_eJe
Definition: vpServo.h:182
unsigned int getDimension() const
Definition: vpServo.cpp:366
bool init_cVf
Definition: vpServo.h:1244
double mu
Definition: vpServo.h:1324
vpVelocityTwistMatrix cVe
Definition: vpServo.h:1229
bool init_fJe
Definition: vpServo.h:1275
void addFeature(vpBasicFeature &s_cur, vpBasicFeature &s_star, unsigned int select=vpBasicFeature::FEATURE_ALL)
Definition: vpServo.cpp:331
vpMatrix P
Definition: vpServo.h:1314
void set_cVe(const vpVelocityTwistMatrix &cVe_)
Definition: vpServo.h:1038
vpColVector e1
Primary task .
Definition: vpServo.h:1185
vpColVector e1_initial
Definition: vpServo.h:1331
bool forceInteractionMatrixComputation
Force the interaction matrix computation even if it is already done.
Definition: vpServo.h:1290
vpColVector secondaryTaskJointLimitAvoidance(const vpColVector &q, const vpColVector &dq, const vpColVector &qmin, const vpColVector &qmax, const double &rho=0.1, const double &rho1=0.3, const double &lambda_tune=0.7)
Definition: vpServo.cpp:1157
void print(const vpServo::vpServoPrintType display_level=ALL, std::ostream &os=std::cout)
Definition: vpServo.cpp:171
void kill()
Definition: vpServo.cpp:108
vpMatrix cJc
Definition: vpServo.h:1340
vpVelocityTwistMatrix fVe
Definition: vpServo.h:1249
void set_eJe(const vpMatrix &eJe_)
Definition: vpServo.h:1101
vpColVector secondaryTask(const vpColVector &de2dt, const bool &useLargeProjectionOperator=false)
Definition: vpServo.cpp:1089
bool taskWasKilled
Flag to indicate if the task was killed.
Definition: vpServo.h:1288
bool testInitialization()
Definition: vpServo.cpp:626
void setServo(const vpServoType &servo_type)
Definition: vpServo.cpp:134
std::list< vpBasicFeature * > featureList
List of current visual features .
Definition: vpServo.h:1201
vpColVector error
Definition: vpServo.h:1169
bool iscJcIdentity
Boolean to know if cJc is identity (for fast computation)
Definition: vpServo.h:1334
vpMatrix I_WpW
Projection operators .
Definition: vpServo.h:1297
virtual ~vpServo()
Definition: vpServo.cpp:70
vpColVector sStar
Definition: vpServo.h:1182
vpMatrix computeInteractionMatrix()
Definition: vpServo.cpp:452
vpMatrix J1p
Pseudo inverse of the task Jacobian.
Definition: vpServo.h:1173
vpColVector s
Definition: vpServo.h:1178
vpMatrix I
Identity matrix.
Definition: vpServo.h:1293
void computeProjectionOperators(const vpMatrix &J1_, const vpMatrix &I_, const vpMatrix &I_WpW_, const vpColVector &error_, vpMatrix &P_) const
Definition: vpServo.cpp:1057
std::list< vpBasicFeature * > desiredFeatureList
List of desired visual features .
Definition: vpServo.h:1203
vpServo()
Definition: vpServo.cpp:47
bool m_first_iteration
True until first call of computeControlLaw() is achieved.
Definition: vpServo.h:1342
vpMatrix L
Interaction matrix.
Definition: vpServo.h:1164
vpServoType servoType
Chosen visual servoing control law.
Definition: vpServo.h:1195
vpServoIteractionMatrixType interactionMatrixType
Type of the interaction matrix (current, mean, desired, user)
Definition: vpServo.h:1215
double m_pseudo_inverse_threshold
Threshold used in the pseudo inverse.
Definition: vpServo.h:1344
void init()
Definition: vpServo.cpp:72
vpServoInversionType
Definition: vpServo.h:226
@ PSEUDO_INVERSE
Definition: vpServo.h:235
std::list< unsigned int > featureSelectionList
Definition: vpServo.h:1206
vpColVector computeControlLaw()
Definition: vpServo.cpp:705
vpColVector e
Task .
Definition: vpServo.h:1187
bool init_eJe
Definition: vpServo.h:1267
vpServoPrintType
Definition: vpServo.h:242
@ ALL
Print all the task information.
Definition: vpServo.h:243
@ CONTROLLER
Print the type of controller law.
Definition: vpServo.h:244
@ ERROR_VECTOR
Print the error vector .
Definition: vpServo.h:245
@ GAIN
Print the gain .
Definition: vpServo.h:248
@ FEATURE_CURRENT
Print the current features .
Definition: vpServo.h:246
@ FEATURE_DESIRED
Print the desired features .
Definition: vpServo.h:247
@ MINIMUM
Same as vpServo::vpServoPrintType::ERROR_VECTOR.
Definition: vpServo.h:250
@ INTERACTION_MATRIX
Print the interaction matrix.
Definition: vpServo.h:249
vpColVector sv
Singular values from the pseudo inverse.
Definition: vpServo.h:1317
vpServoIteractionMatrixType
Definition: vpServo.h:196
@ DESIRED
Definition: vpServo.h:208
@ MEAN
Definition: vpServo.h:214
@ USER_DEFINED
Definition: vpServo.h:219
@ CURRENT
Definition: vpServo.h:202
bool interactionMatrixComputed
true if the interaction matrix has been computed.
Definition: vpServo.h:1284
bool init_fVe
Definition: vpServo.h:1255
vpColVector computeError()
Definition: vpServo.cpp:509
unsigned int dim_task
Dimension of the task updated during computeControlLaw().
Definition: vpServo.h:1286
vpServoInversionType inversionType
Definition: vpServo.h:1218
vpAdaptiveGain lambda
Gain used in the control law.
Definition: vpServo.h:1209