Visual Servoing Platform  version 3.6.1 under development (2024-05-20)
servoMomentImage.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 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 https://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  * Example of visual servoing with moments using an image as object
33  * container
34  */
35 
41 #define PRINT_CONDITION_NUMBER
42 
43 #include <iostream>
44 #include <visp3/core/vpCameraParameters.h>
45 #include <visp3/core/vpHomogeneousMatrix.h>
46 #include <visp3/core/vpIoTools.h>
47 #include <visp3/core/vpMath.h>
48 #include <visp3/core/vpMomentCommon.h>
49 #include <visp3/core/vpMomentDatabase.h>
50 #include <visp3/core/vpMomentObject.h>
51 #include <visp3/core/vpPlane.h>
52 #include <visp3/core/vpPoseVector.h>
53 #include <visp3/gui/vpDisplayGDI.h>
54 #include <visp3/gui/vpDisplayGTK.h>
55 #include <visp3/gui/vpDisplayOpenCV.h>
56 #include <visp3/gui/vpDisplayX.h>
57 #include <visp3/gui/vpPlot.h>
58 #include <visp3/robot/vpImageSimulator.h>
59 #include <visp3/robot/vpSimulatorCamera.h>
60 #include <visp3/visual_features/vpFeatureBuilder.h>
61 #include <visp3/visual_features/vpFeatureMomentCommon.h>
62 #include <visp3/visual_features/vpFeaturePoint.h>
63 #include <visp3/vs/vpServo.h>
64 
65 #if !defined(VISP_HAVE_DISPLAY)
66 int main()
67 {
68  std::cout << "Can't run this example since no display capability is available." << std::endl;
69  std::cout << "You should install one of the following third-party library: X11, OpenCV, GDI, GTK." << std::endl;
70  return EXIT_SUCCESS;
71 }
72 #elif !defined(VISP_HAVE_THREADS)
73 int main()
74 {
75  std::cout << "Can't run this example since multi-threading capability is not available." << std::endl;
76  std::cout << "You should maybe enable cxx11 standard." << std::endl;
77  return EXIT_SUCCESS;
78 }
79 #else
80 
81 #ifndef DOXYGEN_SHOULD_SKIP_THIS
82 class servoMoment
83 {
84 public:
85  servoMoment()
86  : m_width(640), m_height(480), m_cMo(), m_cdMo(), m_robot(), m_Iint(m_height, m_width, 0), m_task(), m_cam(),
87  m_error(0), m_imsim(), m_cur_img(m_height, m_width, 0), m_src_img(m_height, m_width, 0),
88  m_dst_img(m_height, m_width, 0), m_start_img(m_height, m_width, 0), m_interaction_type(), m_src(6), m_dst(6),
89  m_moments(nullptr), m_momentsDes(nullptr), m_featureMoments(nullptr), m_featureMomentsDes(nullptr), m_displayInt(nullptr)
90  { }
91  ~servoMoment()
92  {
93 #ifdef VISP_HAVE_DISPLAY
94  if (m_displayInt) {
95  delete m_displayInt;
96  }
97 #endif
98  delete m_moments;
99  delete m_momentsDes;
100  delete m_featureMoments;
101  delete m_featureMomentsDes;
102  }
103 
104  // setup robot parameters
105  void paramRobot() { m_cam = vpCameraParameters(600, 600, m_width / 2., m_height / 2.); }
106 
107  // update moment objects and interface
108  void refreshScene(vpMomentObject &obj)
109  {
110  m_cur_img = 0;
111  m_imsim.setCameraPosition(m_cMo);
112  m_imsim.getImage(m_cur_img, m_cam);
113  obj.fromImage(m_cur_img, 128, m_cam);
114  }
115 
116  // initialize scene in the interface
117  void initScene()
118  {
119  vpColVector X[4];
120  for (int i = 0; i < 4; i++)
121  X[i].resize(3);
122  X[0][0] = -0.2;
123  X[0][1] = -0.1;
124  X[0][2] = 0;
125 
126  X[1][0] = 0.2;
127  X[1][1] = -0.1;
128  X[1][2] = 0;
129 
130  X[2][0] = 0.2;
131  X[2][1] = 0.1;
132  X[2][2] = 0;
133 
134  X[3][0] = -0.2;
135  X[3][1] = 0.1;
136  X[3][2] = 0;
137  // init source and destination images
138  vpImage<unsigned char> tmp_img(m_height, m_width, 255);
139  vpImage<vpRGBa> tmp_start_img(m_height, m_width, vpRGBa(255, 0, 0));
140 
141  vpImageSimulator imsim_start;
143  imsim_start.init(tmp_start_img, X);
144  imsim_start.setCameraPosition(m_cdMo);
145  imsim_start.getImage(m_start_img, m_cam);
146 
147  m_imsim.setInterpolationType(vpImageSimulator::BILINEAR_INTERPOLATION);
148  m_imsim.init(tmp_img, X);
149 
150  m_imsim.setCameraPosition(m_cMo);
151  m_imsim.getImage(m_src_img, m_cam);
152 
153  m_src.setType(vpMomentObject::DENSE_FULL_OBJECT);
154  m_src.fromImage(m_src_img, 128, m_cam);
155 
156  m_dst.setType(vpMomentObject::DENSE_FULL_OBJECT);
157  m_imsim.setCameraPosition(m_cdMo);
158  m_imsim.getImage(m_dst_img, m_cam);
159  m_dst.fromImage(m_dst_img, 128, m_cam);
160  }
161 
162  // initialize the moment features
163  void initFeatures()
164  {
165  // A,B,C parameters of source and destination plane
166  double A;
167  double B;
168  double C;
169  double Ad;
170  double Bd;
171  double Cd;
172  // init main object: using moments up to order 5
173 
174  // Initializing values from regular plane (with ax+by+cz=d convention)
175  vpPlane pl;
176  pl.setABCD(0, 0, 1.0, 0);
177  pl.changeFrame(m_cMo);
178  planeToABC(pl, A, B, C);
179 
180  pl.setABCD(0, 0, 1.0, 0);
181  pl.changeFrame(m_cdMo);
182  planeToABC(pl, Ad, Bd, Cd);
183 
184  // extracting initial position (actually we only care about Zdst)
186  m_cdMo.extract(vec);
187 
190  // don't need to be specific, vpMomentCommon automatically loads
191  // Xg,Yg,An,Ci,Cj,Alpha moments
193  vpMomentCommon::getAlpha(m_dst), vec[2], true);
194  m_momentsDes = new vpMomentCommon(vpMomentCommon::getSurface(m_dst), vpMomentCommon::getMu3(m_dst),
195  vpMomentCommon::getAlpha(m_dst), vec[2], true);
196  // same thing with common features
197  m_featureMoments = new vpFeatureMomentCommon(*m_moments);
198  m_featureMomentsDes = new vpFeatureMomentCommon(*m_momentsDes);
199 
200  m_moments->updateAll(m_src);
201  m_momentsDes->updateAll(m_dst);
202 
203  m_featureMoments->updateAll(A, B, C);
204  m_featureMomentsDes->updateAll(Ad, Bd, Cd);
205 
206  // setup the interaction type
207  m_task.setInteractionMatrixType(m_interaction_type);
210  m_task.addFeature(m_featureMoments->getFeatureGravityNormalized(),
211  m_featureMomentsDes->getFeatureGravityNormalized());
212  m_task.addFeature(m_featureMoments->getFeatureAn(), m_featureMomentsDes->getFeatureAn());
213  // the moments are different in case of a symmetric object
214  m_task.addFeature(m_featureMoments->getFeatureCInvariant(), m_featureMomentsDes->getFeatureCInvariant(),
215  (1 << 10) | (1 << 11));
216  m_task.addFeature(m_featureMoments->getFeatureAlpha(), m_featureMomentsDes->getFeatureAlpha());
217 
218  m_task.setLambda(1.);
219  }
220 
222  {
223  m_cMo = cMo; // init source matrix
224  m_cdMo = cdMo; // init destination matrix
225 
226  m_interaction_type = vpServo::CURRENT; // use interaction matrix for current position
227 
228 #ifdef VISP_HAVE_DISPLAY
229  // init the right display
230 #if defined(VISP_HAVE_X11)
231  m_displayInt = new vpDisplayX;
232 #elif defined(HAVE_OPENCV_HIGHGUI)
233  m_displayInt = new vpDisplayOpenCV;
234 #elif defined(VISP_HAVE_GDI)
235  m_displayInt = new vpDisplayGDI;
236 #elif defined(VISP_HAVE_D3D9)
237  m_displayInt = new vpDisplayD3D;
238 #elif defined(VISP_HAVE_GTK)
239  m_displayInt = new vpDisplayGTK;
240 #endif
241  m_displayInt->init(m_Iint, 50, 50, "Visual servoing with moments");
242 #endif
243 
244  paramRobot(); // set up robot parameters
245 
246  m_task.setServo(vpServo::EYEINHAND_CAMERA);
247  initScene(); // initialize graphical scene (for interface)
248  initFeatures(); // initialize moment features
249  }
250 
251  // launch the simulation
252  void execute(unsigned int nbIter)
253  {
254  vpPlot ViSP_plot;
255  init_visp_plot(ViSP_plot); // Initialize plot object
256 
257  // init main object: using moments up to order 6
258  vpMomentObject obj(6);
259  // setting object type (disrete, continuous[form polygon])
261 
262  std::cout << "Display task information " << std::endl;
263  m_task.print();
264 
265  vpDisplay::display(m_Iint);
266  vpDisplay::flush(m_Iint);
267  unsigned int iter = 0;
268 
269  vpHomogeneousMatrix wMo; // Set to identity
270  vpHomogeneousMatrix wMc; // Camera position in the world frame
271  wMc = wMo * m_cMo.inverse();
272  m_robot.setPosition(wMc);
273  double sampling_time = 0.010; // Sampling period in seconds
274  m_robot.setSamplingTime(sampling_time);
275 
277  while (iter++ < nbIter) {
278 
279  vpColVector v;
280  double t = vpTime::measureTimeMs();
281  // get the cMo
282  wMc = m_robot.getPosition();
283  m_cMo = wMc.inverse() * wMo;
284  // setup the plane in A,B,C style
285  vpPlane pl;
286  double A, B, C;
287  pl.setABCD(0, 0, 1.0, 0);
288  pl.changeFrame(m_cMo);
289  planeToABC(pl, A, B, C);
290 
291  // track points, draw points and add refresh our object
292  refreshScene(obj);
293  // this is the most important thing to do: update our moments
294  m_moments->updateAll(obj);
295  // and update our features. Do it in that order. Features need to use the
296  // information computed by moments
297  m_featureMoments->updateAll(A, B, C);
298  // some graphics again
299  m_imsim.setCameraPosition(m_cMo);
300 
301  m_Iint = m_start_img;
302 
303  m_imsim.getImage(m_Iint, m_cam);
304  vpDisplay::display(m_Iint);
305 
306  if (iter == 1) {
307  vpDisplay::displayText(m_Iint, 20, 20, "Click to start servoing", vpColor::red);
308  vpDisplay::flush(m_Iint);
309  vpDisplay::getClick(m_Iint);
310  }
311  v = m_task.computeControlLaw();
312 
313  std::cout << " || s - s* || = " << m_task.error.sumSquare() << std::endl;
314 
315  m_robot.setVelocity(vpRobot::CAMERA_FRAME, v);
316 
317  ViSP_plot.plot(0, iter, v);
318  ViSP_plot.plot(1, iter, vpPoseVector(m_cMo)); // Plot the velocities
319  ViSP_plot.plot(2, iter, m_task.getError()); // cMo as translations and theta_u
320 
321  m_error = (m_task.getError()).sumSquare();
322 
323 #if defined(PRINT_CONDITION_NUMBER)
324  /*
325  * Condition number of interaction matrix
326  */
327  vpMatrix Linteraction = m_task.L;
328  vpMatrix tmpry, U;
329  vpColVector singularvals;
330  Linteraction.svd(singularvals, tmpry);
331  double condno = static_cast<double>(singularvals.getMaxValue() / singularvals.getMinValue());
332  std::cout << "Condition Number: " << condno << std::endl;
333 #endif
334  vpDisplay::displayText(m_Iint, 20, 20, "Click to stop visual servo...", vpColor::red);
335  if (vpDisplay::getClick(m_Iint, false)) {
336  break;
337  }
338  vpDisplay::flush(m_Iint);
339  vpTime::wait(t, sampling_time * 1000); // Wait 10 ms
340  }
341 
342  m_imsim.getImage(m_Iint, m_cam);
343  vpDisplay::display(m_Iint);
344  vpDisplay::displayText(m_Iint, 20, 20, "Click to quit...", vpColor::red);
345  vpDisplay::flush(m_Iint);
346  vpDisplay::getClick(m_Iint);
347  }
348 
349  void setInteractionMatrixType(vpServo::vpServoIteractionMatrixType type) { m_interaction_type = type; }
350 
351  double error() { return m_error; }
352 
353  void planeToABC(vpPlane &pl, double &A, double &B, double &C)
354  {
355  if (fabs(pl.getD()) < std::numeric_limits<double>::epsilon()) {
356  std::cout << "Invalid position:" << std::endl;
357  std::cout << m_cMo << std::endl;
358  std::cout << "Cannot put plane in the form 1/Z=Ax+By+C." << std::endl;
359  throw vpException(vpException::divideByZeroError, "invalid position!");
360  }
361  A = -pl.getA() / pl.getD();
362  B = -pl.getB() / pl.getD();
363  C = -pl.getC() / pl.getD();
364  }
365 
366  void init_visp_plot(vpPlot &ViSP_plot)
367  {
368  /* -------------------------------------
369  * Initialize ViSP Plotting
370  * -------------------------------------
371  */
372  const unsigned int NbGraphs = 3; // No. of graphs
373  const unsigned int NbCurves_in_graph[NbGraphs] = { 6, 6, 6 }; // Curves in each graph
374 
375  ViSP_plot.init(NbGraphs, 800, 800, 100 + static_cast<int>(m_width), 50, "Visual Servoing results...");
376 
377  vpColor Colors[6] = {// Colour for s1, s2, s3, in 1st plot
379 
380  for (unsigned int p = 0; p < NbGraphs; p++) {
381  ViSP_plot.initGraph(p, NbCurves_in_graph[p]);
382  for (unsigned int c = 0; c < NbCurves_in_graph[p]; c++)
383  ViSP_plot.setColor(p, c, Colors[c]);
384  }
385 
386  ViSP_plot.setTitle(0, "Robot velocities");
387  ViSP_plot.setLegend(0, 0, "v_x");
388  ViSP_plot.setLegend(0, 1, "v_y");
389  ViSP_plot.setLegend(0, 2, "v_z");
390  ViSP_plot.setLegend(0, 3, "w_x");
391  ViSP_plot.setLegend(0, 4, "w_y");
392  ViSP_plot.setLegend(0, 5, "w_z");
393 
394  ViSP_plot.setTitle(1, "Camera pose cMo");
395  ViSP_plot.setLegend(1, 0, "tx");
396  ViSP_plot.setLegend(1, 1, "ty");
397  ViSP_plot.setLegend(1, 2, "tz");
398  ViSP_plot.setLegend(1, 3, "tu_x");
399  ViSP_plot.setLegend(1, 4, "tu_y");
400  ViSP_plot.setLegend(1, 5, "tu_z");
401 
402  ViSP_plot.setTitle(2, "Error in visual features: ");
403  ViSP_plot.setLegend(2, 0, "x_n");
404  ViSP_plot.setLegend(2, 1, "y_n");
405  ViSP_plot.setLegend(2, 2, "a_n");
406  ViSP_plot.setLegend(2, 3, "sx");
407  ViSP_plot.setLegend(2, 4, "sy");
408  ViSP_plot.setLegend(2, 5, "alpha");
409  }
410 
411 protected:
412  // start and destination positioning matrices
413  unsigned int m_width;
414  unsigned int m_height;
415 
416  // start and destination positioning matrices
417  vpHomogeneousMatrix m_cMo;
418  vpHomogeneousMatrix m_cdMo;
419 
420  vpSimulatorCamera m_robot; // robot used in this simulation
421  vpImage<vpRGBa> m_Iint; // internal image used for interface display
422  vpServo m_task; // servoing task
423  vpCameraParameters m_cam; // robot camera parameters
424  double m_error; // current error
425  vpImageSimulator m_imsim; // image simulator used to simulate the perspective-projection camera
426 
427  // several images used in the simulation
428  vpImage<unsigned char> m_cur_img;
429  vpImage<unsigned char> m_src_img;
430  vpImage<unsigned char> m_dst_img;
431  vpImage<vpRGBa> m_start_img;
432  vpServo::vpServoIteractionMatrixType m_interaction_type; // current or desired
433  // source and destination objects for moment manipulation
434  vpMomentObject m_src;
435  vpMomentObject m_dst;
436 
437  // moment sets and their corresponding features
438  vpMomentCommon *m_moments;
439  vpMomentCommon *m_momentsDes;
440  vpFeatureMomentCommon *m_featureMoments;
441  vpFeatureMomentCommon *m_featureMomentsDes;
442 
443  vpDisplay *m_displayInt;
444 };
445 #endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS
446 
447 int main()
448 {
449  try {
450  // intial pose
451  vpHomogeneousMatrix cMo(-0.1, -0.1, 1.5, -vpMath::rad(20), -vpMath::rad(20), -vpMath::rad(30));
452  // Desired pose
454 
455  servoMoment servo;
456  // init the simulation
457  servo.init(cMo, cdMo);
458 
459  servo.execute(1500);
460  return EXIT_SUCCESS;
461  }
462  catch (const vpException &e) {
463  std::cout << "Catch an exception: " << e << std::endl;
464  return EXIT_FAILURE;
465  }
466 }
467 
468 #endif
Type getMinValue() const
Definition: vpArray2D.h:1088
Type getMaxValue() const
Definition: vpArray2D.h:1105
Generic class defining intrinsic camera parameters.
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
double sumSquare() const
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:152
static const vpColor red
Definition: vpColor.h:211
static const vpColor cyan
Definition: vpColor.h:220
static const vpColor orange
Definition: vpColor.h:221
static const vpColor blue
Definition: vpColor.h:217
static const vpColor purple
Definition: vpColor.h:222
static const vpColor green
Definition: vpColor.h:214
Display for windows using Direct3D 3rd party. Thus to enable this class Direct3D should be installed....
Definition: vpDisplayD3D.h:101
Display for windows using GDI (available on any windows 32 platform).
Definition: vpDisplayGDI.h:128
The vpDisplayGTK allows to display image using the GTK 3rd party library. Thus to enable this class G...
Definition: vpDisplayGTK.h:128
void init(vpImage< unsigned char > &I, int win_x=-1, int win_y=-1, const std::string &win_title="") vp_override
The vpDisplayOpenCV allows to display image using the OpenCV library. Thus to enable this class OpenC...
Use the X11 console to display images on unix-like OS. Thus to enable this class X11 should be instal...
Definition: vpDisplayX.h:128
Class that defines generic functionalities for display.
Definition: vpDisplay.h:173
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ divideByZeroError
Division by zero.
Definition: vpException.h:82
This class allows to access common vpFeatureMoments in a pre-filled database.
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
Class which enables to project an image in the 3D space and get the view of a virtual camera.
void getImage(vpImage< unsigned char > &I, const vpCameraParameters &cam)
void init(const vpImage< unsigned char > &I, vpColVector *X)
void setInterpolationType(const vpInterpolationType interplt)
void setCameraPosition(const vpHomogeneousMatrix &cMt)
static double rad(double deg)
Definition: vpMath.h:127
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:146
void svd(vpColVector &w, vpMatrix &V)
Definition: vpMatrix.cpp:2132
This class initializes and allows access to commonly used moments.
static std::vector< double > getMu3(vpMomentObject &object)
static double getAlpha(vpMomentObject &object)
static double getSurface(vpMomentObject &object)
Class for generic objects.
void setType(vpObjectType input_type)
void fromImage(const vpImage< unsigned char > &image, unsigned char threshold, const vpCameraParameters &cam)
This class defines the container for a plane geometrical structure.
Definition: vpPlane.h:54
void changeFrame(const vpHomogeneousMatrix &cMo)
Definition: vpPlane.cpp:372
double getD() const
Definition: vpPlane.h:106
double getA() const
Definition: vpPlane.h:100
double getC() const
Definition: vpPlane.h:104
void setABCD(double a, double b, double c, double d)
Definition: vpPlane.h:88
double getB() const
Definition: vpPlane.h:102
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
Definition: vpPlot.h:109
void initGraph(unsigned int graphNum, unsigned int curveNbr)
Definition: vpPlot.cpp:202
void init(unsigned int nbGraph, unsigned int height=700, unsigned int width=700, int x=-1, int y=-1, const std::string &title="")
Definition: vpPlot.cpp:95
void setLegend(unsigned int graphNum, unsigned int curveNum, const std::string &legend)
Definition: vpPlot.cpp:545
void plot(unsigned int graphNum, unsigned int curveNum, double x, double y)
Definition: vpPlot.cpp:269
void setColor(unsigned int graphNum, unsigned int curveNum, vpColor color)
Definition: vpPlot.cpp:245
void setTitle(unsigned int graphNum, const std::string &title)
Definition: vpPlot.cpp:503
Implementation of a pose vector and operations on poses.
Definition: vpPoseVector.h:189
Definition: vpRGBa.h:61
@ CAMERA_FRAME
Definition: vpRobot.h:82
@ EYEINHAND_CAMERA
Definition: vpServo.h:155
vpServoIteractionMatrixType
Definition: vpServo.h:190
@ CURRENT
Definition: vpServo.h:196
Class that defines the simplest robot: a free flying camera.
Class that consider the case of a translation vector.
void init(vpImage< unsigned char > &Iinput, vpImage< unsigned char > &IcannyVisp, vpImage< unsigned char > *p_dIx, vpImage< unsigned char > *p_dIy, vpImage< unsigned char > *p_IcannyimgFilter)
Initialize the different displays.
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT double measureTimeMs()