Corentin Chauvin-Hameau – 2019-2020
Coverage Path Planning for an underwater robot surveying a marine farm
camera_nodelet.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  *
4  * \brief Definition of a nodelet simulating a camera
5  * \author Corentin Chauvin-Hameau
6  * \date 2019
7  */
8 
9 #include "camera_nodelet.hpp"
10 #include "mf_sensors_simulator/CameraOutput.h"
11 #include "mf_sensors_simulator/MultiPoses.h"
13 #include "mf_farm_simulator/Algae.h"
14 #include "reactphysics3d.h"
15 #include <visualization_msgs/Marker.h>
16 #include <geometry_msgs/TransformStamped.h>
17 #include <geometry_msgs/Pose.h>
18 #include <geometry_msgs/Point.h>
19 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
20 #include <tf2_ros/transform_listener.h>
21 #include <pluginlib/class_list_macros.h>
22 #include <ros/ros.h>
23 #include <eigen3/Eigen/Dense>
24 #include <csignal>
25 #include <string>
26 #include <vector>
27 #include <iostream>
28 
29 using namespace std;
30 
31 PLUGINLIB_EXPORT_CLASS(mfcpp::CameraNodelet, nodelet::Nodelet)
32 
33 
34 namespace mfcpp {
35 
36 /*
37  * Definition of static variables
38  */
39 sig_atomic_t volatile CameraNodelet::b_sigint_ = 0;
40 ros::Timer CameraNodelet::main_timer_ = ros::Timer();
41 
42 /*
43  * Definition of member functions
44  */
45 CameraNodelet::CameraNodelet():
46  tf_listener_(tf_buffer_),
47  raycast_cb_(this),
48  overlap_cb_(this)
49 {
50 
51 }
52 
54 
55 
57 {
58  nh_ = getNodeHandle();
59  private_nh_ = getPrivateNodeHandle();
60 
61  // Catch SIGINT (Ctrl+C) stop signal
62  struct sigaction sigIntHandler;
63  sigIntHandler.sa_handler = CameraNodelet::sigint_handler;
64  sigemptyset(&sigIntHandler.sa_mask);
65  sigIntHandler.sa_flags = 0;
66  sigaction(SIGINT, &sigIntHandler, NULL);
67 
68  // ROS parameters
69  private_nh_.param<float>("camera_freq", camera_freq_, 1.0);
70  private_nh_.param<string>("fixed_frame", fixed_frame_, "ocean");
71  private_nh_.param<string>("robot_frame", robot_frame_, "base_link");
72  private_nh_.param<string>("camera_frame", camera_frame_, "camera");
73  private_nh_.param<vector<float>>("fov_color", fov_color_, vector<float>(4, 1.0));
74 
75  private_nh_.param<float>("focal_length", focal_length_, 0.0028);
76  private_nh_.param<float>("sensor_width", sensor_width_, 0.0064);
77  private_nh_.param<float>("sensor_height", sensor_height_, 0.00384);
78  private_nh_.param<float>("fov_distance", fov_distance_, 2.0);
79  private_nh_.param<int>("n_pxl_height", n_pxl_height_, 480);
80  private_nh_.param<int>("n_pxl_width", n_pxl_width_, 800);
81 
82  private_nh_.param<bool>("noise_meas", noise_meas_, true);
83  private_nh_.param<float>("noise_std", noise_std_, 1.0);
84  private_nh_.param<float>("noise_decay", noise_decay_, 1.0);
85 
86  // ROS subscribers
87  algae_sub_ = private_nh_.subscribe<mf_farm_simulator::Algae>("algae", 1,
88  boost::bind(&CameraNodelet::algae_cb, this, _1));
89 
90  // ROS services
91  ray_multi_serv_ = nh_.advertiseService("raycast_multi", &CameraNodelet::ray_multi_cb, this);
92 
93  // ROS publishers
94  out_pub_ = nh_.advertise<mf_sensors_simulator::CameraOutput>("camera_out", 0);
95  rviz_pub_ = nh_.advertise<visualization_msgs::Marker>("camera_markers", 0);
96 
97  // Other parameters
98  algae_msg_received_ = false;
99  world_init_ = false;
100 
101  // Initialise collision world
102  rp3d::CollisionWorld coll_world_(world_settings_);
103 
104 
105  // Main loop
106  main_timer_ = private_nh_.createTimer(
107  ros::Duration(1/camera_freq_), &CameraNodelet::main_cb, this
108  );
109 }
110 
111 
112 void CameraNodelet::main_cb(const ros::TimerEvent &timer_event)
113 {
114  if (!ros::ok() || ros::isShuttingDown() || b_sigint_)
115  return;
116 
118  init_coll_world();
119  world_init_ = true;
120  } else if (algae_msg_received_) {
121  update_algae();
122  algae_msg_received_ = false;
123  }
124 
125  if (world_init_ && get_tf()) {
127  publish_output();
128  }
129 }
130 
131 
133  b_sigint_ = 1;
134  main_timer_.stop();
135 
136  raise(SIGTERM);
137 }
138 
139 
140 void CameraNodelet::algae_cb(const mf_farm_simulator::AlgaeConstPtr msg)
141 {
143  algae_msg_received_ = true;
144 }
145 
146 
148 {
149  // Add camera FOV
150  fov_body_ = coll_world_.createCollisionBody(rp3d::Transform::identity());
151 
152  float x = sensor_width_ * fov_distance_
153  / (2 * sqrt(pow(focal_length_, 2) + pow(sensor_width_, 2)/4));
154  float y = sensor_width_ * fov_distance_
155  / (2 * sqrt(pow(focal_length_, 2) + pow(sensor_height_, 2)/4));
156 
157  rp3d::Vector3 fov_half_extents(x, y, fov_distance_/2);
158  fov_shape_ = unique_ptr<rp3d::BoxShape>(new rp3d::BoxShape(fov_half_extents));
159 
160  rp3d::Vector3 fov_pos(0, 0, fov_distance_/2);
161  rp3d::Quaternion fov_orient(0, 0, 0, 1);
162  rp3d::Transform fov_transform(fov_pos, fov_orient);
163  fov_body_->addCollisionShape(fov_shape_.get(), fov_transform);
164 
165  // Add algae
166  unsigned int n = last_algae_msg_->algae.size();
167  algae_bodies_.resize(n);
168  algae_shapes_.resize(n);
169  heatmaps_.resize(n);
170 
171  for (unsigned int k = 0; k < n; k++) {
172  const mf_farm_simulator::Alga *al = &last_algae_msg_->algae[k];
173 
174  // Creating a collision body
175  rp3d::Vector3 pos(al->position.x, al->position.y, al->position.z);
176  rp3d::Quaternion orient(al->orientation.x, al->orientation.y,
177  al->orientation.z, al->orientation.w);
178 
179  rp3d::Transform transform(pos, orient);
180  rp3d::CollisionBody* body = coll_world_.createCollisionBody(transform);
181  algae_bodies_[k] = body;
182 
183  // Adding a collision shape to the body
184  rp3d::Vector3 half_extents(al->dimensions.x/2, al->dimensions.y/2,
185  al->dimensions.z/2);
186  algae_shapes_[k] = unique_ptr<rp3d::BoxShape>(new rp3d::BoxShape(half_extents));
187  body->addCollisionShape(algae_shapes_[k].get(), rp3d::Transform::identity());
188 
189  // Storing disease heatmap
190  unsigned int a = al->disease_heatmap.size();
191  unsigned int b = al->disease_heatmap[0].data.size();
192  heatmaps_[k].resize(a, vector<float>(b));
193 
194  for (unsigned int i = 0; i < a; i++) {
195  for (unsigned int j = 0; j < b; j++) {
196  heatmaps_[k][i][j] = al->disease_heatmap[i].data[j];
197  }
198  }
199  }
200 }
201 
202 
204 {
205  unsigned int n = last_algae_msg_->algae.size();
206 
207  for (unsigned int k = 0; k < n; k++) {
208  const mf_farm_simulator::Alga *al = &last_algae_msg_->algae[k];
209 
210  // Creating a collision body
211  rp3d::Vector3 pos(al->position.x, al->position.y, al->position.z);
212  rp3d::Quaternion orient(al->orientation.x, al->orientation.y,
213  al->orientation.z, al->orientation.w);
214 
215  rp3d::Transform transform(pos, orient);
216  algae_bodies_[k]->setTransform(transform);
217  }
218 }
219 
220 
222 {
223  geometry_msgs::TransformStamped tf1, tf2, tf3, tf4;
224 
225  try {
226  tf1 = tf_buffer_.lookupTransform(fixed_frame_, camera_frame_, ros::Time(0));
227  tf2 = tf_buffer_.lookupTransform(camera_frame_, fixed_frame_, ros::Time(0));
228  tf3 = tf_buffer_.lookupTransform(camera_frame_, robot_frame_, ros::Time(0));
229  tf4 = tf_buffer_.lookupTransform(robot_frame_, camera_frame_, ros::Time(0));
230  }
231  catch (tf2::TransformException &ex) {
232  NODELET_WARN("[camera_nodelet] %s",ex.what());
233  return false;
234  }
235 
236  fixed_camera_tf_ = tf1;
237  camera_fixed_tf_ = tf2;
238  camera_robot_tf_ = tf3;
239  robot_camera_tf_ = tf4;
240  return true;
241 }
242 
243 
244 bool CameraNodelet::ray_multi_cb(mf_sensors_simulator::MultiPoses::Request &req,
245  mf_sensors_simulator::MultiPoses::Response &res)
246 {
247  // Check for initialisation
248  if (!world_init_ || !get_tf()) {
249  res.is_success = false;
250  return true;
251  }
252 
253  // Prepare the output
254  int n_pxl_h = req.n_pxl_height;
255  int n_pxl_w = req.n_pxl_width;
256  if (n_pxl_h <= 0 || n_pxl_w <= 0) {
257  n_pxl_h = n_pxl_height_;
258  n_pxl_w = n_pxl_width_;
259  }
260 
261  int nbr_poses = req.pose_array.poses.size();
262  res.results.resize(nbr_poses);
263 
264  if (nbr_poses == 0) {
265  NODELET_WARN("[camera_nodelet] Called ray_multi_cb with 0 pose");
266  res.is_success = false;
267  return true;
268  }
269 
270  // Creating a collision body for field of view at all poses
271  bool success = true;
272  rp3d::CollisionBody* body = coll_world_.createCollisionBody(rp3d::Transform::identity());
273  unique_ptr<rp3d::BoxShape> shape;
274  success = multi_fov_body(req.pose_array.poses, body, shape, req.stamp);
275 
276  if (!success) {
277  coll_world_.destroyCollisionBody(body);
278  res.is_success = success;
279  return true;
280  }
281 
282  // Selects algae that are in field of view of the camera
283  coll_mutex_.lock();
284  overlap_fov(body);
285 
286  // Raycast all pixels for each pose
287  for (int k = 0; k < nbr_poses; k++) {
288  bool ret = raycast_wall(req.pose_array.poses[k], n_pxl_h, n_pxl_w, res.results[k], req.stamp);
289 
290  if (!ret) {
291  success = false;
292  break;
293  }
294  }
295 
296  // Conclude
297  coll_world_.destroyCollisionBody(body);
298  coll_mutex_.unlock();
299  res.is_success = success;
300 
301  return true;
302 }
303 
304 
305 } // namespace mfcpp
float noise_std_
Maximum standard deviation of the measurement noise.
Definition: common.hpp:23
bool world_init_
Whether the collision world has been initialised.
bool algae_msg_received_
Whether an algae message has been received.
float focal_length_
Focal length of the camera.
void publish_output()
Publishes camera output.
Definition: output.cpp:165
void algae_cb(const mf_farm_simulator::AlgaeConstPtr msg)
Callback for the algae of the farm.
int n_pxl_width_
Nbr of pixels along sensor width.
static ros::Timer main_timer_
Timer callback for the main function.
ros::NodeHandle nh_
Node handler (for topics and services)
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.
float camera_freq_
Frequency of the sensor.
std::vector< std::vector< std::vector< float > > > heatmaps_
Disease heatmatps for all the algae.
box_shape_ptr fov_shape_
Collision shapes of the FOV body.
virtual void onInit()
Function called at beginning of nodelet execution.
Declaration of common Rviz display functions.
geometry_msgs::TransformStamped fixed_camera_tf_
Transform from fixed frame to camera.
ros::NodeHandle private_nh_
Private node handler (for parameters)
static sig_atomic_t volatile b_sigint_
Whether SIGINT signal has been received.
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
tf2_ros::Buffer tf_buffer_
std::vector< float > fov_color_
Color of the camera field of view Rviz marker.
void update_algae()
Updates algae in the collision world.
void init_coll_world()
Initialise the collision world.
void main_cb(const ros::TimerEvent &timer_event)
Main callback which is called by a timer.
float fov_distance_
Maximum distance detected by the camera.
std::string robot_frame_
Frame of the robot.
static void sigint_handler(int s)
SINGINT (Ctrl+C) callback to stop the nodelet properly.
float sensor_height_
Height of the camera sensor.
rp3d::WorldSettings world_settings_
Collision world settings.
mf_farm_simulator::AlgaeConstPtr last_algae_msg_
Last algae message.
geometry_msgs::TransformStamped robot_camera_tf_
Transform from robot to camera frame.
std::string fixed_frame_
Frame in which the pose is expressed.
geometry_msgs::TransformStamped camera_robot_tf_
Transform from camera to robot frame.
std::mutex coll_mutex_
Mutex to control access to collision variables.
ros::Publisher rviz_pub_
Publisher for Rviz markers.
bool get_tf()
Gets tf transforms.
ros::Publisher out_pub_
Publisher for the camera output.
bool noise_meas_
Whether to noise measurements on disease heatmap.
ros::ServiceServer ray_multi_serv_
Service for raycasting from several camera poses.
bool ray_multi_cb(mf_sensors_simulator::MultiPoses::Request &req, mf_sensors_simulator::MultiPoses::Response &res)
Service callback for raycasting from several camera poses.
ros::Subscriber algae_sub_
Subscriber for the algae of the farm.
std::string camera_frame_
Frame of the camera.
void publish_rviz_fov()
Publishes a Rviz marker for the camera field of view.
Definition: output.cpp:24
std::vector< rp3d::CollisionBody * > algae_bodies_
Collision bodies of the algae.
int n_pxl_height_
Nbr of pixels along sensor height.
geometry_msgs::TransformStamped camera_fixed_tf_
Transform from camera to fixed frame.
float sensor_width_
Width of the camera sensor.
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.