RobotsIO
Loading...
Searching...
No Matches
iCubCamera.cpp
1/*
2 * Copyright (C) 2019 Istituto Italiano di Tecnologia (IIT)
3 *
4 * This software may be modified and distributed under the terms of the
5 * BSD 3-Clause license. See the accompanying LICENSE file for details.
6 */
7
8#include <RobotsIO/Camera/iCubCamera.h>
9
10#include <iostream>
11
12#include <unsupported/Eigen/MatrixFunctions>
13
14#include <yarp/cv/Cv.h>
15#include <yarp/eigen/Eigen.h>
16#include <yarp/os/LogStream.h>
17#include <yarp/os/Property.h>
18#include <yarp/os/ResourceFinder.h>
19#include <yarp/sig/Image.h>
20
21using namespace Eigen;
22using namespace RobotsIO::Camera;
23using namespace iCub::iKin;
24using namespace yarp::cv;
25using namespace yarp::eigen;
26using namespace yarp::os;
27using namespace yarp::sig;
28
29
30iCubCamera::iCubCamera
31(
32 const std::string& robot_name,
33 const std::string& laterality,
34 const std::string& port_prefix,
35 const bool& use_calibration,
36 const std::string& calibration_path
37) :
38 laterality_(laterality),
39 use_calibration_(use_calibration)
40{
41 /* Check YARP network. */
42 if (!yarp_.checkNetwork())
43 {
44 throw(std::runtime_error(log_name_ + "::ctor. Error: YARP network is not available."));
45 }
46
47 /* Check laterality. */
48 if ((laterality_ != "left") && (laterality_ != "right"))
49 {
50 std::string err = log_name_ + "::ctor. Please use a valid laterality when constructing the iCubCamera instance.";
51 throw(std::runtime_error(err));
52 }
53
54 /* Prepare properties for the GazeController. */
55 Property properties;
56 properties.put("device", "gazecontrollerclient");
57 properties.put("remote", "/iKinGazeCtrl");
58 properties.put("local", "/" + port_prefix + "/gazecontroller");
59
60 /* Open driver. */
61 bool ok = driver_gaze_.open(properties) && driver_gaze_.view(gaze_control_) && (gaze_control_ != nullptr);
62
63 if (ok)
64 {
65 /* Retrieve camera parameters. */
66 Bottle info;
67 std::string key;
68 gaze_control_->getInfo(info);
69
70 key = "camera_width_" + laterality_;
71 if (info.find(key).isNull())
72 {
73 std::string err = log_name_ + "::ctor. Error: cannot load iCub " + laterality_ + " camera width.";
74 throw(std::runtime_error(err));
75 }
76 parameters_.width(info.find(key).asInt32());
77
78 key = "camera_height_" + laterality_;
79 if (info.find(key).isNull())
80 {
81 std::string err = log_name_ + "::ctor. Error: cannot load iCub " + laterality_ + " camera height.";
82 throw(std::runtime_error(err));
83 }
84 parameters_.height(info.find(key).asInt32());
85
86 key = "camera_intrinsics_" + laterality_;
87 if (info.find(key).isNull())
88 {
89 std::string err = log_name_ + "::ctor. Error: cannot load iCub " + laterality_ + " camera intrinsic parameters.";
90 throw(std::runtime_error(err));
91 }
92 Bottle *list = info.find(key).asList();
93 parameters_.fx(list->get(0).asFloat64());
94 parameters_.cx(list->get(2).asFloat64());
95 parameters_.fy(list->get(5).asFloat64());
96 parameters_.cy(list->get(6).asFloat64());
97
98 parameters_.initialized(true);
99 }
100 else
101 {
102 /* Stick to encoders .*/
103 use_driver_gaze_ = false;
104
105 /* TODO: take parameters from a configuration file. */
106 parameters_.width(640);
107 parameters_.height(480);
108 if (laterality_ == "left")
109 {
110 parameters_.fx(468.672);
111 parameters_.cx(323.045);
112 parameters_.fy(467.73);
113 parameters_.cy(245.784);
114 }
115 else
116 {
117 parameters_.fx(468.488);
118 parameters_.cx(301.274);
119 parameters_.fy(467.427);
120 parameters_.cy(245.503);
121 }
122 parameters_.initialized(true);
123
124 /* Configure torso. */
125 Property properties;
126 properties.put("device", "remote_controlboard");
127 properties.put("local", "/" + port_prefix + "/torso:i");
128 properties.put("remote", "/" + robot_name + "/torso");
129 ok = drv_torso_.open(properties) && drv_torso_.view(itorso_) && (itorso_ != nullptr);
130 if (!ok)
131 {
132 std::string err = log_name_ + "::ctor. Error: cannot open remote control board for torso.";
133 throw(std::runtime_error(err));
134 }
135
136 /* Configure forward kinematics. */
137 left_eye_kinematics_ = iCubEye("left_v2");
138 right_eye_kinematics_ = iCubEye("right_v2");
139
140 left_eye_kinematics_.setAllConstraints(false);
141 right_eye_kinematics_.setAllConstraints(false);
142
143 left_eye_kinematics_.releaseLink(0);
144 left_eye_kinematics_.releaseLink(1);
145 left_eye_kinematics_.releaseLink(2);
146 right_eye_kinematics_.releaseLink(0);
147 right_eye_kinematics_.releaseLink(1);
148 right_eye_kinematics_.releaseLink(2);
149 }
150
151 /* Configure head.
152 We require this anyway in order to provide inputs to the calibration model. */
153 properties.put("device", "remote_controlboard");
154 properties.put("local", "/" + port_prefix + "/head:i");
155 properties.put("remote", "/" + robot_name + "/head");
156 ok = drv_head_.open(properties) && drv_head_.view(ihead_) && (ihead_ != nullptr);
157 if (!ok)
158 {
159 std::string err = log_name_ + "::ctor. Error: cannot open remote control board for head.";
160 throw(std::runtime_error(err));
161 }
162
163 /* Load calibration if requested. */
164 if (use_calibration_)
165 if (!load_calibration_model(calibration_path))
166 {
167 std::string err = log_name_ + "::ctor. Error: cannot load calibration model.";
168 throw(std::runtime_error(err));
169 }
170
171 /* Open rgb input port. */
172 if (!(port_rgb_.open("/" + port_prefix + "/rgb:i")))
173 {
174 std::string err = log_name_ + "::ctor. Error: cannot open rgb input port.";
175 throw(std::runtime_error(err));
176 }
177
178 /* Open depth input port. */
179 if (!(port_depth_.open("/" + port_prefix + "/depth:i")))
180 {
181 std::string err = log_name_ + "::ctor. Error: cannot open depth input port.";
182 throw(std::runtime_error(err));
183 }
184
185 Camera::initialize();
186
187 /* Log parameters. */
188 std::cout << log_name_ + "::ctor. Camera parameters:" << std::endl;
189 std::cout << log_name_ + " - width: " << parameters_.width() << std::endl;
190 std::cout << log_name_ + " - height: " << parameters_.height() << std::endl;
191 std::cout << log_name_ + " - fx: " << parameters_.fx() << std::endl;
192 std::cout << log_name_ + " - fy: " << parameters_.fy() << std::endl;
193 std::cout << log_name_ + " - cx: " << parameters_.cx() << std::endl;
194 std::cout << log_name_ + " - cy: " << parameters_.cy() << std::endl;
195}
196
197
198iCubCamera::iCubCamera
199(
200 const std::string& data_path,
201 const std::string& laterality,
202 const std::size_t& width,
203 const size_t& height,
204 const double& fx,
205 const double& cx,
206 const double& fy,
207 const double& cy,
208 const bool& load_encoders_data,
209 const bool& use_calibration,
210 const std::string& calibration_path
211) :
212 Camera(data_path, width, height, fx, cx, fy, cy),
213 laterality_(laterality),
214 load_encoders_data_(load_encoders_data),
215 use_calibration_(use_calibration)
216{
217 /* Load calibration if requested. */
218 if (use_calibration_)
219 if (!load_calibration_model(calibration_path))
220 {
221 std::string err = log_name_ + "::ctor. Error: cannot load calibration model.";
222 throw(std::runtime_error(err));
223 }
224
225 Camera::initialize();
226}
227
228
229iCubCamera::~iCubCamera()
230{
231 /* Close driver. */
232 if (use_driver_gaze_)
233 driver_gaze_.close();
234 else
235 {
236 drv_torso_.close();
237 drv_head_.close();
238 }
239
240 /* Close ports. */
241 port_rgb_.close();
242
243 port_depth_.close();
244}
245
246
248{
249 return use_driver_gaze_;
250}
251
252
253yarp::dev::IGazeControl& iCubCamera::controller()
254{
255 return *gaze_control_;
256}
257
258
259std::pair<bool, MatrixXf> iCubCamera::depth(const bool& blocking)
260{
261 if (is_offline())
262 return Camera::depth_offline();
263
264 ImageOf<PixelFloat>* image_in;
265 image_in = port_depth_.read(blocking);
266
267 if (image_in == nullptr)
268 return std::make_pair(false, MatrixXf());
269
270 Stamp stamp;
271 port_depth_.getEnvelope(stamp);
272 time_stamp_depth_ = stamp.getTime();
273 is_time_stamp_depth_ = true;
274
275 cv::Mat image = yarp::cv::toCvMat(*image_in);
276 Map<Eigen::Matrix<float, Dynamic, Dynamic, Eigen::RowMajor>> depth(image.ptr<float>(), image.rows, image.cols);
277
278 return std::make_pair(true, depth);
279}
280
281
282std::pair<bool, Transform<double, 3, Affine>> iCubCamera::pose(const bool& blocking)
283{
284 bool valid_pose = false;
285 Transform<double, 3, Affine> pose;
286
287 if (is_offline())
288 std::tie(valid_pose, pose) = Camera::pose_offline();
289 else
290 std::tie(valid_pose, pose) = laterality_pose(laterality_, blocking);
291
292 if (!valid_pose)
293 return std::make_pair(false, Transform<double, 3, Affine>());
294
295 /* If calibration was loaded and eye encoders are available, correct pose of right eye. */
296 if ((laterality() == "right") && use_calibration_)
297 {
298 bool valid_encoders_input = false;
299 if ((ihead_ != nullptr) || (load_encoders_data_))
300 {
301 /* Set input. */
302 Eigen::VectorXd head_encoders(6);
303 std::tie(valid_encoders_input, head_encoders) = auxiliary_data(true);
304
305 /* Corrrect pose of the righe eye. */
306 if (valid_encoders_input)
307 {
308 yarp::sig::Vector input(3);
309 toEigen(input) = head_encoders.tail<3>() * M_PI / 180.0;
310
311 /* Get prediction. */
312 yarp::sig::Vector prediction = calibration_.predict(input).getPrediction();
313
314 /* Convert to SE3. */
315 Eigen::Transform<double, 3, Eigen::Affine> output = exp_map(yarp::eigen::toEigen(prediction));
316
317 pose = pose * output;
318 }
319 }
320
321 if (!valid_encoders_input)
322 std::cout << log_name_ + "::pose. Warning: calibration requested, however eyes encoders cannot be retrieved." << std::endl;
323 }
324
325 return std::make_pair(true, pose);
326}
327
328
329std::pair<bool, cv::Mat> iCubCamera::rgb(const bool& blocking)
330{
331 if (is_offline())
332 return Camera::rgb_offline();
333
334 ImageOf<PixelRgb>* image_in;
335 image_in = port_rgb_.read(blocking);
336
337 if (image_in == nullptr)
338 return std::make_pair(false, cv::Mat());
339
340 Stamp stamp;
341 port_rgb_.getEnvelope(stamp);
342 time_stamp_rgb_ = stamp.getTime();
343 is_time_stamp_rgb_ = true;
344
345 cv::Mat image = yarp::cv::toCvMat(*image_in);
346
347 return std::make_pair(true, image);
348}
349
350
351std::pair<bool, double> iCubCamera::time_stamp_rgb() const
352{
353 if (is_offline())
354 return Camera::time_stamp_rgb_offline();
355
356 return std::make_pair(is_time_stamp_rgb_, time_stamp_rgb_);
357}
358
359
360std::pair<bool, double> iCubCamera::time_stamp_depth() const
361{
362 if (is_offline())
363 return Camera::time_stamp_depth_offline();
364
365 return std::make_pair(is_time_stamp_depth_, time_stamp_depth_);
366}
367
368
369std::pair<bool, Eigen::VectorXd> iCubCamera::auxiliary_data(const bool& blocking)
370{
371 if (is_offline())
373
374 /* Gaze driver do not provides additional information from encoders. */
375 if (use_driver_gaze_)
376 return std::make_pair(false, VectorXd());
377
378 yarp::sig::Vector torso_encoders_(3);
379 yarp::sig::Vector head_encoders_(6);
380
381 if (!itorso_->getEncoders(torso_encoders_.data()))
382 return std::make_pair(false, VectorXd());
383
384 if (!ihead_->getEncoders(head_encoders_.data()))
385 return std::make_pair(false, VectorXd());
386
387 VectorXd encoders(9);
388 encoders.head<3>() = toEigen(torso_encoders_);
389 encoders.tail<6>() = toEigen(head_encoders_);
390
391 return std::make_pair(true, encoders);
392}
393
394
395std::size_t iCubCamera::auxiliary_data_size() const
396{
397 /* Auxiliary data are torso (3) and head (6) encoders. */
398 if (load_encoders_data_)
399 return 3 + 6;
400
401 return 0;
402}
403
404
405std::string iCubCamera::laterality()
406{
407 return laterality_;
408}
409
410
411std::pair<bool, Eigen::Transform<double, 3, Eigen::Affine>> iCubCamera::laterality_pose(const std::string& laterality, const bool& blocking)
412{
413 Transform<double, 3, Affine> pose;
414
415 yarp::sig::Vector position_yarp;
416 yarp::sig::Vector orientation_yarp;
417
418 bool ok = getLateralityEyePose(laterality, position_yarp, orientation_yarp);
419
420 if (!ok)
421 return std::make_pair(false, Transform<double, 3, Affine>());
422
423 pose = Translation<double, 3>(toEigen(position_yarp));
424 pose.rotate(AngleAxisd(orientation_yarp(3), toEigen(orientation_yarp).head<3>()));
425
426 return std::make_pair(true, pose);
427}
428
429
430void iCubCamera::set_laterality(const std::string& laterality)
431{
432 laterality_ = laterality;
433}
434
435
436bool iCubCamera::getLateralityEyePose(const std::string& laterality, yarp::sig::Vector& position, yarp::sig::Vector& orientation)
437{
438 if ((laterality != "left") && (laterality != "right"))
439 return false;
440
441 if (use_driver_gaze_)
442 {
443 if (laterality == "left")
444 return gaze_control_->getLeftEyePose(position, orientation);
445 else
446 return gaze_control_->getRightEyePose(position, orientation);
447 }
448 else
449 {
450 yarp::sig::Vector torso_encoders_(3);
451 yarp::sig::Vector head_encoders_(6);
452
453 if (!itorso_->getEncoders(torso_encoders_.data()))
454 return false;
455
456 if (!ihead_->getEncoders(head_encoders_.data()))
457 return false;
458
459 yarp::sig::Vector chain_joints(8);
460 chain_joints(0) = torso_encoders_(2);
461 chain_joints(1) = torso_encoders_(1);
462 chain_joints(2) = torso_encoders_(0);
463 chain_joints(3) = head_encoders_(0);
464 chain_joints(4) = head_encoders_(1);
465 chain_joints(5) = head_encoders_(2);
466 chain_joints(6) = head_encoders_(3);
467
468 double version = head_encoders_(4);
469 double vergence = head_encoders_(5);
470
471 if (laterality == "left")
472 chain_joints(7) = version + vergence / 2.0;
473 else
474 chain_joints(7) = version - vergence / 2.0;
475
476 yarp::sig::Vector pose;
477 if (laterality == "left")
478 pose = left_eye_kinematics_.EndEffPose(chain_joints * M_PI / 180.0);
479 else
480 pose = right_eye_kinematics_.EndEffPose(chain_joints * M_PI / 180.0);
481
482 position.resize(3);
483 orientation.resize(4);
484 position = pose.subVector(0, 2);
485 orientation = pose.subVector(3, 6);
486
487 return true;
488 }
489}
490
491
492Eigen::Transform<double, 3, Eigen::Affine> iCubCamera::exp_map(const Eigen::VectorXd& se3)
493{
494 Eigen::Transform<double, 3, Eigen::Affine> SE3;
495
496 Eigen::Matrix3d log_R = Eigen::Matrix3d::Zero();
497 log_R(0, 1) = -1.0 * se3(5);
498 log_R(0, 2) = se3(4);
499 log_R(1, 0) = se3(5);
500 log_R(1, 2) = -1.0 * se3(3);
501 log_R(2, 0) = -1.0 * se3(4);
502 log_R(2, 1) = se3(3);
503
504 double theta = se3.tail<3>().norm() + std::numeric_limits<double>::epsilon();
505 Eigen::Matrix3d V = Eigen::Matrix3d::Identity() + (1 - std::cos(theta)) / (theta * theta) * log_R + (theta - std::sin(theta)) / (std::pow(theta, 3)) * log_R * log_R;
506
507 SE3 = Eigen::Translation<double, 3>(V * se3.head<3>());
508 SE3.rotate(log_R.exp());
509
510 return SE3;
511}
512
513
514bool iCubCamera::load_calibration_model(const std::string& model_path)
515{
516 std::ifstream model_in;
517 model_in.open(model_path);
518 if (!model_in.is_open())
519 return false;
520
521 Bottle model;
522 std::stringstream ss;
523 ss << model_in.rdbuf();
524 model.fromString(ss.str());
525 model_in.close();
526
527 calibration_.readBottle(model);
528
529 return true;
530}
virtual std::pair< bool, Eigen::MatrixXf > depth_offline()
Definition: Camera.cpp:392
virtual std::pair< bool, Eigen::VectorXd > auxiliary_data_offline()
Definition: Camera.cpp:519
std::pair< bool, Eigen::VectorXd > auxiliary_data(const bool &blocking) override
Definition: iCubCamera.cpp:369
std::pair< bool, Eigen::MatrixXf > depth(const bool &blocking) override
Definition: iCubCamera.cpp:259