Visual Servoing Platform  version 3.5.1 under development (2022-07-06)
tutorial-hand-eye-calibration.cpp
1 #include <map>
3 
4 #include <visp3/core/vpIoTools.h>
5 #include <visp3/vision/vpHandEyeCalibration.h>
6 
7 void usage(const char *argv[], int error)
8 {
9  std::cout << "Synopsis" << std::endl
10  << " " << argv[0] << " [--data-path <path>] [--help] [-h]" << std::endl
11  << std::endl;
12  std::cout << "Description" << std::endl
13  << " --data-path <path> Path to the folder containing" << std::endl
14  << " pose_fPe_%d.yaml and pose_cPo_%d.yaml data files." << std::endl
15  << " Default: \"./\"" << std::endl
16  << std::endl
17  << " --fPe <generic name> Generic name of the yaml files" << std::endl
18  << " containing the pose of the end-effector expressed in the robot base" << std::endl
19  << " frame and located in the data path folder." << std::endl
20  << " Default: pose_fPe_%d.yaml" << std::endl
21  << std::endl
22  << " --cPo <generic name> Generic name of the yaml files" << std::endl
23  << " containing the pose of the calibration grid expressed in the camera" << std::endl
24  << " frame and located in the data path folder." << std::endl
25  << " Default: pose_cPo_%d.yaml" << std::endl
26  << std::endl
27  << " --output <filename> File in yaml format containing the pose of the camera" << std::endl
28  << " in the end-effector frame. Data are saved as a pose vector" << std::endl
29  << " with first the 3 translations along X,Y,Z in [m]" << std::endl
30  << " and then the 3 rotations in axis-angle representation (thetaU) in [rad]." << std::endl
31  << " Default: eMc.yaml" << std::endl
32  << std::endl
33  << " --help, -h Print this helper message." << std::endl
34  << std::endl;
35  if (error) {
36  std::cout << "Error" << std::endl
37  << " "
38  << "Unsupported parameter " << argv[error] << std::endl;
39  }
40 }
41 
42 int main(int argc, const char *argv[])
43 {
44  std::string opt_data_path = "./";
45  std::string opt_fPe_files = "pose_fPe_%d.yaml";
46  std::string opt_cPo_files = "pose_cPo_%d.yaml";
47  std::string opt_eMc_file = "eMc.yaml";
48 
49  for (int i = 1; i < argc; i++) {
50  if (std::string(argv[i]) == "--data-path" && i + 1 < argc) {
51  opt_data_path = std::string(argv[i + 1]);
52  i++;
53  } else if (std::string(argv[i]) == "--fPe" && i + 1 < argc) {
54  opt_fPe_files = std::string(argv[i + 1]);
55  i++;
56  } else if (std::string(argv[i]) == "--cPo" && i + 1 < argc) {
57  opt_cPo_files = std::string(argv[i + 1]);
58  i++;
59  } else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
60  opt_eMc_file = std::string(argv[i + 1]);
61  i++;
62  } else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
63  usage(argv, 0);
64  return EXIT_SUCCESS;
65  } else {
66  usage(argv, i);
67  return EXIT_FAILURE;
68  }
69  }
70 
71  std::vector<vpHomogeneousMatrix> cMo;
72  std::vector<vpHomogeneousMatrix> wMe;
74 
75  std::map<long, std::string> map_fPe_files;
76  std::map<long, std::string> map_cPo_files;
77  std::vector<std::string> files = vpIoTools::getDirFiles(opt_data_path);
78  for (unsigned int i = 0; i < files.size(); i++) {
79  long index_fPe = vpIoTools::getIndex(files[i], opt_fPe_files);
80  long index_cPo = vpIoTools::getIndex(files[i], opt_cPo_files);
81  if (index_fPe != -1) {
82  map_fPe_files[index_fPe] = files[i];
83  }
84  if (index_cPo != -1) {
85  map_cPo_files[index_cPo] = files[i];
86  }
87  }
88 
89  if (map_fPe_files.size() == 0) {
90  std::cout << "No " << opt_fPe_files
91  << " files found. Use --data-path <path> or --fPe <generic name> be able to read your data." << std::endl;
92  return EXIT_FAILURE;
93  }
94  if (map_cPo_files.size() == 0) {
95  std::cout << "No " << opt_cPo_files
96  << " files found. Use --data-path <path> or --cPo <generic name> be able to read your data." << std::endl;
97  return EXIT_FAILURE;
98  }
99 
100  for (std::map<long, std::string>::const_iterator it_fPe = map_fPe_files.begin(); it_fPe != map_fPe_files.end();
101  ++it_fPe) {
102  std::string file_fPe = it_fPe->second;
103  std::map<long, std::string>::const_iterator it_cPo = map_cPo_files.find(it_fPe->first);
104  if (it_cPo != map_cPo_files.end()) {
105  std::string file_cPo = it_cPo->second;
106  vpPoseVector wPe;
107  if (wPe.loadYAML(file_fPe, wPe) == false) {
108  std::cout << "Unable to read data from " << opt_data_path << "/" << file_fPe << ". Skip data" << std::endl;
109  continue;
110  }
111 
112  vpPoseVector cPo;
113  if (cPo.loadYAML(file_cPo, cPo) == false) {
114  std::cout << "Unable to read data from " << opt_data_path << "/" << file_cPo << ". Skip data" << std::endl;
115  continue;
116  }
117  std::cout << "Use data from " << opt_data_path << "/" << file_fPe << " and from " << opt_data_path << "/"
118  << file_cPo << std::endl;
119  wMe.push_back(vpHomogeneousMatrix(wPe));
120  cMo.push_back(vpHomogeneousMatrix(cPo));
121  }
122  }
123 
124  if (wMe.size() < 3) {
125  std::cout << "Not enough data pairs found." << std::endl;
126  return EXIT_FAILURE;
127  }
128 
129  int ret = vpHandEyeCalibration::calibrate(cMo, wMe, eMc);
130 
131  if (ret == 0) {
132  std::cout << std::endl << "** Hand-eye calibration succeed" << std::endl;
133  std::cout << std::endl << "** Hand-eye (eMc) transformation estimated:" << std::endl;
134  std::cout << eMc << std::endl;
135  std::cout << "** Corresponding pose vector: " << vpPoseVector(eMc).t() << std::endl;
136 
138  std::cout << std::endl << "** Translation [m]: " << eMc[0][3] << " " << eMc[1][3] << " " << eMc[2][3] << std::endl;
139  std::cout << "** Rotation (theta-u representation) [rad]: " << erc.t() << std::endl;
140  std::cout << "** Rotation (theta-u representation) [deg]: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1])
141  << " " << vpMath::deg(erc[2]) << std::endl;
142  vpQuaternionVector quaternion(eMc.getRotationMatrix());
143  std::cout << "** Rotation (quaternion representation) [rad]: " << quaternion.t() << std::endl;
144 
145  // save eMc
146  std::string name_we = vpIoTools::getNameWE(opt_eMc_file) + ".txt";
147 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
148  std::ofstream file_eMc(name_we);
149 #else
150  std::ofstream file_eMc(name_we.c_str());
151 #endif
152  eMc.save(file_eMc);
153 
154  vpPoseVector pose_vec(eMc);
155  std::string output_filename(opt_eMc_file);
156  std::cout << std::endl << "Save transformation matrix eMc as a vpPoseVector in " << opt_eMc_file << std::endl;
157  pose_vec.saveYAML(opt_eMc_file, pose_vec);
158  } else {
159  std::cout << std::endl << "** Hand-eye calibration failed" << std::endl;
160  std::cout << std::endl
161  << "Check your input data and ensure they are covering the half sphere over the chessboard." << std::endl;
162  std::cout << std::endl
163  << "See https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-calibration-extrinsic.html" << std::endl;
164  }
165 
166  return EXIT_SUCCESS;
167 }
static bool loadYAML(const std::string &filename, vpArray2D< Type > &A, char *header=NULL)
Definition: vpArray2D.h:652
static int calibrate(const std::vector< vpHomogeneousMatrix > &cMo, const std::vector< vpHomogeneousMatrix > &rMe, vpHomogeneousMatrix &eMc)
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpRotationMatrix getRotationMatrix() const
void save(std::ofstream &f) const
static long getIndex(const std::string &filename, const std::string &format)
Definition: vpIoTools.cpp:1577
static std::vector< std::string > getDirFiles(const std::string &dirname)
Definition: vpIoTools.cpp:1946
static std::string getNameWE(const std::string &pathname)
Definition: vpIoTools.cpp:1536
static double deg(double rad)
Definition: vpMath.h:110
Implementation of a pose vector and operations on poses.
Definition: vpPoseVector.h:152
vpRowVector t() const
Implementation of a rotation vector as quaternion angle minimal representation.
Implementation of a rotation vector as axis-angle minimal representation.