2#include <visp3/core/vpConfig.h>
3#include <visp3/core/vpMomentAreaNormalized.h>
4#include <visp3/core/vpMomentBasic.h>
5#include <visp3/core/vpMomentCentered.h>
6#include <visp3/core/vpMomentDatabase.h>
7#include <visp3/core/vpMomentGravityCenter.h>
8#include <visp3/core/vpMomentGravityCenterNormalized.h>
9#include <visp3/core/vpMomentObject.h>
10#include <visp3/core/vpPixelMeterConversion.h>
11#include <visp3/core/vpPoint.h>
12#include <visp3/core/vpSerial.h>
13#include <visp3/core/vpXmlParserCamera.h>
14#include <visp3/detection/vpDetectorAprilTag.h>
15#include <visp3/gui/vpDisplayFactory.h>
16#include <visp3/io/vpImageIo.h>
17#include <visp3/robot/vpUnicycle.h>
18#include <visp3/sensor/vpV4l2Grabber.h>
19#include <visp3/visual_features/vpFeatureMomentAreaNormalized.h>
20#include <visp3/visual_features/vpFeatureMomentGravityCenterNormalized.h>
21#include <visp3/vs/vpServo.h>
23void usage(
const char **argv,
int error)
25 std::cout <<
"Synopsis" << std::endl
27 <<
" [--camera-device <id>]"
28 <<
" [--tag-size <size>]"
29 <<
" [--tag-family <family>]"
30 <<
" [--tag-decision-margin-threshold <threshold>]"
31 <<
" [--tag-hamming-distance-threshold <threshold>]"
32 <<
" [--tag-quad-decimate <factor>]"
33 <<
" [--tag-n-threads <number>]"
34 <<
" [--tag-pose-method <method>]"
35#if defined(VISP_HAVE_PUGIXML)
36 <<
" [--intrinsic <xmlfile>]"
37 <<
" [--camera-name <name>]"
39#if defined(VISP_HAVE_DISPLAY)
45 <<
" [--help, -h]" << std::endl
47 std::cout <<
"Description" << std::endl
48 <<
" Image-based visual servoing using an Apriltag." << std::endl
50 <<
" --camera-device <id>" << std::endl
51 <<
" Camera device id." << std::endl
52 <<
" Default: 0" << std::endl
54 <<
" --tag-size <size>" << std::endl
55 <<
" Apriltag size in [m]." << std::endl
56 <<
" Default: 0.03" << std::endl
58 <<
" --tag-family <family>" << std::endl
59 <<
" Apriltag family. Supported values are:" << std::endl
60 <<
" 0: TAG_36h11" << std::endl
61 <<
" 1: TAG_36h10 (DEPRECATED)" << std::endl
62 <<
" 2: TAG_36ARTOOLKIT (DEPRECATED)" << std::endl
63 <<
" 3: TAG_25h9" << std::endl
64 <<
" 4: TAG_25h7 (DEPRECATED)" << std::endl
65 <<
" 5: TAG_16h5" << std::endl
66 <<
" 6: TAG_CIRCLE21h7" << std::endl
67 <<
" 7: TAG_CIRCLE49h12" << std::endl
68 <<
" 8: TAG_CUSTOM48h12" << std::endl
69 <<
" 9: TAG_STANDARD41h12" << std::endl
70 <<
" 10: TAG_STANDARD52h13" << std::endl
71 <<
" 11: TAG_ARUCO_4x4_50" << std::endl
72 <<
" 12: TAG_ARUCO_4x4_100" << std::endl
73 <<
" 13: TAG_ARUCO_4x4_250" << std::endl
74 <<
" 14: TAG_ARUCO_4x4_1000" << std::endl
75 <<
" 15: TAG_ARUCO_5x5_50" << std::endl
76 <<
" 16: TAG_ARUCO_5x5_100" << std::endl
77 <<
" 17: TAG_ARUCO_5x5_250" << std::endl
78 <<
" 18: TAG_ARUCO_5x5_1000" << std::endl
79 <<
" 19: TAG_ARUCO_6x6_50" << std::endl
80 <<
" 20: TAG_ARUCO_6x6_100" << std::endl
81 <<
" 21: TAG_ARUCO_6x6_250" << std::endl
82 <<
" 22: TAG_ARUCO_6x6_1000" << std::endl
83 <<
" 23: TAG_ARUCO_7x7_50" << std::endl
84 <<
" 24: TAG_ARUCO_7x7_100" << std::endl
85 <<
" 25: TAG_ARUCO_7x7_250" << std::endl
86 <<
" 26: TAG_ARUCO_7x7_1000" << std::endl
87 <<
" 27: TAG_ARUCO_MIP_36h12" << std::endl
88 <<
" Default: 0 (36h11)" << std::endl
90 <<
" --tag-decision-margin-threshold <threshold>" << std::endl
91 <<
" Threshold used to discard low-confident detections. A typical value is " << std::endl
92 <<
" around 100. The higher this value, the more false positives will be filtered" << std::endl
93 <<
" out. When this value is set to -1, false positives are not filtered out." << std::endl
94 <<
" Default: 50" << std::endl
96 <<
" --tag-hamming-distance-threshold <threshold>" << std::endl
97 <<
" Threshold used to discard low-confident detections with corrected bits." << std::endl
98 <<
" A typical value is between 0 and 3. The lower this value, the more false" << std::endl
99 <<
" positives will be filtered out." << std::endl
100 <<
" Default: 0" << std::endl
102 <<
" --tag-quad-decimate <factor>" << std::endl
103 <<
" Decimation factor used to detect a tag. " << std::endl
104 <<
" Default: 1" << std::endl
106 <<
" --tag-n-threads <number>" << std::endl
107 <<
" Number of threads used to detect a tag." << std::endl
108 <<
" Default: 1" << std::endl
110#if defined(VISP_HAVE_PUGIXML)
111 <<
" --intrinsic <xmlfile>" << std::endl
112 <<
" Camera intrinsic parameters file in xml format." << std::endl
113 <<
" Default: empty" << std::endl
115 <<
" --camera-name <name>" << std::endl
116 <<
" Camera name in the intrinsic parameters file in xml format." << std::endl
117 <<
" Default: empty" << std::endl
120#if defined(VISP_HAVE_DISPLAY)
121 <<
" --display-tag" << std::endl
122 <<
" Flag used to enable displaying the edges of a tag." << std::endl
123 <<
" Default: disabled" << std::endl
125 <<
" --display-on" << std::endl
126 <<
" Flag used to turn display on." << std::endl
127 <<
" Default: disabled" << std::endl
129 <<
" --save-image" << std::endl
130 <<
" Flag used to save images with overlay drawings." << std::endl
131 <<
" Default: disabled" << std::endl
134 <<
" --serial-off" << std::endl
135 <<
" Flag used to disable serial link." << std::endl
136 <<
" Default: enabled" << std::endl
138 <<
" --help, -h" << std::endl
139 <<
" Print this helper message." << std::endl
143 std::cout <<
"Error" << std::endl
145 <<
"Unsupported parameter " << argv[
error] << std::endl;
149int main(
int argc,
const char **argv)
151#if defined(VISP_HAVE_APRILTAG) && defined(VISP_HAVE_V4L2)
152#ifdef ENABLE_VISP_NAMESPACE
158 double opt_tag_size = 0.065;
159 float opt_tag_quad_decimate = 4.0;
160 float opt_tag_decision_margin_threshold = 50;
161 float opt_tag_hamming_distance_threshold = 2;
162 int opt_tag_nThreads = 2;
163 std::string intrinsic_file =
"";
165 bool display_on =
false;
166 bool serial_off =
false;
167#if defined(VISP_HAVE_DISPLAY)
168 bool display_tag =
false;
169 bool save_image =
false;
172 for (
int i = 1;
i < argc;
i++) {
173 if (std::string(argv[i]) ==
"--camera-device" && i + 1 < argc) {
174 device = std::atoi(argv[++i]);
176 else if (std::string(argv[i]) ==
"--tag-size" && i + 1 < argc) {
177 opt_tag_size = std::atof(argv[++i]);
179 else if (std::string(argv[i]) ==
"--tag-family" && i + 1 < argc) {
182 else if (std::string(argv[i]) ==
"--tag-decision-margin-threshold" && i + 1 < argc) {
183 opt_tag_decision_margin_threshold =
static_cast<float>(atof(argv[++i]));
185 else if (std::string(argv[i]) ==
"--tag-hamming-distance-threshold" && i + 1 < argc) {
186 opt_tag_hamming_distance_threshold = atoi(argv[++i]);
188 else if (std::string(argv[i]) ==
"--tag-quad-decimate" && i + 1 < argc) {
189 opt_tag_quad_decimate =
static_cast<float>(atof(argv[++i]));
191 else if (std::string(argv[i]) ==
"--tag-n-threads" && i + 1 < argc) {
192 opt_tag_nThreads = std::atoi(argv[++i]);
194#if defined(VISP_HAVE_PUGIXML)
195 else if (std::string(argv[i]) ==
"--intrinsic" && i + 1 < argc) {
196 intrinsic_file = std::string(argv[++i]);
198 else if (std::string(argv[i]) ==
"--camera-name" && i + 1 < argc) {
202#if defined(VISP_HAVE_DISPLAY)
203 else if (std::string(argv[i]) ==
"--display-tag") {
206 else if (std::string(argv[i]) ==
"--display-on") {
209 else if (std::string(argv[i]) ==
"--save-image") {
213 else if (std::string(argv[i]) ==
"--serial-off") {
216 else if (std::string(argv[i]) ==
"--help" || std::string(argv[i]) ==
"-h") {
235 serial =
new vpSerial(
"/dev/ttyAMA0", 115200);
237 serial->
write(
"LED_RING=0,0,0,0\n");
238 serial->
write(
"LED_RING=1,0,10,0\n");
245 std::ostringstream device_name;
246 device_name <<
"/dev/video" << device;
253#ifdef VISP_HAVE_DISPLAY
260 cam.initPersProjWithoutDistortion(615.1674805, 615.1675415, I.getWidth() / 2., I.getHeight() / 2.);
262#if defined(VISP_HAVE_PUGIXML)
264 if (!intrinsic_file.empty() && !
camera_name.empty()) {
269 std::cout <<
cam << std::endl;
270 std::cout <<
"Tag detector settings" << std::endl;
271 std::cout <<
" Tag size [m] : " << opt_tag_size << std::endl;
272 std::cout <<
" Tag family : " << opt_tag_family << std::endl;
273 std::cout <<
" Quad decimate : " << opt_tag_quad_decimate << std::endl;
274 std::cout <<
" Decision margin threshold : " << opt_tag_decision_margin_threshold << std::endl;
275 std::cout <<
" Hamming distance threshold: " << opt_tag_hamming_distance_threshold << std::endl;
276 std::cout <<
" Num threads : " << opt_tag_nThreads << std::endl;
280 detector.setAprilTagQuadDecimate(opt_tag_quad_decimate);
281 detector.setAprilTagNbThreads(opt_tag_nThreads);
282 detector.setAprilTagDecisionMarginThreshold(opt_tag_decision_margin_threshold);
283 detector.setAprilTagHammingDistanceThreshold(opt_tag_hamming_distance_threshold);
284#ifdef VISP_HAVE_DISPLAY
285 detector.setDisplayTag(display_tag);
300 task.setLambda(lambda);
317 eJe[0][0] = eJe[5][1] = 1.0;
319 std::cout <<
"eJe: \n" << eJe << std::endl;
325 double X[4] = { opt_tag_size / 2., +opt_tag_size / 2., -opt_tag_size / 2., -opt_tag_size / 2. };
326 double Y[4] = { opt_tag_size / 2., -opt_tag_size / 2., -opt_tag_size / 2., +opt_tag_size / 2. };
327 std::vector<vpPoint> vec_P, vec_P_d;
329 for (
int i = 0;
i < 4;
i++) {
333 vec_P_d.push_back(P_d);
347 m_obj_d.fromVector(vec_P_d);
360 area = mb_d.
get(2, 0) + mb_d.
get(0, 2);
362 area = mb_d.
get(0, 0);
364 man_d.setDesiredArea(area);
372 double C = 1.0 / Z_d;
380 task.addFeature(s_man, s_man_d);
383 s_mgn_d.update(A, B, C);
384 s_mgn_d.compute_interaction();
386 s_man_d.update(A, B, C);
387 s_man_d.compute_interaction();
389 std::vector<double> time_vec;
393#ifdef VISP_HAVE_DISPLAY
398 std::vector<vpHomogeneousMatrix> cMo_vec;
399 detector.detect(I, opt_tag_size, cam, cMo_vec);
401 time_vec.push_back(t);
404 std::stringstream ss;
405 ss <<
"Detection time: " <<
t <<
" ms";
406#ifdef VISP_HAVE_DISPLAY
411 if (detector.getNbObjects() == 1) {
413 serial->
write(
"LED_RING=2,0,10,0\n");
417 std::vector<vpImagePoint> vec_ip = detector.getPolygon(0);
419 for (
size_t i = 0;
i < vec_ip.size();
i++) {
428#ifdef VISP_HAVE_DISPLAY
439 m_obj.fromVector(vec_P);
453 s_mgn.update(A, B, C);
454 s_mgn.compute_interaction();
455 s_man.update(A, B, C);
456 s_man.compute_interaction();
464 std::cout <<
"Send velocity to the mbot: " <<
v[0] <<
" m/s " <<
vpMath::deg(v[1]) <<
" deg/s" << std::endl;
467 double radius = 0.0325;
469 double motor_left = (-
v[0] - L *
v[1]) / radius;
470 double motor_right = (
v[0] - L *
v[1]) / radius;
471 std::cout <<
"motor left vel: " << motor_left <<
" motor right vel: " << motor_right << std::endl;
476 std::stringstream ss;
477 double rpm_left = motor_left * 30. / M_PI;
478 double rpm_right = motor_right * 30. / M_PI;
480 std::cout <<
"Send: " << ss.str() << std::endl;
482 serial->
write(ss.str());
488 serial->
write(
"LED_RING=2,10,0,0\n");
491 serial->
write(
"MOTOR_RPM=0,-0\n");
495#ifdef VISP_HAVE_DISPLAY
499 if (display_on && save_image) {
511 serial->
write(
"LED_RING=0,0,0,0\n");
514 std::cout <<
"Benchmark computation time" << std::endl;
515 std::cout <<
"Mean / Median / Std: " <<
vpMath::getMean(time_vec) <<
" ms"
527 std::cerr <<
"Catch an exception: " <<
e.getMessage() << std::endl;
529 serial->
write(
"LED_RING=1,10,0,0\n");
537#ifndef VISP_HAVE_APRILTAG
538 std::cout <<
"ViSP is not build with Apriltag support" << std::endl;
540#ifndef VISP_HAVE_V4L2
541 std::cout <<
"ViSP is not build with v4l2 support" << std::endl;
543 std::cout <<
"Install missing 3rd parties, configure and build ViSP to run this tutorial" << std::endl;
Adaptive gain computation.
void initStandard(double gain_at_zero, double gain_at_infinity, double slope_at_zero)
Generic class defining intrinsic camera parameters.
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
Implementation of column vector and the associated operations.
static const vpColor green
@ TAG_36h11
AprilTag 36h11 pattern (recommended).
Class that defines generic functionalities for display.
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void getImage(const vpImage< unsigned char > &Is, vpImage< vpRGBa > &Id)
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
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)
static void displayPolygon(const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &vip, const vpColor &color, unsigned int thickness=1, bool closed=true)
error that can be emitted by ViSP classes.
Functionality computation for normalized surface moment feature. Computes the interaction matrix asso...
Functionality computation for centered and normalized moment feature. Computes the interaction matrix...
static unsigned int selectXn()
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
static double getMedian(const std::vector< double > &v)
static double getStdev(const std::vector< double > &v, bool useBesselCorrection=false)
static int round(double x)
static double getMean(const std::vector< double > &v)
static double deg(double rad)
Implementation of a matrix and operations on matrices.
Class handling the normalized surface moment that is invariant in scale and used to estimate depth.
This class defines the 2D basic moment . This class is a wrapper for vpMomentObject which allows to u...
const std::vector< double > & get() const
This class defines the double-indexed centered moment descriptor .
void compute() VP_OVERRIDE
This class allows to register all vpMoments so they can access each other according to their dependen...
virtual void updateAll(vpMomentObject &object)
Class describing 2D normalized gravity center moment.
void compute() VP_OVERRIDE
Class describing 2D gravity center moment.
void compute() VP_OVERRIDE
Class for generic objects.
void linkTo(vpMomentDatabase &moments)
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
void set_x(double x)
Set the point x coordinate in the image plane.
void set_y(double y)
Set the point y coordinate in the image plane.
Implementation of a rotation matrix and operations on such kind of matrices.
void write(const std::string &s)
Class that consider the case of a translation vector.
Generic functions for unicycle mobile robots.
Class that is a wrapper over the Video4Linux2 (V4L2) driver.
void setScale(unsigned scale=vpV4l2Grabber::DEFAULT_SCALE)
void setDevice(const std::string &devname)
void acquire(vpImage< unsigned char > &I)
XML parser to load and save intrinsic camera parameters.
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.
VISP_EXPORT double measureTimeMs()