Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
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 VISP_NLOHMANN_JSON(json.hpp)
12using json = nlohmann::json;
13
14#if defined(ENABLE_VISP_NAMESPACE)
15using namespace VISP_NAMESPACE_NAME;
16#endif
17
18#ifndef DOXYGEN_SHOULD_SKIP_THIS
20enum vpInteractionMatrixTypeSubset
21{
22 UNKNOWN = -1,
23 CURRENT,
24 DESIRED,
25 MEAN
26};
28
29#if defined(__clang__)
30// Mute warning : declaration requires an exit-time destructor [-Wexit-time-destructors]
31// message : expanded from macro 'NLOHMANN_JSON_SERIALIZE_ENUM'
32# pragma clang diagnostic push
33# pragma clang diagnostic ignored "-Wexit-time-destructors"
34#endif
35
37NLOHMANN_JSON_SERIALIZE_ENUM(vpInteractionMatrixTypeSubset, {
38 {UNKNOWN, nullptr}, // Default value if the json string is not in "current", "desired" or "mean"
39 {CURRENT, "current"},
40 {DESIRED, "desired"},
41 {MEAN, "mean"} }
42 );
44
45#if defined(__clang__)
46# pragma clang diagnostic pop
47#endif
48
50class Arguments
51{
52public:
53 // Default values
54 Arguments() :
55 lambda(0.5), cdMo(0, 0, 0.75, 0, 0, 0),
56 cMo(0.15, -0.1, 1., vpMath::rad(10), vpMath::rad(-10), vpMath::rad(50)),
57 samplingTime(0.04), errorThreshold(0.0001), interactionMatrixType(CURRENT)
58 { }
59
60 vpServo::vpServoIteractionMatrixType getInteractionMatrixType() const
61 {
62 switch (interactionMatrixType) {
63 case CURRENT:
64 return vpServo::CURRENT;
65 case DESIRED:
66 return vpServo::DESIRED;
67 case MEAN:
68 return vpServo::MEAN;
69 default:
70 throw vpException(vpException::badValue, "Unexpected value");
71 }
72 return vpServo::CURRENT;
73 }
74
75 double lambda; // Control law gain
76 vpHomogeneousMatrix cdMo; // Target (desired) camera pose
77 vpHomogeneousMatrix cMo; // Initial camera pose
78 double samplingTime; // Robot sampling time
79 double errorThreshold; // Error threshold. Once error is below, consider servoing as successful
80 vpInteractionMatrixTypeSubset interactionMatrixType;
81};
83
85// Read script arguments from JSON. All values are optional and if an argument is not present,
86// the default value defined in the constructor is kept
87void from_json(const json &j, Arguments &a)
88{
89#ifdef ENABLE_VISP_NAMESPACE
90 using VISP_NAMESPACE_ADDRESSING from_json;
91#endif
92 a.lambda = j.value("lambda", a.lambda);
93 if (a.lambda <= 0) {
94 throw vpException(vpException::badValue, "Lambda should be > 0");
95 }
96
97 a.cMo = j.value("cMo", a.cMo);
98 a.cdMo = j.value("cdMo", a.cdMo);
99
100 a.samplingTime = j.value("samplingTime", a.samplingTime);
101 if (a.samplingTime <= 0) {
102 throw vpException(vpException::badValue, "Sampling time should be > 0");
103 }
104
105 a.errorThreshold = j.value("errorThreshold", a.errorThreshold);
106 if (a.errorThreshold <= 0) {
107 throw vpException(vpException::badValue, "Error threshold should be > 0");
108 }
109
110 a.interactionMatrixType = j.value("interactionMatrix", a.interactionMatrixType);
111 if (a.interactionMatrixType == UNKNOWN) {
112 throw vpException(vpException::badValue, "Unknown interaction matrix type defined in JSON");
113 }
114}
115
116void to_json(json &j, const Arguments &a)
117{
118#ifdef ENABLE_VISP_NAMESPACE
119 using VISP_NAMESPACE_ADDRESSING to_json;
120#endif
121 j = json {
122 {"lambda", a.lambda},
123 {"cMo", a.cMo},
124 {"cdMo", a.cdMo},
125 {"errorThreshold", a.errorThreshold},
126 {"samplingTime", a.samplingTime} ,
127 {"interactionMatrix", a.interactionMatrixType}
128 };
129}
131
133Arguments readArguments(const std::string &path)
134{
135 Arguments a;
136
137 if (!path.empty()) {
138 std::ifstream file(path);
139 if (!file.good()) {
140 std::stringstream ss;
141 ss << "Problem opening file " << path << ". Make sure it exists and is readable" << std::endl;
142 throw vpException(vpException::badValue, ss.str());
143 }
144 json j;
145 try {
146 j = json::parse(file);
147 }
148 catch (json::parse_error &e) {
149 std::stringstream msg;
150 msg << "Could not parse JSON file : \n";
151
152 msg << e.what() << std::endl;
153 msg << "Byte position of error: " << e.byte;
154 throw vpException(vpException::ioError, msg.str());
155 }
156 a = j; // Call from_json(const json& j, Argument& a) to read json into arguments a
157 file.close();
158 }
159 else {
160 std::cout << "Using default arguments. Try using a JSON file to set the arguments of the visual servoing." << std::endl;
161 }
162 return a;
163}
165
167#ifdef ENABLE_VISP_NAMESPACE
168// Required to have the to_json method in the same namespace than vpFeaturePoint
169namespace VISP_NAMESPACE_NAME
170{
171#endif
172void to_json(json &j, const vpFeaturePoint &p)
173{
174 j = json {
175 {"x", p.get_x()},
176 {"y", p.get_y()},
177 {"z", p.get_Z()}
178 };
179}
180END_VISP_NAMESPACE
181
183
185class ServoingExperimentData
186{
187public:
188 ServoingExperimentData(const Arguments &arguments, const std::vector<vpFeaturePoint> &desiredFeatures) :
189 m_arguments(arguments), m_desiredFeatures(desiredFeatures)
190 { }
191
192 void onIter(const vpHomogeneousMatrix &cMo, const double errorNorm, const std::vector<vpFeaturePoint> &points,
193 const vpColVector &velocity, const vpMatrix &interactionMatrix)
194 {
195 vpPoseVector r(cMo);
196 m_trajectory.push_back(r);
197 m_errorNorms.push_back(errorNorm);
198 m_points3D.push_back(points);
199 m_velocities.push_back(velocity);
200 m_interactionMatrices.push_back(interactionMatrix);
201 }
202
203private:
204 Arguments m_arguments;
205 std::vector<vpFeaturePoint> m_desiredFeatures;
206 std::vector<vpPoseVector> m_trajectory;
207 std::vector<double> m_errorNorms;
208 std::vector<std::vector<vpFeaturePoint> > m_points3D;
209 std::vector<vpColVector> m_velocities;
210 std::vector<vpMatrix> m_interactionMatrices;
211 friend void to_json(json &j, const ServoingExperimentData &res);
212};
213
214void to_json(json &j, const ServoingExperimentData &res)
215{
216#ifdef ENABLE_VISP_NAMESPACE
217 using VISP_NAMESPACE_ADDRESSING to_json;
218#endif
219 j = json {
220 {"parameters", res.m_arguments},
221 {"trajectory", res.m_trajectory},
222 {"errorNorm", res.m_errorNorms},
223 {"features", res.m_points3D},
224 {"desiredFeatures", res.m_desiredFeatures},
225 {"velocities", res.m_velocities},
226 {"interactionMatrices", res.m_interactionMatrices}
227 };
228}
230
232void saveResults(const ServoingExperimentData &results, const std::string &path)
233{
234 std::ofstream file(path);
235 const json j = results;
236 file << j.dump(4);
237 file.close();
238}
240#endif // DOXYGEN_SHOULD_SKIP_THIS
241
242int main(int argc, char *argv[])
243{
245 std::string arguments_path = "";
246 std::string output_path = "";
247 for (int i = 1; i < argc; ++i) {
248 if (std::string(argv[i]) == "--settings" && i + 1 < argc) {
249 arguments_path = std::string(argv[i + 1]);
250 }
251 else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
252 output_path = std::string(argv[i + 1]);
253 }
254 }
255
256 if (output_path.empty()) {
257 std::cerr << "JSON output path must be specified" << std::endl;
258 return EXIT_FAILURE;
259 }
260 const Arguments args = readArguments(arguments_path);
262
263 try {
264 vpHomogeneousMatrix cdMo = args.cdMo;
265 vpHomogeneousMatrix cMo = args.cMo;
266 std::cout << cdMo << std::endl;
267
268 vpPoint point[4];
269 point[0].setWorldCoordinates(-0.1, -0.1, 0);
270 point[1].setWorldCoordinates(0.1, -0.1, 0);
271 point[2].setWorldCoordinates(0.1, 0.1, 0);
272 point[3].setWorldCoordinates(-0.1, 0.1, 0);
273
274 vpServo task;
276 task.setInteractionMatrixType(args.getInteractionMatrixType());
277 task.setLambda(args.lambda);
278
279 vpFeaturePoint p[4], pd[4];
280 std::vector<vpFeaturePoint> features;
281 features.resize(4);
282 for (unsigned int i = 0; i < 4; i++) {
283 point[i].track(cdMo);
284 vpFeatureBuilder::create(pd[i], point[i]);
285 point[i].track(cMo);
286 vpFeatureBuilder::create(p[i], point[i]);
287 task.addFeature(p[i], pd[i]);
288 features[i] = pd[i];
289 }
291 ServoingExperimentData results(args, features);
293
294 vpHomogeneousMatrix wMc, wMo;
295 vpSimulatorCamera robot;
296 robot.setSamplingTime(args.samplingTime);
297 robot.getPosition(wMc);
298 wMo = wMc * cMo;
299
300 unsigned int iter = 0;
301 bool end = false;
302 std::cout << "Starting visual-servoing loop until convergence..." << std::endl;
304 while (!end) {
305 robot.getPosition(wMc);
306 cMo = wMc.inverse() * wMo;
307 for (unsigned int i = 0; i < 4; i++) {
308 point[i].track(cMo);
309 vpFeatureBuilder::create(p[i], point[i]);
310 features[i] = p[i];
311 }
312 const vpColVector v = task.computeControlLaw();
314 const double errorNorm = task.getError().sumSquare();
315
317 results.onIter(cMo, errorNorm, features, v, task.getInteractionMatrix());
319
320 if (errorNorm < args.errorThreshold) {
321 end = true;
322 }
323 vpTime::wait(10);
324 iter++;
325 }
327 std::cout << "Convergence in " << iter << " iterations" << std::endl;
329 saveResults(results, output_path);
331 }
332 catch (const vpException &e) {
333 std::cout << "Caught an exception: " << e << std::endl;
334 }
335}
336#else
337int main()
338{
339 std::cerr << "Cannot run tutorial: ViSP is not built with JSON integration. Install the JSON library and recompile ViSP" << std::endl;
340}
341#endif
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)
void track(const vpHomogeneousMatrix &cMo)
vpHomogeneousMatrix inverse() const
void setWorldCoordinates(double oX, double oY, double oZ)
Definition vpPoint.cpp:116
void setVelocity(const vpRobot::vpControlFrameType frame, const vpColVector &vel) VP_OVERRIDE
@ CAMERA_FRAME
Definition vpRobot.h:81
@ EYEINHAND_CAMERA
Definition vpServo.h:176
vpServoIteractionMatrixType
Definition vpServo.h:211
@ DESIRED
Definition vpServo.h:223
@ CURRENT
Definition vpServo.h:217
VISP_EXPORT int wait(double t0, double t)