Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
collision.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of collision functions
5  * \author Corentin Chauvin-Hameau
6  * \date 2019
7  */
8 
9 #include "camera_nodelet.hpp"
10 #include "mf_sensors_simulator/CameraOutput.h"
11 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
12 #include <eigen3/Eigen/Dense>
13 #include <vector>
14 
15 using namespace std;
16 
17 
18 namespace mfcpp {
19 
20 CameraNodelet::OverlapCallback::OverlapCallback(CameraNodelet *parent):
21  rp3d::OverlapCallback(),
22  parent_(parent)
23 {
24 
25 }
26 
27 
29  rp3d::RaycastCallback(),
30  parent_(parent)
31 {
32 
33 }
34 
35 
37  rp3d::CollisionBody *body)
38 {
39  for (unsigned int k = 0; k < parent_->algae_bodies_.size(); k++) {
40  if (parent_->algae_bodies_[k] == body) {
41  parent_->corr_algae_.emplace_back(k);
42 
43  rp3d::Transform transform = body->getTransform();
44  rp3d::CollisionBody* new_body = parent_->ray_world_.createCollisionBody(transform);
45  parent_->ray_bodies_.emplace_back(new_body);
46 
47  rp3d::Vector3 half_extents = parent_->algae_shapes_[k]->getExtent();
48  box_shape_ptr new_shape = box_shape_ptr(new rp3d::BoxShape(half_extents));
49 
50  parent_->ray_shapes_.emplace_back(std::move(new_shape));
51  new_body->addCollisionShape(parent_->ray_shapes_.back().get(), rp3d::Transform::identity());
52 
53  return;
54  }
55  }
56 }
57 
58 
60  const rp3d::RaycastInfo& info)
61 {
62  for (unsigned int k = 0; k < parent_->ray_bodies_.size(); k++) {
63  if (parent_->ray_bodies_[k] == info.body) {
64  alga_hit_ = true;
65  hit_pt_ = tf2::Vector3(info.worldPoint.x, info.worldPoint.y,
66  info.worldPoint.z);
67  alga_idx_ = k;
68  }
69  }
70 
71  // Stop the raycasting
72  return rp3d::decimal(0.0);
73 }
74 
75 
76 bool CameraNodelet::multi_fov_body(const vector<geometry_msgs::Pose> &poses,
77  rp3d::CollisionBody* body, std::unique_ptr<rp3d::BoxShape> &shape, ros::Time stamp)
78 {
79  // Get ROS transforms if needed
80  geometry_msgs::TransformStamped fixed_camera_tf;
81 
82  if (stamp != ros::Time(0)) {
83  try {
84  fixed_camera_tf = tf_buffer_.lookupTransform(fixed_frame_, camera_frame_, ros::Time(0));
85  }
86  catch (tf2::TransformException &ex) {
87  NODELET_WARN("[camera_nodelet] %s", ex.what());
88  return false;
89  }
90  } else {
91  fixed_camera_tf = fixed_camera_tf_;
92  }
93 
94  // Create the collision body
95  rp3d::Vector3 pos(fixed_camera_tf_.transform.translation.x,
96  fixed_camera_tf_.transform.translation.y,
97  fixed_camera_tf_.transform.translation.z);
98  rp3d::Quaternion orient(fixed_camera_tf_.transform.rotation.x,
99  fixed_camera_tf_.transform.rotation.y,
100  fixed_camera_tf_.transform.rotation.z,
101  fixed_camera_tf_.transform.rotation.w);
102 
103  rp3d::Transform transform(pos, orient);
104  body->setTransform(transform);
105 
106  // Adding FOV shapes for each pose
107  unsigned int nbr_poses = poses.size();
108  std::vector<box_shape_ptr> shapes(nbr_poses);
109 
110  float x = sensor_width_ * fov_distance_
111  / (2 * sqrt(pow(focal_length_, 2) + pow(sensor_width_, 2)/4));
112  float y = sensor_width_ * fov_distance_
113  / (2 * sqrt(pow(focal_length_, 2) + pow(sensor_height_, 2)/4));
114  rp3d::Vector3 half_extents(x, y, fov_distance_/2);
115 
116  for (unsigned int k = 0; k < nbr_poses; k++) {
117  shapes[k] = unique_ptr<rp3d::BoxShape>(new rp3d::BoxShape(half_extents));
118 
119  rp3d::Vector3 fov_pos(poses[k].position.x, poses[k].position.y, poses[k].position.z + fov_distance_/2);
120  rp3d::Quaternion fov_orient(poses[k].orientation.x, poses[k].orientation.y, poses[k].orientation.y, poses[k].orientation.z);
121  rp3d::Transform fov_transform(fov_pos, fov_orient);
122  body->addCollisionShape(shapes[k].get(), fov_transform);
123  }
124 
125  // Merge all these shapes into a single AABB
126  rp3d::AABB merged_shape = body->getAABB();
127 
128  for (unsigned int k = 0; k < nbr_poses; k++) {
129  body->removeCollisionShape(body->getProxyShapesList());
130  }
131 
132  pos = merged_shape.getCenter();
133  orient = rp3d::Quaternion(0, 0, 0, 1);
134  transform = rp3d::Transform(pos, orient);
135  body->setTransform(transform);
136 
137  half_extents = merged_shape.getExtent();
138  shape = unique_ptr<rp3d::BoxShape>(new rp3d::BoxShape(half_extents));
139  body->addCollisionShape(shape.get(), rp3d::Transform::identity());
140 
141  return true;
142 }
143 
144 
146 {
147  rp3d::Vector3 pos(fixed_camera_tf_.transform.translation.x,
148  fixed_camera_tf_.transform.translation.y,
149  fixed_camera_tf_.transform.translation.z);
150  rp3d::Quaternion orient(fixed_camera_tf_.transform.rotation.x,
151  fixed_camera_tf_.transform.rotation.y,
152  fixed_camera_tf_.transform.rotation.z,
153  fixed_camera_tf_.transform.rotation.w);
154 
155  rp3d::Transform transform(pos, orient);
156  fov_body_->setTransform(transform);
157 }
158 
159 
160 void CameraNodelet::overlap_fov(rp3d::CollisionBody* body)
161 {
162  // Check for overlaps between fov body and algae and update ray_world_
163  unsigned int n = ray_bodies_.size();
164  for (unsigned int k = 0; k < n; k++) {
165  ray_world_.destroyCollisionBody(ray_bodies_[k]);
166  }
167  ray_bodies_.resize(0);
168  ray_shapes_.resize(0);
169  corr_algae_.resize(0);
170  coll_world_.testOverlap(body, &overlap_cb_);
171 }
172 
173 
175  std::vector<float> &w_algae, std::vector<float> &h_algae,
176  std::vector<float> &inc_y3, std::vector<float> &inc_z3,
177  std::vector<geometry_msgs::TransformStamped> &tf_algae)
178 {
179  unsigned int n = w_algae.size();
180  float n_height = heatmaps_[0].size(); // height of the heatmap
181  float n_width = heatmaps_[0][0].size(); // width of the heatmap
182 
183  for (int k = 0; k < n; k++) {
184  // Compute the alga inverse transform
185  rp3d::Transform inverse_tf = ray_bodies_[k]->getTransform().getInverse();
186  rp3d::Vector3 pos = inverse_tf.getPosition();
187  rp3d::Quaternion quati = inverse_tf.getOrientation();
188  tf_algae[k].transform.translation.x = pos.x;
189  tf_algae[k].transform.translation.y = pos.y;
190  tf_algae[k].transform.translation.z = pos.z;
191  tf_algae[k].transform.rotation.x = quati.x;
192  tf_algae[k].transform.rotation.y = quati.y;
193  tf_algae[k].transform.rotation.z = quati.z;
194  tf_algae[k].transform.rotation.w = quati.w;
195 
196  // Get dimensions and increments
197  w_algae[k] = 2*ray_shapes_[k]->getExtent().y;
198  h_algae[k] = 2*ray_shapes_[k]->getExtent().z;
199 
200  inc_y3[k] = w_algae[k] / (n_width);
201  inc_z3[k] = h_algae[k] / (n_height);
202  }
203 }
204 
205 
206 tf2::Vector3 CameraNodelet::get_aim_pt(int pxl_h, int pxl_w, int n_pixel_h, int n_pixel_w)
207 {
208  if (n_pixel_h <= 0 || n_pixel_w <= 0) {
209  n_pixel_h = n_pxl_height_;
210  n_pixel_w = n_pxl_width_;
211  }
212 
213  tf2::Vector3 a(sensor_width_*(-1./2 + float(pxl_w)/(n_pixel_w-1)),
214  sensor_height_*(-1./2 + float(pxl_h)/(n_pixel_h-1)),
215  focal_length_);
216  tf2::Vector3 origin(0, 0, 0);
217  tf2::Vector3 aim_pt = fov_distance_ / tf2::tf2Distance(a, origin) * a;
218 
219  return aim_pt;
220 }
221 
222 
223 geometry_msgs::TransformStamped CameraNodelet::combine_transforms(
224  const vector<geometry_msgs::TransformStamped> &transforms)
225 {
226  tf2::Transform tf;
227  tf.setIdentity();
228 
229  for (int k = 0; k < transforms.size(); k++) {
230  tf2::Vector3 trans(transforms[k].transform.translation.x,
231  transforms[k].transform.translation.y,
232  transforms[k].transform.translation.z);
233  tf2::Quaternion orient(transforms[k].transform.rotation.x,
234  transforms[k].transform.rotation.y,
235  transforms[k].transform.rotation.z,
236  transforms[k].transform.rotation.w);
237 
238  tf *= tf2::Transform(orient, trans);
239  }
240 
241  tf2::Vector3 trans = tf.getOrigin();
242  tf2::Quaternion orient = tf.getRotation();
243 
244  geometry_msgs::TransformStamped ret;
245  ret.transform.translation.x = trans.getX();
246  ret.transform.translation.y = trans.getY();
247  ret.transform.translation.z = trans.getZ();
248  ret.transform.rotation.x = orient.getX();
249  ret.transform.rotation.y = orient.getY();
250  ret.transform.rotation.z = orient.getZ();
251  ret.transform.rotation.w = orient.getW();
252 
253  return ret;
254 }
255 
256 
257 tf2::Vector3 CameraNodelet::apply_transform(const tf2::Vector3 &in_vector,
258  const geometry_msgs::TransformStamped &transform)
259 {
260  geometry_msgs::Pose pose;
261  tf2::toMsg(in_vector, pose.position);
262  pose.orientation.w = 1;
263 
264  geometry_msgs::Pose transf_pose;
265  tf2::doTransform(pose, transf_pose, transform);
266 
267  return tf2::Vector3(transf_pose.position.x, transf_pose.position.y, transf_pose.position.z);
268 }
269 
270 
272  const tf2::Vector3 &aim_pt,
273  tf2::Vector3 &hit_pt,
274  int &alga_idx,
275  const geometry_msgs::TransformStamped &fixed_camera_tf,
276  const tf2::Vector3 &origin)
277 {
278  // Transform the ray into fixed frame
279  tf2::Vector3 tf_origin = apply_transform(origin, fixed_camera_tf);
280  tf2::Vector3 tf_aim_pt = apply_transform(aim_pt, fixed_camera_tf);
281 
282  // Raycast
283  raycast_cb_.alga_hit_ = false;
284  rp3d::Ray ray(tf2_to_rp3d(tf_origin), tf2_to_rp3d(tf_aim_pt));
285 
286  ray_world_.raycast(ray, &raycast_cb_);
287 
288  if (raycast_cb_.alga_hit_) {
289  hit_pt = raycast_cb_.hit_pt_;
290  alga_idx = raycast_cb_.alga_idx_;
291 
292  return true;
293  }
294  else {
295  return false;
296  }
297 }
298 
299 
301  const tf2::Vector3 &aim_pt,
302  float &distance,
303  const geometry_msgs::TransformStamped &fixed_camera_tf,
304  const tf2::Vector3 &origin)
305 {
306  // Perform raycast
307  int alga_idx;
308  tf2::Vector3 hit_pt;
309  bool alga_hit = raycast_alga(aim_pt, hit_pt, alga_idx, fixed_camera_tf, origin);
310 
311  // Get distance to the hit alga
312  if (alga_hit) {
313  distance = tf2::tf2Distance(hit_pt, apply_transform(origin, fixed_camera_tf));
314  return true;
315  } else {
316  return false;
317  }
318 }
319 
320 
322  const geometry_msgs::Pose &vp_pose,
323  int n_pxl_h, int n_pxl_w,
324  mf_sensors_simulator::CameraOutput &pxl_output,
325  ros::Time stamp)
326 {
327  // Prepare the output
328  int n_pixels = n_pxl_h * n_pxl_w;
329  pxl_output.x.reserve(n_pixels);
330  pxl_output.y.reserve(n_pixels);
331  pxl_output.z.reserve(n_pixels);
332 
333  // Fetch ROS transforms
334  geometry_msgs::TransformStamped camera_fixed_tf, fixed_camera_tf;
335 
336  if (stamp != ros::Time(0)) {
337  try {
338  camera_fixed_tf = tf_buffer_.lookupTransform(camera_frame_, fixed_frame_, stamp);
339  fixed_camera_tf = tf_buffer_.lookupTransform(fixed_frame_, camera_frame_, stamp);
340  }
341  catch (tf2::TransformException &ex) {
342  NODELET_WARN("[camera_nodelet] Bonjour %s", ex.what());
343  return false;
344  }
345  } else {
346  camera_fixed_tf = camera_fixed_tf_;
347  fixed_camera_tf = fixed_camera_tf_;
348  }
349 
350  // Check the four corners
351  int x_corner[4] = {0, n_pxl_h-1, n_pxl_h-1, 0}; // position of the 4 corners in height direction
352  int y_corner[4] = {0, 0, n_pxl_w-1, n_pxl_w-1}; // position of the 4 corners in width direction
353  bool hit_all_corners = true;
354  tf2::Vector3 origin(vp_pose.position.x, vp_pose.position.y, vp_pose.position.z);
355  geometry_msgs::TransformStamped camera_vp_tf = pose_to_tf(vp_pose); // transform from camera to viewpoint
356 
357  for (int l = 0; l < 4; l++) {
358  // Transform aim point into camera frame
359  tf2::Vector3 aim_pt1 = get_aim_pt(x_corner[l], y_corner[l], n_pxl_h, n_pxl_w); // aim point in view point camera frame
360  tf2::Vector3 aim_pt = apply_transform(aim_pt1, camera_vp_tf);
361 
362  // Perform raycast
363  int alga_idx;
364  tf2::Vector3 hit_pt; // hit point in fixed frame
365  bool alga_hit = raycast_alga(aim_pt, hit_pt, alga_idx, fixed_camera_tf, origin);
366 
367  if (alga_hit) {
368  tf2::Vector3 out_pt = apply_transform(hit_pt, camera_fixed_tf); // transform in camera frame
369 
370  pxl_output.x.emplace_back(out_pt.getX());
371  pxl_output.y.emplace_back(out_pt.getY());
372  pxl_output.z.emplace_back(out_pt.getZ());
373  } else {
374  hit_all_corners = false;
375  }
376  }
377 
378  // If all corners have been hit, simply interpolate between it.
379  if (hit_all_corners) {
380  tf2::Vector3 p1(pxl_output.x[0], pxl_output.y[0], pxl_output.z[0]);
381  tf2::Vector3 p2(pxl_output.x[1], pxl_output.y[1], pxl_output.z[1]);
382  tf2::Vector3 p3(pxl_output.x[2], pxl_output.y[2], pxl_output.z[2]);
383  tf2::Vector3 p4(pxl_output.x[3], pxl_output.y[3], pxl_output.z[3]);
384 
385  interpolate_quadri(p1, p2, p3, p4,
386  n_pxl_h, n_pxl_w,
387  pxl_output.x, pxl_output.y, pxl_output.z
388  );
389  }
390 
391  // Otherwise, just raycast through all pixels
392  else {
393  for (int i = 0; i < n_pxl_h; i++) {
394  for (int j = 0; j < n_pxl_w; j++) {
395  if ((i != 0 && i != n_pxl_h-1) || (j != 0 && j != n_pxl_w-1)) {
396  // Transform aim point into camera frame
397  tf2::Vector3 aim_pt1 = get_aim_pt(i, j, n_pxl_h, n_pxl_w); // aim point in view point camera frame
398  tf2::Vector3 aim_pt = apply_transform(aim_pt1, camera_vp_tf);
399 
400  // Perform raycast
401  int alga_idx;
402  tf2::Vector3 hit_pt;
403  bool alga_hit = raycast_alga(aim_pt, hit_pt, alga_idx, fixed_camera_tf, origin);
404 
405  if (alga_hit) {
406  tf2::Vector3 out_pt = apply_transform(hit_pt, camera_fixed_tf); // transform in camera frame
407 
408  pxl_output.x.emplace_back(out_pt.getX());
409  pxl_output.y.emplace_back(out_pt.getY());
410  pxl_output.z.emplace_back(out_pt.getZ());
411  }
412  }
413  }
414  }
415  }
416 
417  // Compute the distances to the waypoint origin and fill fake measurements
418  int n = pxl_output.x.size();
419  pxl_output.distance.resize(n);
420  pxl_output.value.resize(n);
421 
422  for (int k = 0; k < n; k++) {
423  tf2::Vector3 pt(pxl_output.x[k], pxl_output.y[k], pxl_output.z[k]);
424  pxl_output.distance[k] = tf2::tf2Distance(pt, origin);
425 
426  pxl_output.value[k] = 0;
427  }
428 
429  return true;
430 }
431 
432 
434  const tf2::Vector3 &p1, const tf2::Vector3 &p2, const tf2::Vector3 &p3, const tf2::Vector3 &p4,
435  int n_h, int n_w,
436  vector<float> &x_list, vector<float> &y_list, vector<float> &z_list
437 )
438 {
439  Eigen::Vector4d X(p1.getX(), p2.getX(), p3.getX(), p4.getX());
440  Eigen::Vector4d Y(p1.getY(), p2.getY(), p3.getY(), p4.getY());
441  Eigen::Vector4d Z(p1.getZ(), p2.getZ(), p3.getZ(), p4.getZ());
442 
443  Eigen::Matrix4d A;
444  A << 1, -1, -1, 1,
445  1, 1, -1, -1,
446  1, 1, 1, 1,
447  1, -1, 1, -1;
448  Eigen::Matrix4d A_inv = A.inverse();
449 
450  Eigen::Vector4d alpha = A_inv * X;
451  Eigen::Vector4d beta = A_inv * Y;
452  Eigen::Vector4d gamma = A_inv * Z;
453 
454  for (int i = 0; i < n_h; i++) {
455  for (int j = 0; j < n_w; j++) {
456  if ((i != 0 && i != n_h-1) || (j != 0 && j != n_w-1)) {
457  float u = -1 + 2.0*i/(n_h-1);
458  float v = -1 + 2.0*j/(n_w-1);
459  float x = alpha[0] + alpha[1]*u + alpha[2]*v + alpha[3]*u*v;
460  float y = beta[0] + beta[1]*u + beta[2]*v + beta[3]*u*v;
461  float z = gamma[0] + gamma[1]*u + gamma[2]*v + gamma[3]*u*v;
462 
463  x_list.emplace_back(x);
464  y_list.emplace_back(y);
465  z_list.emplace_back(z);
466  }
467  }
468  }
469 }
470 
471 
472 } // mfcpp
void get_ray_algae_carac(std::vector< float > &w_algae, std::vector< float > &h_algae, std::vector< float > &inc_y3, std::vector< float > &inc_z3, std::vector< geometry_msgs::TransformStamped > &tf_algae)
Gets position, dimension and axes of the algae for raycasting.
Definition: collision.cpp:174
Definition: common.hpp:23
std::vector< int > corr_algae_
Correspondance between the algae used for raytracing and all the others.
float focal_length_
Focal length of the camera.
virtual rp3d::decimal notifyRaycastHit(const rp3d::RaycastInfo &info)
Definition: collision.cpp:59
int n_pxl_width_
Nbr of pixels along sensor width.
rp3d::CollisionWorld ray_world_
World with algae colliding with FOV.
void overlap_fov(rp3d::CollisionBody *body)
Selects algae that are in field of view of the camera.
Definition: collision.cpp:160
rp3d::CollisionWorld coll_world_
World with all algae and camera FOV.
std::vector< std::vector< std::vector< float > > > heatmaps_
Disease heatmatps for all the algae.
tf2::Vector3 apply_transform(const tf2::Vector3 &in_vector, const geometry_msgs::TransformStamped &transform)
Applies a transform to a vector.
Definition: collision.cpp:257
geometry_msgs::TransformStamped fixed_camera_tf_
Transform from fixed frame to camera.
std::vector< box_shape_ptr > ray_shapes_
Collision shapes of algae for raycasting.
CameraNodelet * parent_
Parent CameraNodelet instance.
bool raycast_wall(const geometry_msgs::Pose &vp_pose, int n_pxl_h, int n_pxl_w, mf_sensors_simulator::CameraOutput &pxl_output, ros::Time stamp=ros::Time(0))
Definition: collision.cpp:321
geometry_msgs::TransformStamped pose_to_tf(const geometry_msgs::Pose &pose)
Converts a pose to a transform.
virtual void notifyOverlap(rp3d::CollisionBody *body)
Definition: collision.cpp:36
tf2_ros::Buffer tf_buffer_
std::unique_ptr< rp3d::BoxShape > box_shape_ptr
float distance(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Computes the the euclidean distance between two positions.
Definition: common.hpp:69
float fov_distance_
Maximum distance detected by the camera.
rp3d::Vector3 tf2_to_rp3d(const tf2::Vector3 vect)
Converts a tf2 vector to a rp3d vector.
geometry_msgs::TransformStamped inverse_tf(const geometry_msgs::TransformStamped &transform)
Inverse a transform.
void interpolate_quadri(const tf2::Vector3 &p1, const tf2::Vector3 &p2, const tf2::Vector3 &p3, const tf2::Vector3 &p4, int n_h, int n_w, std::vector< float > &x_list, std::vector< float > &y_list, std::vector< float > &z_list)
Interpolates a 3D quadrilateral lying on a plane.
Definition: collision.cpp:433
float sensor_height_
Height of the camera sensor.
bool raycast_alga(const tf2::Vector3 &aim_pt, tf2::Vector3 &hit_pt, int &alga_idx, const geometry_msgs::TransformStamped &fixed_camera_tf, const tf2::Vector3 &origin=tf2::Vector3(0, 0, 0))
Casts a ray on algae and gets hit point.
Definition: collision.cpp:271
Callback class for raycasting.
std::string fixed_frame_
Frame in which the pose is expressed.
Callback class for overlap detection.
RaycastCallback(CameraNodelet *parent)
Definition: collision.cpp:28
void update_fov_pose()
Updates pose of field of view body.
Definition: collision.cpp:145
std::vector< rp3d::CollisionBody * > ray_bodies_
Collision bodies for raycasting.
geometry_msgs::TransformStamped combine_transforms(const std::vector< geometry_msgs::TransformStamped > &transforms)
Combine transforms sequentially.
Definition: collision.cpp:223
RaycastCallback raycast_cb_
Callback instance for raycasting.
OverlapCallback overlap_cb_
int alga_idx_
Index of the hit alga in the ray_bodies_ vector.
std::string camera_frame_
Frame of the camera.
std::vector< rp3d::CollisionBody * > algae_bodies_
Collision bodies of the algae.
int n_pxl_height_
Nbr of pixels along sensor height.
tf2::Vector3 get_aim_pt(int pxl_h, int pxl_w, int n_pixel_h=-1, int n_pixel_w=-1)
Gets an aim point from pixel coordinates for raycasting.
Definition: collision.cpp:206
geometry_msgs::TransformStamped camera_fixed_tf_
Transform from camera to fixed frame.
bool alga_hit_
Whether an alga has been hit.
float sensor_width_
Width of the camera sensor.
CameraNodelet * parent_
Parent CameraNodelet instance.
Nodelet for a simulated camera.
std::vector< box_shape_ptr > algae_shapes_
Collision shapes of the algae bodies.
bool multi_fov_body(const std::vector< geometry_msgs::Pose > &poses, rp3d::CollisionBody *body, std::unique_ptr< rp3d::BoxShape > &shape, ros::Time stamp=ros::Time(0))
Fill a collision body for multi-pose FOV overlap.
Definition: collision.cpp:76
Declaration of a nodelet simulating a camera.
rp3d::CollisionBody * fov_body_
Collision body of the camera FOV.