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