Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
servoAfma6Cylinder2DCamVelocity.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 * tests the control law
32 * eye-in-hand control
33 * velocity computed in the camera frame
34 */
35
45
46#include <iostream>
47
48#include <visp3/core/vpConfig.h>
49
50#if defined(VISP_HAVE_REALSENSE2) && defined(VISP_HAVE_AFMA6)
51
52#include <visp3/core/vpImage.h>
53#include <visp3/gui/vpDisplayFactory.h>
54#include <visp3/io/vpImageIo.h>
55#include <visp3/sensor/vpRealSense2.h>
56#include <visp3/core/vpCylinder.h>
57#include <visp3/core/vpHomogeneousMatrix.h>
58#include <visp3/core/vpMath.h>
59#include <visp3/me/vpMeLine.h>
60#include <visp3/visual_features/vpFeatureBuilder.h>
61#include <visp3/visual_features/vpFeatureLine.h>
62#include <visp3/robot/vpRobotAfma6.h>
63#include <visp3/vs/vpServoDisplay.h>
64#include <visp3/vs/vpServo.h>
65
66#ifdef ENABLE_VISP_NAMESPACE
67using namespace VISP_NAMESPACE_NAME;
68#endif
69
74void match(vpFeatureLine const s_line1, vpFeatureLine const s_line2, vpFeatureLine s_line_di, vpFeatureLine s_line_dj)
75{
76 double rho1 = s_line1.getRho();
77 double theta1 = s_line1.getTheta();
78 double rho2 = s_line2.getRho();
79 double theta2 = s_line2.getTheta();
80 double rhoi = s_line_di.getRho();
81 double thetai = s_line_di.getTheta();
82 double rhoj = s_line_dj.getRho();
83 double thetaj = s_line_dj.getTheta();
84
85 unsigned int config = 0;
86 double err = 0.0;
87 double err_min = 1.e6;
88 // Test 1: (1,2) with (i,j)
89 double er_theta1 = theta1-thetai;
90 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
91 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
92 double er_theta2 = theta2-thetaj;
93 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
94 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
95
96 err = (rho1-rhoi)*(rho1-rhoi) + er_theta1*er_theta1
97 + (rho2-rhoj)*(rho2-rhoj) + er_theta2*er_theta2;
98 if (err < err_min) {
99 config++;
100 err_min = err;
101 }
102 std::cout << "Test 1 (1,2) <-> (i,j), err = " << err << std::endl;
103 // Test 2: (1,2) with (i,-j) thetaj += pi, rhoj *= -1
104 er_theta1 = theta1-thetai;
105 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
106 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
107 er_theta2 = theta2-thetaj-M_PI;
108 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
109 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
110
111 err = (rho1-rhoi)*(rho1-rhoi) + er_theta1*er_theta1
112 + (rho2+rhoj)*(rho2+rhoj) + er_theta2*er_theta2;
113 if (err < err_min) {
114 config++;
115 err_min = err;
116 }
117 std::cout << "Test 2 (1,2) <-> (i,-j), err = " << err << std::endl;
118 // Test 3: (1,2) with (-i,j) thetai += pi, rhoi *= -1
119 er_theta1 = theta1-thetai-M_PI;
120 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
121 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
122 er_theta2 = theta2-thetaj;
123 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
124 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
125
126 err = (rho1+rhoi)*(rho1+rhoi) + er_theta1*er_theta1
127 + (rho2-rhoj)*(rho2-rhoj) + er_theta2*er_theta2;
128 if (err < err_min) {
129 config++;
130 err_min = err;
131 }
132 std::cout << "Test 3 (1,2) <-> (-i,j), err = " << err << std::endl;
133 // Test 4: (1,2) with (-i,-j) thetai += pi, rhoi *= -1, thetaj += pi, rhoj *= -1
134 er_theta1 = theta1-thetai-M_PI;
135 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
136 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
137 er_theta2 = theta2-thetaj-M_PI;
138 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
139 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
140
141 err = (rho1+rhoi)*(rho1+rhoi) + er_theta1*er_theta1
142 + (rho2+rhoj)*(rho2+rhoj) + er_theta2*er_theta2;
143 if (err < err_min) {
144 config++;
145 err_min = err;
146 }
147 std::cout << "Test 4 (1,2) <-> (-i,-j), err = " << err << std::endl;
148 // Test 5: (1,2) with (j,i)
149 er_theta1 = theta1-thetaj;
150 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
151 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
152 er_theta2 = theta2-thetai;
153 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
154 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
155
156 err = (rho1-rhoj)*(rho1-rhoj) + er_theta1*er_theta1
157 + (rho2-rhoi)*(rho2-rhoi) + er_theta2*er_theta2;
158 if (err < err_min) {
159 config++;
160 err_min = err;
161 }
162 std::cout << "Test 5 (1,2) <-> (j,i), err = " << err << std::endl;
163 // Test 6: (1,2) with (-j,i) thetaj += pi, rhoj *= -1
164 er_theta1 = theta1-thetaj-M_PI;
165 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
166 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
167 er_theta2 = theta2-thetai;
168 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
169 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
170
171 err = (rho1+rhoj)*(rho1+rhoj) + er_theta1*er_theta1
172 + (rho2-rhoi)*(rho2-rhoi) + er_theta2*er_theta2;
173 if (err < err_min) {
174 config++;
175 err_min = err;
176 }
177 std::cout << "Test 6 (1,2) <-> (-j,i), err = " << err << std::endl;
178 // Test 7: (1,2) with (j,-i) thetai += pi, rhoi *= -1
179 er_theta1 = theta1-thetaj;
180 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
181 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
182 er_theta2 = theta2-thetai-M_PI;
183 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
184 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
185
186 err = (rho1-rhoj)*(rho1-rhoj) + er_theta1*er_theta1
187 + (rho2+rhoi)*(rho2+rhoi) + er_theta2*er_theta2;
188 if (err < err_min) {
189 config++;
190 err_min = err;
191 }
192 std::cout << "Test 7 (1,2) <-> (j,-i), err = " << err << std::endl;
193 // Test 8: (1,2) with (-j,-i) thetaj += pi, rhoj *= -1, thetai += pi, rhoi *= -1
194 er_theta1 = theta1-thetaj-M_PI;
195 if (er_theta1 > M_PI) er_theta1 -= 2.0*M_PI;
196 else if (er_theta1 < -M_PI) er_theta1 += 2.0*M_PI;
197 er_theta2 = theta2-thetai-M_PI;
198 if (er_theta2 > M_PI) er_theta2 -= 2.0*M_PI;
199 else if (er_theta2 < -M_PI) er_theta2 += 2.0*M_PI;
200
201 err = (rho1+rhoj)*(rho1+rhoj) + er_theta1*er_theta1
202 + (rho2+rhoi)*(rho2+rhoi) + er_theta2*er_theta2;
203 if (err < err_min) {
204 config++;
205 err_min = err;
206 }
207 std::cout << "Test 8 (1,2) <-> (-j,-i), err = " << err << std::endl;
208 std::cout << "Test choisi = " << config << std::endl;
209
210 // If config == 1, no change: (1,2) <-> (i,j)
211 if (config == 2) { // (1,2) <-> (-i,j)
212 s_line_di.setRhoTheta(-rhoi, (thetai + M_PI));
213 }
214 else if (config == 3) { // (1,2) <-> (i,-j)
215 s_line_dj.setRhoTheta(-rhoj, (thetaj + M_PI));
216 }
217 else if (config == 4) { // (1,2) <-> (-i,-j)
218 s_line_di.setRhoTheta(-rhoi, (thetai + M_PI));
219 s_line_dj.setRhoTheta(-rhoj, (thetaj + M_PI));
220 }
221 else if (config == 5) { // (1,2) <-> (j,i)
222 s_line_di.setRhoTheta(rhoj, thetaj);
223 s_line_dj.setRhoTheta(rhoi, thetai);
224 }
225 else if (config == 6) { // (1,2) <-> (-j,i)
226 s_line_di.setRhoTheta(-rhoj, (thetaj + M_PI));
227 s_line_dj.setRhoTheta(rhoi, thetai);
228 }
229 else if (config == 7) { // (1,2) <-> (j,-i)
230 s_line_di.setRhoTheta(rhoj, thetaj);
231 s_line_dj.setRhoTheta(-rhoi, (thetai + M_PI));
232 }
233 else if (config == 8) { // (1,2) <-> (-j,-i)
234 s_line_di.setRhoTheta(-rhoj, (thetaj + M_PI));
235 s_line_dj.setRhoTheta(-rhoi, (thetai + M_PI));
236 }
237}
238
247int main(int argc, char **argv)
248{
249 bool opt_verbose = false;
250 bool opt_adaptive_gain = false;
251
252 for (int i = 1; i < argc; ++i) {
253 if (std::string(argv[i]) == "--verbose") {
254 opt_verbose = true;
255 }
256 else if (std::string(argv[i]) == "--adaptive-gain") {
257 opt_adaptive_gain = true;
258 }
259 else if ((std::string(argv[i]) == "--help") || (std::string(argv[i]) == "-h")) {
260 std::cout
261 << argv[0]
262 << " [--adaptive-gain]"
263 << " [--verbose]"
264 << " [--help] [-h]"
265 << std::endl;
266 return EXIT_SUCCESS;
267 }
268 }
269
270 vpRobotAfma6 robot;
272
273 // Load the end-effector to camera frame transformation obtained
274 // using a camera intrinsic model with distortion
275 robot.init(vpAfma6::TOOL_INTEL_D435_CAMERA, projModel);
276
277#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
278 std::shared_ptr<vpDisplay> display;
279#else
280 vpDisplay *display = nullptr;
281#endif
282
283 try {
284 std::cout << "WARNING: This example will move the robot! "
285 << "Please make sure to have the user stop button at hand!" << std::endl
286 << "Press Enter to continue..." << std::endl;
287 std::cin.ignore();
288
289 vpRealSense2 rs;
290 rs2::config config;
291 unsigned int width = 640, height = 480, fps = 60;
292 config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_RGBA8, fps);
293 config.enable_stream(RS2_STREAM_DEPTH, width, height, RS2_FORMAT_Z16, fps);
294 config.enable_stream(RS2_STREAM_INFRARED, width, height, RS2_FORMAT_Y8, fps);
295 rs.open(config);
296
297 // Warm up camera
299 for (size_t i = 0; i < 10; ++i) {
300 rs.acquire(I);
301 }
302
303#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
304 std::shared_ptr<vpDisplay> display = vpDisplayFactory::createDisplay(I, 100, 100, "Current image");
305#else
306 vpDisplay *display = vpDisplayFactory::allocateDisplay(I, 100, 100, "Current image");
307#endif
310
311 int nblines = 2;
312 std::vector<vpMeLine> line(nblines);
313
314 vpMe me;
315 me.setRange(10);
316 me.setPointsToTrack(100);
318 me.setThreshold(15);
319 me.setSampleStep(10);
320
321 // Initialize the tracking of the two edges of the cylinder
322 for (int i = 0; i < nblines; ++i) {
323 line[i].setDisplay(vpMeSite::RANGE_RESULT);
324 line[i].setMe(&me);
325
326 line[i].initTracking(I);
327 line[i].track(I);
329 }
330
331 // Get camera intrinsics
333 robot.getCameraParameters(cam, I);
334 std::cout << "cam:\n" << cam << std::endl;
335
336 // Sets the current position of the visual feature ");
337 std::vector<vpFeatureLine> s_line(nblines);
338 for (int i = 0; i < nblines; ++i) {
339 vpFeatureBuilder::create(s_line[i], cam, line[i]);
340 }
341
342 // Sets the desired position of the visual feature ");
343 vpCylinder cylinder(0, 1, 0, 0, 0, 0, 0.04);
344
345 vpHomogeneousMatrix c_M_o(0, 0, 0.4, vpMath::rad(0), vpMath::rad(0), vpMath::rad(0));
346
347 cylinder.project(c_M_o);
348
349 std::vector<vpFeatureLine> s_line_d(nblines);
350 vpFeatureBuilder::create(s_line_d[0], cylinder, vpCylinder::line1);
351 vpFeatureBuilder::create(s_line_d[1], cylinder, vpCylinder::line2);
352
353 {
354 std::cout << "Measured features: " << std::endl;
355 std::cout << " - line 1: rho: " << s_line[0].getRho() << " theta: " << vpMath::deg(s_line[0].getTheta()) << "deg" << std::endl;
356 std::cout << " - line 2: rho: " << s_line[1].getRho() << " theta: " << vpMath::deg(s_line[1].getTheta()) << "deg" << std::endl;
357 std::cout << "Desired features: " << std::endl;
358 std::cout << " - line 1: rho: " << s_line_d[0].getRho() << " theta: " << vpMath::deg(s_line_d[0].getTheta()) << "deg" << std::endl;
359 std::cout << " - line 2: rho: " << s_line_d[1].getRho() << " theta: " << vpMath::deg(s_line_d[1].getTheta()) << "deg" << std::endl;
360 }
361
362 match(s_line[0], s_line[1], s_line_d[0], s_line_d[1]);
363
364 {
365 std::cout << "Modified desired features: " << std::endl;
366 std::cout << " - line 1: rho: " << s_line_d[0].getRho() << " theta: " << vpMath::deg(s_line_d[0].getTheta()) << "deg" << std::endl;
367 std::cout << " - line 2: rho: " << s_line_d[1].getRho() << " theta: " << vpMath::deg(s_line_d[1].getTheta()) << "deg" << std::endl;
368 }
369 // Define the task
371 // - we want an eye-in-hand control law
372 // - robot is controlled in the camera frame
374 task.setInteractionMatrixType(vpServo::DESIRED, vpServo::PSEUDO_INVERSE);
375 // - we want to see a two lines on two lines
376 for (int i = 0; i < nblines; ++i) {
377 task.addFeature(s_line[i], s_line_d[i]);
378 }
379
380 // Set the gain
381 if (opt_adaptive_gain) {
382 vpAdaptiveGain lambda(1.5, 0.4, 30); // lambda(0)=4, lambda(oo)=0.4 and lambda'(0)=30
383 task.setLambda(lambda);
384 }
385 else {
386 task.setLambda(0.5);
387 }
388
389 // Display task information
390 task.print();
391 robot.setRobotState(vpRobot::STATE_VELOCITY_CONTROL);
392
393 vpColVector v_c(6);
394 bool final_quit = false;
395 bool send_velocities = false;
396
397 while (!final_quit) {
398 double t_start = vpTime::measureTimeMs();
399
400 rs.acquire(I);
402
403 std::stringstream ss;
404 ss << "Left click to " << (send_velocities ? "stop the robot" : "servo the robot") << ", right click to quit.";
405 vpDisplay::displayText(I, 20, 20, ss.str(), vpColor::red);
406
407 // Track the two edges and update the features
408 for (int i = 0; i < nblines; ++i) {
409 line[i].track(I);
410 line[i].display(I, vpColor::red);
411
412 vpFeatureBuilder::create(s_line[i], cam, line[i]);
413 //std::cout << "line " << i << " rho: " << s[i].getRho() << " theta: " << vpMath::deg(s[i].getTheta()) << " deg" << std::endl;
414
415 s_line[i].display(cam, I, vpColor::red);
416 s_line_d[i].display(cam, I, vpColor::green);
417 }
418
419 v_c = task.computeControlLaw();
420
421 if (opt_verbose) {
422 std::cout << "v: " << v_c.t() << std::endl;
423 std::cout << "\t\t || s - s* || = " << task.getError().sumSquare() << std::endl;
424 }
425
426 if (!send_velocities) {
427 v_c = 0;
428 }
429
430 robot.setVelocity(vpRobot::CAMERA_FRAME, v_c);
431
432 ss.str("");
433 ss << "Loop time: " << vpTime::measureTimeMs() - t_start << " ms";
434 vpDisplay::displayText(I, 40, 20, ss.str(), vpColor::red);
436
438 if (vpDisplay::getClick(I, button, false)) {
439 switch (button) {
441 send_velocities = !send_velocities;
442 break;
443
445 final_quit = true;
446 break;
447
448 default:
449 break;
450 }
451 }
452 }
453
454 std::cout << "Stop the robot " << std::endl;
455 robot.setRobotState(vpRobot::STATE_STOP);
456
457 if (!final_quit) {
458 while (!final_quit) {
459 rs.acquire(I);
461
462 vpDisplay::displayText(I, 20, 20, "Click to quit the program.", vpColor::red);
463 vpDisplay::displayText(I, 40, 20, "Visual servo converged.", vpColor::red);
464
465 if (vpDisplay::getClick(I, false)) {
466 final_quit = true;
467 }
468
470 }
471 }
472 task.print();
473 }
474 catch (const vpException &e) {
475 std::cout << "ViSP exception: " << e.what() << std::endl;
476 std::cout << "Stop the robot " << std::endl;
477 robot.setRobotState(vpRobot::STATE_STOP);
478#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
479 if (display != nullptr) {
480 delete display;
481 }
482#endif
483 return EXIT_FAILURE;
484 }
485
486#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
487 if (display != nullptr) {
488 delete display;
489 }
490#endif
491 return EXIT_SUCCESS;
492}
493
494#else
495int main()
496{
497 std::cout << "You do not have an afma6 robot connected to your computer..." << std::endl;
498 return EXIT_SUCCESS;
499}
500
501#endif
Adaptive gain computation.
@ TOOL_INTEL_D435_CAMERA
Definition vpAfma6.h:129
Generic class defining intrinsic camera parameters.
@ perspectiveProjWithDistortion
Perspective projection with distortion model.
Implementation of column vector and the associated operations.
static const vpColor red
Definition vpColor.h:198
static const vpColor green
Definition vpColor.h:201
Class that defines a 3D cylinder in the object frame and allows forward projection of a 3D cylinder i...
Definition vpCylinder.h:101
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 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
static void create(vpFeaturePoint &s, const vpCameraParameters &cam, const vpDot &d)
Class that defines a 2D line visual feature which is composed by two parameters that are and ,...
void setRhoTheta(double rho, double theta)
double getTheta() const
double getRho() const
Implementation of an homogeneous matrix and operations on such kind of matrices.
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double rad(double deg)
Definition vpMath.h:129
static double deg(double rad)
Definition vpMath.h:119
@ RANGE_RESULT
Definition vpMeSite.h:85
Definition vpMe.h:143
void setPointsToTrack(const int &points_to_track)
Definition vpMe.h:431
void setRange(const unsigned int &range)
Definition vpMe.h:438
void setLikelihoodThresholdType(const vpLikelihoodThresholdType likelihood_threshold_type)
Definition vpMe.h:531
void setThreshold(const double &threshold)
Definition vpMe.h:489
void setSampleStep(const double &sample_step)
Definition vpMe.h:445
@ NORMALIZED_THRESHOLD
Definition vpMe.h:154
void acquire(vpImage< unsigned char > &grey, double *ts=nullptr)
bool open(const rs2::config &cfg=rs2::config())
Control of Irisa's gantry robot named Afma6.
@ CAMERA_FRAME
Definition vpRobot.h:81
@ STATE_VELOCITY_CONTROL
Initialize the velocity controller.
Definition vpRobot.h:64
@ STATE_STOP
Stops robot motion especially in velocity and acceleration control.
Definition vpRobot.h:63
@ EYEINHAND_CAMERA
Definition vpServo.h:176
@ PSEUDO_INVERSE
Definition vpServo.h:250
@ DESIRED
Definition vpServo.h:223
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()