Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
servoSimuSphere.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 * Demonstration of the wireframe simulator with a simple visual servoing
32 */
33
39
40#include <cmath> // std::fabs
41#include <limits> // numeric_limits
42#include <stdlib.h>
43
44#include <visp3/core/vpCameraParameters.h>
45#include <visp3/core/vpConfig.h>
46#include <visp3/core/vpHomogeneousMatrix.h>
47#include <visp3/core/vpImage.h>
48#include <visp3/core/vpIoTools.h>
49#include <visp3/core/vpMath.h>
50#include <visp3/core/vpSphere.h>
51#include <visp3/core/vpTime.h>
52#include <visp3/core/vpVelocityTwistMatrix.h>
53#include <visp3/gui/vpDisplayFactory.h>
54#include <visp3/gui/vpPlot.h>
55#include <visp3/io/vpImageIo.h>
56#include <visp3/io/vpParseArgv.h>
57#include <visp3/robot/vpSimulatorCamera.h>
58#include <visp3/robot/vpWireFrameSimulator.h>
59#include <visp3/visual_features/vpFeatureBuilder.h>
60#include <visp3/visual_features/vpGenericFeature.h>
61#include <visp3/vs/vpServo.h>
62
63#define GETOPTARGS "dhp"
64
65#if defined(VISP_HAVE_DISPLAY) && (defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV))
66
67#if defined(ENABLE_VISP_NAMESPACE)
68using namespace VISP_NAMESPACE_NAME;
69#endif
70
79void usage(const char *name, const char *badparam)
80{
81 fprintf(stdout, "\n\
82Demonstration of the wireframe simulator with a simple visual servoing.\n\
83 \n\
84The visual servoing consists in bringing the camera at a desired position from the object.\n\
85 \n\
86The visual features used to compute the pose of the camera and thus the control law are special moments computed with the sphere's parameters.\n\
87 \n\
88SYNOPSIS\n\
89 %s [-d] [-p] [-h]\n",
90 name);
91
92 fprintf(stdout, "\n\
93OPTIONS: Default\n\
94 -d \n\
95 Turn off the display.\n\
96 \n\
97 -p \n\
98 Turn off the plotter.\n\
99 \n\
100 -h\n\
101 Print the help.\n");
102
103 if (badparam)
104 fprintf(stdout, "\nERROR: Bad parameter [%s]\n", badparam);
105}
106
119bool getOptions(int argc, const char **argv, bool &display, bool &plot)
120{
121 const char *optarg_;
122 int c;
123 while ((c = vpParseArgv::parse(argc, argv, GETOPTARGS, &optarg_)) > 1) {
124
125 switch (c) {
126 case 'd':
127 display = false;
128 break;
129 case 'p':
130 plot = false;
131 break;
132 case 'h':
133 usage(argv[0], nullptr);
134 return false;
135
136 default:
137 usage(argv[0], optarg_);
138 return false;
139 }
140 }
141
142 if ((c == 1) || (c == -1)) {
143 // standalone param or error
144 usage(argv[0], nullptr);
145 std::cerr << "ERROR: " << std::endl;
146 std::cerr << " Bad argument " << optarg_ << std::endl << std::endl;
147 return false;
148 }
149
150 return true;
151}
152
153/*
154 Computes the virtual visual features corresponding to the sphere and stores
155 it in the generic feature.
156
157 The visual feature vector is computed thanks to the following formula : s =
158 {sx, sy, sz} sx = gx*h2/(sqrt(h2+1) sx = gy*h2/(sqrt(h2+1) sz = sqrt(h2+1)
159
160 with gx and gy the center of gravity of the ellipse,
161 with h2 = (gx²+gy²)/(4*n20*gy²+4*n02*gx²-8n11gxgy)
162 with n20,n02,n11 the second order centered moments of the sphere normalized by its area
163 (i.e., such that \f$n_{ij} = \mu_{ij}/a\f$ where \f$\mu_{ij}\f$ are the centered moments and a the area)
164*/
165void computeVisualFeatures(const vpSphere &sphere, vpGenericFeature &s)
166{
167 double gx = sphere.get_x();
168 double gy = sphere.get_y();
169 double n02 = sphere.get_n02();
170 double n20 = sphere.get_n20();
171 double n11 = sphere.get_n11();
172 double h2;
173 // if (gx != 0 || gy != 0)
174 if (std::fabs(gx) > std::numeric_limits<double>::epsilon() || std::fabs(gy) > std::numeric_limits<double>::epsilon())
175 h2 = (vpMath::sqr(gx) + vpMath::sqr(gy)) /
176 (4 * n20 * vpMath::sqr(gy) + 4 * n02 * vpMath::sqr(gx) - 8 * n11 * gx * gy);
177 else
178 h2 = 1 / (4 * n20);
179
180 double sx = gx * h2 / (sqrt(h2 + 1));
181 double sy = gy * h2 / (sqrt(h2 + 1));
182 double sz = sqrt(h2 + 1); //(h2-(vpMath::sqr(sx)+vpMath::sqr(sy)-1))/(2*sqrt(h2));
183
184 s.set_s(sx, sy, sz);
185}
186
187/*
188 Computes the interaction matrix such as L = [-1/R*I3 [s]x]
189
190 with R the radius of the sphere
191 with I3 the 3x3 identity matrix
192 with [s]x the skew matrix related to s
193*/
194void computeInteractionMatrix(const vpGenericFeature &s, const vpSphere &sphere, vpMatrix &L)
195{
196 L = 0;
197 L[0][0] = -1 / sphere.getR();
198 L[1][1] = -1 / sphere.getR();
199 L[2][2] = -1 / sphere.getR();
200
201 double s0, s1, s2;
202 s.get_s(s0, s1, s2);
203
204 vpTranslationVector c(s0, s1, s2);
205 vpMatrix sk;
206 sk = c.skew();
207
208 for (unsigned int i = 0; i < 3; i++)
209 for (unsigned int j = 0; j < 3; j++)
210 L[i][j + 3] = sk[i][j];
211}
212
213int main(int argc, const char **argv)
214{
215 const unsigned int NB_DISPLAYS = 3;
216#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
217 std::shared_ptr<vpDisplay> display[NB_DISPLAYS];
218 for (unsigned int i = 0; i < NB_DISPLAYS; ++i) {
220 }
221#else
222 vpDisplay *display[NB_DISPLAYS];
223 for (unsigned int i = 0; i < NB_DISPLAYS; ++i) {
225 }
226#endif
227 unsigned int exit_status = EXIT_SUCCESS;
228 try {
229 bool opt_display = true;
230 bool opt_plot = true;
231
232 // Read the command line options
233 if (getOptions(argc, argv, opt_display, opt_plot) == false) {
234 return EXIT_FAILURE;
235 }
236
237 vpImage<vpRGBa> Iint(480, 640, vpRGBa(255));
238 vpImage<vpRGBa> Iext1(480, 640, vpRGBa(255));
239 vpImage<vpRGBa> Iext2(480, 640, vpRGBa(255));
240
241 if (opt_display) {
242 // Display size is automatically defined by the image (I) size
243 display[0]->init(Iint, 100, 100, "The internal view");
244 display[1]->init(Iext1, 100, 100, "The first external view");
245 display[2]->init(Iext2, 100, 100, "The second external view");
247 vpDisplay::setWindowPosition(Iext1, 750, 0);
248 vpDisplay::setWindowPosition(Iext2, 0, 550);
249 vpDisplay::display(Iint);
250 vpDisplay::flush(Iint);
251 vpDisplay::display(Iext1);
252 vpDisplay::flush(Iext1);
253 vpDisplay::display(Iext2);
254 vpDisplay::flush(Iext2);
255 }
256
257 vpPlot *plotter = nullptr;
258
260 vpSimulatorCamera robot;
261 float sampling_time = 0.020f; // Sampling period in second
262 robot.setSamplingTime(sampling_time);
263
264 // Since the task gain lambda is very high, we need to increase default
265 // max velocities
266 robot.setMaxTranslationVelocity(10);
267 robot.setMaxRotationVelocity(vpMath::rad(180));
268
269 // Set initial position of the object in the camera frame
270 vpHomogeneousMatrix cMo(0, 0.1, 2.0, vpMath::rad(35), vpMath::rad(25), 0);
271 // Set desired position of the object in the camera frame
272 vpHomogeneousMatrix cdMo(0.0, 0.0, 0.8, vpMath::rad(0), vpMath::rad(0), vpMath::rad(0));
273 // Set initial position of the object in the world frame
274 vpHomogeneousMatrix wMo(0.0, 0.0, 0, 0, 0, 0);
275 // Position of the camera in the world frame
277 wMc = wMo * cMo.inverse();
278
279 robot.setPosition(wMc);
280
281 // The sphere
282 vpSphere sphere(0, 0, 0, 0.15);
283
284 // Projection of the sphere
285 sphere.track(cMo);
286
287 // Set the current visual feature
289 computeVisualFeatures(sphere, s);
290
291 // Projection of the points
292 sphere.track(cdMo);
293
295 computeVisualFeatures(sphere, sd);
296
297 vpMatrix L(3, 6);
298 computeInteractionMatrix(sd, sphere, L);
299 sd.setInteractionMatrix(L);
300
302 task.setInteractionMatrixType(vpServo::DESIRED);
303
305 vpVelocityTwistMatrix cVe(cMe);
306 task.set_cVe(cVe);
307
308 vpMatrix eJe;
309 robot.get_eJe(eJe);
310 task.set_eJe(eJe);
311
312 task.addFeature(s, sd);
313
314 task.setLambda(7);
315
316 if (opt_plot) {
317 plotter = new vpPlot(2, 480, 640, 750, 550, "Real time curves plotter");
318 plotter->setTitle(0, "Visual features error");
319 plotter->setTitle(1, "Camera velocities");
320 plotter->initGraph(0, task.getDimension());
321 plotter->initGraph(1, 6);
322 plotter->setLegend(0, 0, "error_feat_sx");
323 plotter->setLegend(0, 1, "error_feat_sy");
324 plotter->setLegend(0, 2, "error_feat_sz");
325 plotter->setLegend(1, 0, "vc_x");
326 plotter->setLegend(1, 1, "vc_y");
327 plotter->setLegend(1, 2, "vc_z");
328 plotter->setLegend(1, 3, "wc_x");
329 plotter->setLegend(1, 4, "wc_y");
330 plotter->setLegend(1, 5, "wc_z");
331 }
332
334
335 // Set the scene
337
338 // Initialize simulator frames
339 sim.set_fMo(wMo); // Position of the object in the world reference frame
340 sim.setCameraPositionRelObj(cMo); // Initial position of the object in the camera frame
341 sim.setDesiredCameraPosition(cdMo); // Desired position of the object in the camera frame
342
343 // Set the External camera position
344 vpHomogeneousMatrix camMf(0.0, 0, 3.5, vpMath::rad(0), vpMath::rad(30), 0);
345 sim.setExternalCameraPosition(camMf);
346
347 // Computes the position of a camera which is fixed in the object frame
348 vpHomogeneousMatrix camoMf(0, 0.0, 2.5, 0, vpMath::rad(140), 0);
349 camoMf = camoMf * (sim.get_fMo().inverse());
350
351 // Set the parameters of the cameras (internal and external)
352 vpCameraParameters camera(1000, 1000, 320, 240);
353 sim.setInternalCameraParameters(camera);
354 sim.setExternalCameraParameters(camera);
355
356 int max_iter = 10;
357
358 if (opt_display) {
359 max_iter = 1000;
360 // Get the internal and external views
361 sim.getInternalImage(Iint);
362 sim.getExternalImage(Iext1);
363 sim.getExternalImage(Iext2, camoMf);
364
365 // Display the object frame (current and desired position)
366 vpDisplay::displayFrame(Iint, cMo, camera, 0.2, vpColor::none);
367 vpDisplay::displayFrame(Iint, cdMo, camera, 0.2, vpColor::none);
368
369 // Display the object frame the world reference frame and the camera
370 // frame
371 vpDisplay::displayFrame(Iext1, camMf * sim.get_fMo() * cMo.inverse(), camera, 0.2, vpColor::none);
372 vpDisplay::displayFrame(Iext1, camMf * sim.get_fMo(), camera, 0.2, vpColor::none);
373 vpDisplay::displayFrame(Iext1, camMf, camera, 0.2, vpColor::none);
374
375 // Display the world reference frame and the object frame
376 vpDisplay::displayFrame(Iext2, camoMf, camera, 0.2, vpColor::none);
377 vpDisplay::displayFrame(Iext2, camoMf * sim.get_fMo(), camera, 0.05, vpColor::none);
378
379 vpDisplay::displayText(Iint, 20, 20, "Click to start visual servo", vpColor::red);
380
381 vpDisplay::flush(Iint);
382 vpDisplay::flush(Iext1);
383 vpDisplay::flush(Iext2);
384
385 std::cout << "Click on a display" << std::endl;
386 while (!vpDisplay::getClick(Iint, false) && !vpDisplay::getClick(Iext1, false) &&
387 !vpDisplay::getClick(Iext2, false)) {
388 }
389 }
390
391 // Print the task
392 task.print();
393
394 int iter = 0;
395 bool stop = false;
397
398 double t_prev, t = vpTime::measureTimeMs();
399
400 while (iter++ < max_iter && !stop) {
401 t_prev = t;
403
404 if (opt_display) {
405 vpDisplay::display(Iint);
406 vpDisplay::display(Iext1);
407 vpDisplay::display(Iext2);
408 }
409
410 robot.get_eJe(eJe);
411 task.set_eJe(eJe);
412
413 wMc = robot.getPosition();
414 cMo = wMc.inverse() * wMo;
415
416 sphere.track(cMo);
417
418 // Set the current visual feature
419 computeVisualFeatures(sphere, s);
420
421 v = task.computeControlLaw();
422 robot.setVelocity(vpRobot::CAMERA_FRAME, v);
424
425 // Compute the position of the external view which is fixed in the object frame
426 camoMf.buildFrom(0, 0.0, 2.5, 0, vpMath::rad(150), 0);
427 camoMf = camoMf * (sim.get_fMo().inverse());
428
429 if (opt_plot) {
430 plotter->plot(0, iter, task.getError());
431 plotter->plot(1, iter, v);
432 }
433
434 if (opt_display) {
435 // Get the internal and external views
436 sim.getInternalImage(Iint);
437 sim.getExternalImage(Iext1);
438 sim.getExternalImage(Iext2, camoMf);
439
440 // Display the object frame (current and desired position)
441 vpDisplay::displayFrame(Iint, cMo, camera, 0.2, vpColor::none);
442 vpDisplay::displayFrame(Iint, cdMo, camera, 0.2, vpColor::none);
443
444 // Display the camera frame, the object frame the world reference
445 // frame
446 vpDisplay::displayFrame(Iext1, sim.getExternalCameraPosition() * sim.get_fMo() * cMo.inverse(), camera, 0.2,
450
451 // Display the world reference frame and the object frame
452 vpDisplay::displayFrame(Iext2, camoMf, camera, 0.2, vpColor::none);
453 vpDisplay::displayFrame(Iext2, camoMf * sim.get_fMo(), camera, 0.05, vpColor::none);
454
455 vpDisplay::displayText(Iint, 20, 20, "Click to stop visual servo", vpColor::red);
456
457 std::stringstream ss;
458 ss << "Loop time: " << t - t_prev << " ms";
459 vpDisplay::displayText(Iint, 40, 20, ss.str(), vpColor::red);
460
461 if (vpDisplay::getClick(Iint, false)) {
462 stop = true;
463 }
464
465 vpDisplay::flush(Iint);
466 vpDisplay::flush(Iext1);
467 vpDisplay::flush(Iext2);
468
469 vpTime::wait(t, sampling_time * 1000); // Wait ms
470 }
471
472 std::cout << "|| s - s* || = " << (task.getError()).sumSquare() << std::endl;
473 }
474
475 if (opt_plot && plotter != nullptr) {
476 vpDisplay::display(Iint);
477 sim.getInternalImage(Iint);
478 vpDisplay::displayFrame(Iint, cMo, camera, 0.2, vpColor::none);
479 vpDisplay::displayFrame(Iint, cdMo, camera, 0.2, vpColor::none);
480 vpDisplay::displayText(Iint, 20, 20, "Click to quit", vpColor::red);
481 if (vpDisplay::getClick(Iint)) {
482 stop = true;
483 }
484 vpDisplay::flush(Iint);
485
486 delete plotter;
487 }
488
489 task.print();
490 exit_status = EXIT_SUCCESS;
491 }
492 catch (const vpException &e) {
493 std::cout << "Catch an exception: " << e << std::endl;
494 exit_status = EXIT_FAILURE;
495 }
496
497#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
498 for (unsigned int i = 0; i < NB_DISPLAYS; ++i) {
499 delete display[i];
500 }
501#endif
502
503 return exit_status;
504}
505#elif !(defined(VISP_HAVE_LAPACK) || defined(VISP_HAVE_EIGEN3) || defined(VISP_HAVE_OPENCV))
506int main()
507{
508 std::cout << "Cannot run this example: install Lapack, Eigen3 or OpenCV" << std::endl;
509 return EXIT_SUCCESS;
510}
511#else
512int main()
513{
514 std::cout << "You do not have X11, or GDI (Graphical Device Interface), or GTK functionalities to display images..."
515 << std::endl;
516 std::cout << "Tip if you are on a unix-like system:" << std::endl;
517 std::cout << "- Install X11, configure again ViSP using cmake and build again this example" << std::endl;
518 std::cout << "Tip if you are on a windows-like system:" << std::endl;
519 std::cout << "- Install GDI, configure again ViSP using cmake and build again this example" << std::endl;
520 return EXIT_SUCCESS;
521}
522#endif
Generic class defining intrinsic camera parameters.
Implementation of column vector and the associated operations.
static const vpColor red
Definition vpColor.h:198
static const vpColor none
Definition vpColor.h:210
Class that defines generic functionalities for display.
Definition vpDisplay.h:171
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayFrame(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, double size, const vpColor &color=vpColor::none, unsigned int thickness=1, const vpImagePoint &offset=vpImagePoint(0, 0), const std::string &frameName="", const vpColor &textColor=vpColor::black, const vpImagePoint &textOffset=vpImagePoint(15, 15))
static void flush(const vpImage< unsigned char > &I)
static void setWindowPosition(const vpImage< unsigned char > &I, int winx, int winy)
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
void track(const vpHomogeneousMatrix &cMo)
Class that enables to define a feature or a set of features which are not implemented in ViSP as a sp...
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double rad(double deg)
Definition vpMath.h:129
static double sqr(double x)
Definition vpMath.h:203
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
static bool parse(int *argcPtr, const char **argv, vpArgvInfo *argTable, int flags)
This class enables real time drawing of 2D or 3D graphics. An instance of the class open a window whi...
Definition vpPlot.h:117
virtual void setSamplingTime(const double &delta_t)
@ CAMERA_FRAME
Definition vpRobot.h:81
@ EYEINHAND_L_cVe_eJe
Definition vpServo.h:183
@ DESIRED
Definition vpServo.h:223
Class that defines the simplest robot: a free flying camera.
Class that defines a 3D sphere in the object frame and allows forward projection of a 3D sphere in th...
Definition vpSphere.h:80
double get_n02() const
Definition vpSphere.h:106
double get_y() const
Definition vpSphere.h:102
double get_n11() const
Definition vpSphere.h:105
double get_x() const
Definition vpSphere.h:101
double getR() const
Definition vpSphere.h:111
double get_n20() const
Definition vpSphere.h:104
Class that consider the case of a translation vector.
Implementation of a wire frame simulator. Compared to the vpSimulator class, it does not require thir...
vpHomogeneousMatrix getExternalCameraPosition() const
void setCameraPositionRelObj(const vpHomogeneousMatrix &cMo_)
void getInternalImage(vpImage< unsigned char > &I)
void initScene(const vpSceneObject &obj, const vpSceneDesiredObject &desiredObject)
void setExternalCameraPosition(const vpHomogeneousMatrix &cam_Mf)
void set_fMo(const vpHomogeneousMatrix &fMo_)
vpHomogeneousMatrix get_fMo() const
void setDesiredCameraPosition(const vpHomogeneousMatrix &cdMo_)
void setInternalCameraParameters(const vpCameraParameters &cam)
void setExternalCameraParameters(const vpCameraParameters &cam)
@ SPHERE
A 15cm radius sphere.
void getExternalImage(vpImage< unsigned char > &I)
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)