File: tutorial-hand-eye-calibration.cpp

package info (click to toggle)
visp 3.6.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 119,296 kB
  • sloc: cpp: 500,914; ansic: 52,904; xml: 22,642; python: 7,365; java: 4,247; sh: 482; makefile: 237; objc: 145
file content (174 lines) | stat: -rw-r--r-- 7,401 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! \example tutorial-hand-eye-calibration.cpp
#include <map>

#include <visp3/core/vpIoTools.h>
#include <visp3/vision/vpHandEyeCalibration.h>

void usage(const char *argv [], int error)
{
  std::cout << "Synopsis" << std::endl
    << "  " << argv[0] << " [--data-path <path>] [--help] [-h]" << std::endl
    << std::endl;
  std::cout << "Description" << std::endl
    << "  --data-path <path>  Path to the folder containing" << std::endl
    << "    pose_fPe_%d.yaml and pose_cPo_%d.yaml data files." << std::endl
    << "    Default: \"./\"" << std::endl
    << std::endl
    << "  --fPe <generic name>  Generic name of the yaml files" << std::endl
    << "    containing the pose of the end-effector expressed in the robot base" << std::endl
    << "    frame and located in the data path folder." << std::endl
    << "    Default: pose_fPe_%d.yaml" << std::endl
    << std::endl
    << "  --cPo <generic name>  Generic name of the yaml files" << std::endl
    << "    containing the pose of the calibration grid expressed in the camera" << std::endl
    << "    frame and located in the data path folder." << std::endl
    << "    Default: pose_cPo_%d.yaml" << std::endl
    << std::endl
    << "  --output <filename>  File in yaml format containing the pose of the camera" << std::endl
    << "    in the end-effector frame. Data are saved as a pose vector" << std::endl
    << "    with first the 3 translations along X,Y,Z in [m]" << std::endl
    << "    and then the 3 rotations in axis-angle representation (thetaU) in [rad]." << std::endl
    << "    Default: eMc.yaml" << std::endl
    << std::endl
    << "  --help, -h  Print this helper message." << std::endl
    << std::endl;
  if (error) {
    std::cout << "Error" << std::endl
      << "  "
      << "Unsupported parameter " << argv[error] << std::endl;
  }
}

int main(int argc, const char *argv [])
{
  std::string opt_data_path = "./";
  std::string opt_fPe_files = "pose_fPe_%d.yaml";
  std::string opt_cPo_files = "pose_cPo_%d.yaml";
  std::string opt_eMc_file = "eMc.yaml";

  for (int i = 1; i < argc; i++) {
    if (std::string(argv[i]) == "--data-path" && i + 1 < argc) {
      opt_data_path = std::string(argv[i + 1]);
      i++;
    }
    else if (std::string(argv[i]) == "--fPe" && i + 1 < argc) {
      opt_fPe_files = std::string(argv[i + 1]);
      i++;
    }
    else if (std::string(argv[i]) == "--cPo" && i + 1 < argc) {
      opt_cPo_files = std::string(argv[i + 1]);
      i++;
    }
    else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
      opt_eMc_file = std::string(argv[i + 1]);
      i++;
    }
    else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
      usage(argv, 0);
      return EXIT_SUCCESS;
    }
    else {
      usage(argv, i);
      return EXIT_FAILURE;
    }
  }

  std::vector<vpHomogeneousMatrix> cMo;
  std::vector<vpHomogeneousMatrix> wMe;
  vpHomogeneousMatrix eMc;

  std::map<long, std::string> map_fPe_files;
  std::map<long, std::string> map_cPo_files;
  std::vector<std::string> files = vpIoTools::getDirFiles(opt_data_path);
  for (unsigned int i = 0; i < files.size(); i++) {
    long index_fPe = vpIoTools::getIndex(files[i], opt_fPe_files);
    long index_cPo = vpIoTools::getIndex(files[i], opt_cPo_files);
    if (index_fPe != -1) {
      map_fPe_files[index_fPe] = files[i];
    }
    if (index_cPo != -1) {
      map_cPo_files[index_cPo] = files[i];
    }
  }

  if (map_fPe_files.size() == 0) {
    std::cout << "No " << opt_fPe_files
      << " files found. Use --data-path <path> or --fPe <generic name> be able to read your data." << std::endl;
    return EXIT_FAILURE;
  }
  if (map_cPo_files.size() == 0) {
    std::cout << "No " << opt_cPo_files
      << " files found. Use --data-path <path> or --cPo <generic name> be able to read your data." << std::endl;
    return EXIT_FAILURE;
  }

  for (std::map<long, std::string>::const_iterator it_fPe = map_fPe_files.begin(); it_fPe != map_fPe_files.end();
    ++it_fPe) {
    std::string file_fPe = vpIoTools::createFilePath(opt_data_path, it_fPe->second);
    std::map<long, std::string>::const_iterator it_cPo = map_cPo_files.find(it_fPe->first);
    if (it_cPo != map_cPo_files.end()) {
      vpPoseVector wPe;
      if (wPe.loadYAML(file_fPe, wPe) == false) {
        std::cout << "Unable to read data from " << file_fPe << ". Skip data" << std::endl;
        continue;
      }

      vpPoseVector cPo;
      std::string file_cPo = vpIoTools::createFilePath(opt_data_path, it_cPo->second);
      if (cPo.loadYAML(file_cPo, cPo) == false) {
        std::cout << "Unable to read data from " << file_cPo << ". Skip data" << std::endl;
        continue;
      }
      std::cout << "Use data from " << opt_data_path << "/" << file_fPe << " and from " << file_cPo << std::endl;
      wMe.push_back(vpHomogeneousMatrix(wPe));
      cMo.push_back(vpHomogeneousMatrix(cPo));
    }
  }

  if (wMe.size() < 3) {
    std::cout << "Not enough data pairs found." << std::endl;
    return EXIT_FAILURE;
  }

  int ret = vpHandEyeCalibration::calibrate(cMo, wMe, eMc);

  if (ret == 0) {
    std::cout << std::endl << "** Hand-eye calibration succeed" << std::endl;
    std::cout << std::endl << "** Hand-eye (eMc) transformation estimated:" << std::endl;
    std::cout << eMc << std::endl;
    std::cout << "** Corresponding pose vector [tx ty tz tux tuy tuz] in [m] and [rad]: " << vpPoseVector(eMc).t() << std::endl;

    vpThetaUVector erc(eMc.getRotationMatrix());
    std::cout << std::endl << "** Translation [m]: " << eMc[0][3] << " " << eMc[1][3] << " " << eMc[2][3] << std::endl;
    std::cout << "** Rotation (theta-u representation) [rad]: " << erc.t() << std::endl;
    std::cout << "** Rotation (theta-u representation) [deg]: " << vpMath::deg(erc[0]) << " " << vpMath::deg(erc[1])
      << " " << vpMath::deg(erc[2]) << std::endl;
    vpQuaternionVector quaternion(eMc.getRotationMatrix());
    std::cout << "** Rotation (quaternion representation) [rad]: " << quaternion.t() << std::endl;
    vpRxyzVector rxyz(eMc.getRotationMatrix());
    std::cout << "** Rotation (r-x-y-z representation) [rad]: " << rxyz.t() << std::endl;
    std::cout << "** Rotation (r-x-y-z representation) [deg]: " << vpMath::deg(rxyz).t() << std::endl;

    // save eMc
    std::string name_we = vpIoTools::createFilePath(vpIoTools::getParent(opt_eMc_file), vpIoTools::getNameWE(opt_eMc_file)) + ".txt";
    std::cout << std::endl << "Save transformation matrix eMc as an homogeneous matrix in: " << name_we << std::endl;
#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
    std::ofstream file_eMc(name_we);
#else
    std::ofstream file_eMc(name_we.c_str());
#endif
    eMc.save(file_eMc);

    vpPoseVector pose_vec(eMc);
    std::string output_filename = vpIoTools::createFilePath(vpIoTools::getParent(opt_eMc_file), vpIoTools::getName(opt_eMc_file));
    std::cout << "Save transformation matrix eMc as a vpPoseVector in       : " << output_filename << std::endl;
    pose_vec.saveYAML(output_filename, pose_vec);
  }
  else {
    std::cout << std::endl << "** Hand-eye calibration failed" << std::endl;
    std::cout << std::endl << "Check your input data and ensure they are covering the half sphere over the chessboard." << std::endl;
    std::cout << std::endl << "See https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-calibration-extrinsic.html" << std::endl;
  }

  return EXIT_SUCCESS;
}