Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
tutorial-ibvs-4pts-json.cpp
1 
2 #include <iostream>
3 #include <visp3/core/vpConfig.h>
4 
5 #ifdef VISP_HAVE_NLOHMANN_JSON
6 #include <visp3/robot/vpSimulatorCamera.h>
7 #include <visp3/visual_features/vpFeatureBuilder.h>
8 #include <visp3/vs/vpServo.h>
9 
10 
11 #include <nlohmann/json.hpp>
12 using json = nlohmann::json;
13 
14 #if defined(ENABLE_VISP_NAMESPACE)
15 using namespace VISP_NAMESPACE_NAME;
16 #endif
17 
18 #ifndef DOXYGEN_SHOULD_SKIP_THIS
20 enum vpInteractionMatrixTypeSubset
21 {
22  UNKNOWN = -1,
23  CURRENT,
24  DESIRED,
25  MEAN
26 };
29 NLOHMANN_JSON_SERIALIZE_ENUM(vpInteractionMatrixTypeSubset, {
30  {UNKNOWN, nullptr}, // Default value if the json string is not in "current", "desired" or "mean"
31  {CURRENT, "current"},
32  {DESIRED, "desired"},
33  {MEAN, "mean"} }
34  );
36 
38 class Arguments
39 {
40 public:
41  // Default values
42  Arguments() :
43  lambda(0.5), cdMo(0, 0, 0.75, 0, 0, 0),
44  cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)),
45  samplingTime(0.04), errorThreshold(0.0001), interactionMatrixType(CURRENT)
46  { }
47 
48  vpServo::vpServoIteractionMatrixType getInteractionMatrixType() const
49  {
50  switch (interactionMatrixType) {
51  case CURRENT:
52  return vpServo::CURRENT;
53  case DESIRED:
54  return vpServo::DESIRED;
55  case MEAN:
56  return vpServo::MEAN;
57  default:
58  throw vpException(vpException::badValue, "Unexpected value");
59  }
60  return vpServo::CURRENT;
61  }
62 
63  double lambda; // Control law gain
64  vpHomogeneousMatrix cdMo; // Target (desired) camera pose
65  vpHomogeneousMatrix cMo; // Initial camera pose
66  double samplingTime; // Robot sampling time
67  double errorThreshold; // Error threshold. Once error is below, consider servoing as successful
68  vpInteractionMatrixTypeSubset interactionMatrixType;
69 };
71 
73 // Read script arguments from JSON. All values are optional and if an argument is not present,
74 // the default value defined in the constructor is kept
75 void from_json(const json &j, Arguments &a)
76 {
77 #ifdef ENABLE_VISP_NAMESPACE
78  using VISP_NAMESPACE_ADDRESSING from_json;
79 #endif
80  a.lambda = j.value("lambda", a.lambda);
81  if (a.lambda <= 0) {
82  throw vpException(vpException::badValue, "Lambda should be > 0");
83  }
84 
85  a.cMo = j.value("cMo", a.cMo);
86  a.cdMo = j.value("cdMo", a.cdMo);
87 
88  a.samplingTime = j.value("samplingTime", a.samplingTime);
89  if (a.samplingTime <= 0) {
90  throw vpException(vpException::badValue, "Sampling time should be > 0");
91  }
92 
93  a.errorThreshold = j.value("errorThreshold", a.errorThreshold);
94  if (a.errorThreshold <= 0) {
95  throw vpException(vpException::badValue, "Error threshold should be > 0");
96  }
97 
98  a.interactionMatrixType = j.value("interactionMatrix", a.interactionMatrixType);
99  if (a.interactionMatrixType == UNKNOWN) {
100  throw vpException(vpException::badValue, "Unknown interaction matrix type defined in JSON");
101  }
102 }
103 
104 void to_json(json &j, const Arguments &a)
105 {
106 #ifdef ENABLE_VISP_NAMESPACE
107  using VISP_NAMESPACE_ADDRESSING to_json;
108 #endif
109  j = json {
110  {"lambda", a.lambda},
111  {"cMo", a.cMo},
112  {"cdMo", a.cdMo},
113  {"errorThreshold", a.errorThreshold},
114  {"samplingTime", a.samplingTime} ,
115  {"interactionMatrix", a.interactionMatrixType}
116  };
117 }
119 
121 Arguments readArguments(const std::string &path)
122 {
123  Arguments a;
124 
125  if (!path.empty()) {
126  std::ifstream file(path);
127  if (!file.good()) {
128  std::stringstream ss;
129  ss << "Problem opening file " << path << ". Make sure it exists and is readable" << std::endl;
130  throw vpException(vpException::badValue, ss.str());
131  }
132  json j;
133  try {
134  j = json::parse(file);
135  }
136  catch (json::parse_error &e) {
137  std::stringstream msg;
138  msg << "Could not parse JSON file : \n";
139 
140  msg << e.what() << std::endl;
141  msg << "Byte position of error: " << e.byte;
142  throw vpException(vpException::ioError, msg.str());
143  }
144  a = j; // Call from_json(const json& j, Argument& a) to read json into arguments a
145  file.close();
146  }
147  else {
148  std::cout << "Using default arguments. Try using a JSON file to set the arguments of the visual servoing." << std::endl;
149  }
150  return a;
151 }
153 
155 #ifdef ENABLE_VISP_NAMESPACE
156 // Required to have the to_json method in the same namespace than vpFeaturePoint
157 namespace VISP_NAMESPACE_NAME
158 {
159 #endif
160 void to_json(json &j, const vpFeaturePoint &p)
161 {
162  j = json {
163  {"x", p.get_x()},
164  {"y", p.get_y()},
165  {"z", p.get_Z()}
166  };
167 }
168 END_VISP_NAMESPACE
169 
171 
173 class ServoingExperimentData
174 {
175 public:
176  ServoingExperimentData(const Arguments &arguments, const std::vector<vpFeaturePoint> &desiredFeatures) :
177  m_arguments(arguments), m_desiredFeatures(desiredFeatures)
178  { }
179 
180  void onIter(const vpHomogeneousMatrix &cMo, const double errorNorm, const std::vector<vpFeaturePoint> &points,
181  const vpColVector &velocity, const vpMatrix &interactionMatrix)
182  {
183  vpPoseVector r(cMo);
184  m_trajectory.push_back(r);
185  m_errorNorms.push_back(errorNorm);
186  m_points3D.push_back(points);
187  m_velocities.push_back(velocity);
188  m_interactionMatrices.push_back(interactionMatrix);
189  }
190 
191 private:
192  Arguments m_arguments;
193  std::vector<vpFeaturePoint> m_desiredFeatures;
194  std::vector<vpPoseVector> m_trajectory;
195  std::vector<double> m_errorNorms;
196  std::vector<std::vector<vpFeaturePoint> > m_points3D;
197  std::vector<vpColVector> m_velocities;
198  std::vector<vpMatrix> m_interactionMatrices;
199  friend void to_json(json &j, const ServoingExperimentData &res);
200 };
201 
202 void to_json(json &j, const ServoingExperimentData &res)
203 {
204 #ifdef ENABLE_VISP_NAMESPACE
205  using VISP_NAMESPACE_ADDRESSING to_json;
206 #endif
207  j = json {
208  {"parameters", res.m_arguments},
209  {"trajectory", res.m_trajectory},
210  {"errorNorm", res.m_errorNorms},
211  {"features", res.m_points3D},
212  {"desiredFeatures", res.m_desiredFeatures},
213  {"velocities", res.m_velocities},
214  {"interactionMatrices", res.m_interactionMatrices}
215  };
216 }
218 
220 void saveResults(const ServoingExperimentData &results, const std::string &path)
221 {
222  std::ofstream file(path);
223  const json j = results;
224  file << j.dump(4);
225  file.close();
226 }
228 #endif // DOXYGEN_SHOULD_SKIP_THIS
229 
230 int main(int argc, char *argv[])
231 {
233  std::string arguments_path = "";
234  std::string output_path = "";
235  for (int i = 1; i < argc; ++i) {
236  if (std::string(argv[i]) == "--settings" && i + 1 < argc) {
237  arguments_path = std::string(argv[i + 1]);
238  }
239  else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
240  output_path = std::string(argv[i + 1]);
241  }
242  }
243 
244  if (output_path.empty()) {
245  std::cerr << "JSON output path must be specified" << std::endl;
246  return EXIT_FAILURE;
247  }
248  const Arguments args = readArguments(arguments_path);
250 
251  try {
252  vpHomogeneousMatrix cdMo = args.cdMo;
253  vpHomogeneousMatrix cMo = args.cMo;
254  std::cout << cdMo << std::endl;
255 
256  vpPoint point[4];
257  point[0].setWorldCoordinates(-0.1, -0.1, 0);
258  point[1].setWorldCoordinates(0.1, -0.1, 0);
259  point[2].setWorldCoordinates(0.1, 0.1, 0);
260  point[3].setWorldCoordinates(-0.1, 0.1, 0);
261 
262  vpServo task;
264  task.setInteractionMatrixType(args.getInteractionMatrixType());
265  task.setLambda(args.lambda);
266 
267  vpFeaturePoint p[4], pd[4];
268  std::vector<vpFeaturePoint> features;
269  features.resize(4);
270  for (unsigned int i = 0; i < 4; i++) {
271  point[i].track(cdMo);
272  vpFeatureBuilder::create(pd[i], point[i]);
273  point[i].track(cMo);
274  vpFeatureBuilder::create(p[i], point[i]);
275  task.addFeature(p[i], pd[i]);
276  features[i] = pd[i];
277  }
279  ServoingExperimentData results(args, features);
281 
282  vpHomogeneousMatrix wMc, wMo;
283  vpSimulatorCamera robot;
284  robot.setSamplingTime(args.samplingTime);
285  robot.getPosition(wMc);
286  wMo = wMc * cMo;
287 
288  unsigned int iter = 0;
289  bool end = false;
290  std::cout << "Starting visual-servoing loop until convergence..." << std::endl;
292  while (!end) {
293  robot.getPosition(wMc);
294  cMo = wMc.inverse() * wMo;
295  for (unsigned int i = 0; i < 4; i++) {
296  point[i].track(cMo);
297  vpFeatureBuilder::create(p[i], point[i]);
298  features[i] = p[i];
299  }
300  const vpColVector v = task.computeControlLaw();
302  const double errorNorm = task.getError().sumSquare();
303 
305  results.onIter(cMo, errorNorm, features, v, task.getInteractionMatrix());
307 
308  if (errorNorm < args.errorThreshold) {
309  end = true;
310  }
311  vpTime::wait(10);
312  iter++;
313  }
315  std::cout << "Convergence in " << iter << " iterations" << std::endl;
317  saveResults(results, output_path);
319  }
320  catch (const vpException &e) {
321  std::cout << "Caught an exception: " << e << std::endl;
322  }
323 }
324 #else
325 int main()
326 {
327  std::cerr << "Cannot run tutorial: ViSP is not built with JSON integration. Install the JSON library and recompile ViSP" << std::endl;
328 }
329 #endif
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
double sumSquare() const
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ ioError
I/O error.
Definition: vpException.h:67
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
static void create(vpFeaturePoint &s, const vpCameraParameters &cam, const vpDot &d)
Class that defines a 2D point visual feature which is composed by two parameters that are the cartes...
double get_y() const
double get_x() const
double get_Z() const
void track(const vpHomogeneousMatrix &cMo)
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
Provides simple mathematics computation tools that are not available in the C mathematics library (ma...
Definition: vpMath.h:111
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
Definition: vpPoint.h:79
void setWorldCoordinates(double oX, double oY, double oZ)
Definition: vpPoint.cpp:111
Implementation of a pose vector and operations on poses.
Definition: vpPoseVector.h:203
void setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel) VP_OVERRIDE
@ CAMERA_FRAME
Definition: vpRobot.h:84
vpMatrix getInteractionMatrix() const
Definition: vpServo.h:525
void setInteractionMatrixType(const vpServoIteractionMatrixType &interactionMatrixType, const vpServoInversionType &interactionMatrixInversion=PSEUDO_INVERSE)
Definition: vpServo.cpp:380
@ EYEINHAND_CAMERA
Definition: vpServo.h:161
void addFeature(vpBasicFeature &s_cur, vpBasicFeature &s_star, unsigned int select=vpBasicFeature::FEATURE_ALL)
Definition: vpServo.cpp:331
void setLambda(double c)
Definition: vpServo.h:986
void setServo(const vpServoType &servo_type)
Definition: vpServo.cpp:134
vpColVector getError() const
Definition: vpServo.h:510
vpColVector computeControlLaw()
Definition: vpServo.cpp:705
vpServoIteractionMatrixType
Definition: vpServo.h:196
@ DESIRED
Definition: vpServo.h:208
@ MEAN
Definition: vpServo.h:214
@ CURRENT
Definition: vpServo.h:202
Class that defines the simplest robot: a free flying camera.
VISP_EXPORT int wait(double t0, double t)