panorama.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016, Yujin Robot, Rohan Agrawal
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of Yujin Robot nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
40 #include <cmath>
41 #include <iostream>
42 #include <eigen3/Eigen/Core>
43 #include <eigen3/Eigen/Geometry>
45 #include <cv_bridge/cv_bridge.h>
47 
48 namespace turtlebot_panorama
49 {
50 
51 PanoApp::PanoApp() : nh(), priv_nh("~")
52 {
53 
54 }
55 
57 {
58 
59 }
60 
62 {
63  //***************************
64  // public API for the app
65  //***************************
67 
69  pub_stitched = it_priv.advertise("panorama", 1, true);
70 
72  sub_camera = it.subscribe("/camera/rgb/image_raw", 1, &PanoApp::cameraImageCb, this);
73 
74  //***************************
75  // Robot control
76  //***************************
77  pub_cmd_vel = nh.advertise<geometry_msgs::Twist>("cmd_vel", 100);
78  sub_odom = nh.subscribe("odom", 100, &PanoApp::odomCb, this);
79 
80  cmd_vel.linear.x = 0.0f;
81  cmd_vel.linear.y = 0.0f;
82  cmd_vel.linear.z = 0.0f;
83  cmd_vel.angular.x = 0.0f;
84  cmd_vel.angular.y = 0.0f;
85  cmd_vel.angular.z = 0.0f;
87  is_active = false;
88  continuous = false;
89  ang_vel_cur = 0.0;
90  given_angle = 0.0;
91  angle = 0.0;
92  last_angle = 0.0;
93 }
94 
96 {
97  ros::Rate loop_rate(10);
98  double start_time;
99  start_time = 0.0;
100  bool take_snapshot = false;
101 
102  while (ros::ok())
103  {
104  if (is_active)
105  {
106  ROS_INFO_STREAM_THROTTLE(1.0, "Degrees to go: " << radians_to_degrees(std::abs(given_angle - angle)));
107  if ((given_angle - angle) <= 0.0174) // check, if target angle is reached (< 1 degree)
108  {
109  snap();
110 
112 
113  ROS_INFO("Stiching %lu images", images_.size());
114 
115  cv::Mat pano;
116  cv::Stitcher stitcher = cv::Stitcher::createDefault(false);
117  cv::Stitcher::Status status = stitcher.stitch(images_, pano);
118  log("Finished Stiching");
119 
120  cv_bridge::CvImage cv_img;
121  cv_img.image = pano;
122  cv_img.encoding = "bgr8";
123  cv_img.header.stamp = ros::Time::now();
124  pub_stitched.publish(cv_img.toImageMsg());
125  log("Publishing Completed Panorama");
126  ROS_INFO("Angle: %f", angle);
127  ROS_INFO("Last Angle: %f", last_angle);
128  angle=0.0;
129  last_angle=0.0;
130  images_.clear();
131  // imwrite("pano.jpg", pano);
132  is_active = false;
133  }
134  else
135  {
136  if (continuous) // then snap_interval is a duration
137  {
138  log("Continuous Mode panorama");
139  rotate();
141  snap();
142  ROS_INFO("Angle Continuous: %f", angle);
143  ROS_INFO("Angle Given: %f", given_angle);
144  }
145  else
146  {
147  if (hasReachedAngle())
148  {
149  pub_cmd_vel.publish(zero_cmd_vel); // stop before taking a snapshot
150  take_snapshot = true;
151  }
152  if (take_snapshot)
153  {
154  if (std::abs(ang_vel_cur) <= 0.01) // wait until robot has stopped
155  {
156  snap();
157  take_snapshot = false;
158  }
159  else
160  {
161  std::stringstream ss;
162  std::string str;
163  ss << "Waiting for robot to stop ... (speed = " << ang_vel_cur << ")";
164  str = ss.str();
165  log(str);
166  }
167  }
168  else
169  {
170  rotate();
171  }
172  }
173  }
174  }
175  ros::spinOnce();
176  loop_rate.sleep();
177  }
178 }
179 
181 {
182  log("snap");
183  store_image = true;
184  ros::spinOnce();
185  ros::Duration(1.0).sleep();
186 }
187 
189 {
190  log("rotate");
191  pub_cmd_vel.publish(cmd_vel); // rotate a bit
192 }
193 
195 {
197  {
198  last_angle = angle;
199  return true;
200  }
201  else
202  {
203  return false;
204  }
205 }
206 
207 void PanoApp::odomCb(const nav_msgs::OdometryConstPtr& msg)
208 {
209  static double heading_last = 0.0f;
210  double heading = 0.0f;
211 
212  Eigen::AngleAxisf angle_axis(Eigen::Quaternionf(msg->pose.pose.orientation.w,
213  msg->pose.pose.orientation.x,
214  msg->pose.pose.orientation.y,
215  msg->pose.pose.orientation.z));
216  Eigen::Vector3f axis = angle_axis.axis();
217 
218  if (axis(2) > 0.0)
219  {
220  heading = angle_axis.angle();
221  }
222  else if (axis(2) < 0.0)
223  {
224  heading = -1.0 * angle_axis.angle();
225  }
226 
227  angle += std::abs(wrap_angle(heading - heading_last));
228  heading_last = heading;
229  ang_vel_cur = msg->twist.twist.angular.z;
230 }
231 
232 //*************************
233 // Public interface
234 //*************************
235 bool PanoApp::takePanoServiceCb(turtlebot3_applications_msgs::TakePanorama::Request& request,
236  turtlebot3_applications_msgs::TakePanorama::Response& response)
237 {
238  if (is_active && (request.mode == request.CONTINUOUS || request.mode == request.SNAPANDROTATE))
239  {
240  log("Panorama creation already in progress.");
241  response.status = request.IN_PROGRESS;
242  }
243  else if (is_active && (request.mode == request.STOP))
244  {
245  is_active = false;
246  log("Panorama creation stopped.");
247  response.status = request.STOPPED;
248  return true;
249  }
250  else if (!is_active && (request.mode == request.STOP))
251  {
252  log("No panorama creation in progress.");
253  response.status = request.STOPPED;
254  return true;
255  }
256  else
257  {
258  if (request.pano_angle <= 0.0)
259  {
260  log("Specified panorama angle is zero or negative! Panorama creation aborted.");
261  return true;
262  }
263  else if (request.snap_interval <= 0.0)
264  {
265  log("Specified snapshot interval is zero or negative! Panorama creation aborted.");
266  return true;
267  }
268  else if (request.rot_vel == 0.0)
269  {
270  log("Specified rotating speed is zero! Panorama creation aborted.");
271  return true;
272  }
273  else
274  {
275  given_angle = degrees_to_radians(request.pano_angle);
276  snap_interval = request.snap_interval;
277  cmd_vel.angular.z = request.rot_vel;
278  }
279  if (request.mode == turtlebot3_applications_msgs::TakePanoramaRequest::CONTINUOUS)
280  {
281  continuous = true;
282  }
283  else
284  {
285  continuous = false;
286  }
287  log("Starting panorama creation.");
288  // startPanoAction();
289  is_active = true;
290  response.status = request.STARTED;
291  }
292  return true;
293 }
294 
295 void PanoApp::cameraImageCb(const sensor_msgs::ImageConstPtr& msg)
296 {
297  if (store_image)
298  {
299  std::cout << "encoding: " << msg->encoding << std::endl;
300  std::cout << "is_bigendian: " << msg->is_bigendian << std::endl;
301 
302  cv_bridge::CvImagePtr cv_ptr;
303 
304  try
305  {
307  }
308  catch (cv_bridge::Exception& e)
309  {
310  ROS_ERROR("cv_bridge exception: %s", e.what());
311  return;
312  }
313 
314  images_.push_back(cv_ptr->image);
315  store_image = false;
316  }
317  else
318  {
319  //pub_stitched.publish(msg);
320  }
321 }
322 
323 //*************
324 // Logging
325 //*************
326 void PanoApp::log(std::string log)
327 {
328  std_msgs::String msg;
329  msg.data = log;
330  ROS_INFO_STREAM(log);
331 }
332 } //namespace turtlebot_panorama
333 
334 int main(int argc, char **argv)
335 {
336  ros::init(argc, argv, "turtlebot3_panorama");
337 
339  pano.log("Panorama app starting...");
340  pano.init();
341  pano.log("Panorama application initialised.");
342  pano.spin();
343  pano.log("Bye, bye!");
344 
345  return 0;
346 }
geometry_msgs::Twist zero_cmd_vel
Definition: panorama.h:88
Subscriber subscribe(const std::string &base_topic, uint32_t queue_size, const boost::function< void(const sensor_msgs::ImageConstPtr &)> &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
ros::NodeHandle priv_nh
Definition: panorama.h:85
T radians_to_degrees(const T &radians)
Definition: geometry.h:36
ros::ServiceServer srv_start_pano
Definition: panorama.h:97
void publish(const boost::shared_ptr< M > &message) const
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void cameraImageCb(const sensor_msgs::ImageConstPtr &msg)
Definition: panorama.cpp:295
bool sleep() const
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
std::string encoding
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
image_transport::Publisher pub_stitched
Definition: panorama.h:94
bool takePanoServiceCb(turtlebot3_applications_msgs::TakePanorama::Request &request, turtlebot3_applications_msgs::TakePanorama::Response &response)
Definition: panorama.cpp:235
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
ros::Publisher pub_cmd_vel
Definition: panorama.h:100
std::vector< cv::Mat > images_
Definition: panorama.h:104
image_transport::Subscriber sub_camera
Definition: panorama.h:95
ros::NodeHandle nh
Definition: panorama.h:84
void odomCb(const nav_msgs::OdometryConstPtr &msg)
Definition: panorama.cpp:207
void publish(const sensor_msgs::Image &message) const
#define ROS_INFO(...)
int main(int argc, char **argv)
Definition: panorama.cpp:334
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
ROSCPP_DECL bool ok()
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool sleep()
geometry_msgs::Twist cmd_vel
Definition: panorama.h:88
void log(std::string msg)
Definition: panorama.cpp:326
T degrees_to_radians(const T &degrees)
Definition: geometry.h:29
#define ROS_INFO_STREAM(args)
T wrap_angle(const T &angle)
Definition: geometry.h:43
static Time now()
#define ROS_INFO_STREAM_THROTTLE(rate, args)
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
sensor_msgs::ImagePtr toImageMsg() const
ros::Subscriber sub_odom
Definition: panorama.h:102
std_msgs::Header header


turtlebot3_panorama
Author(s): Younghun Ju, Jihoon Lee, Marcus Liebhardt, Christopher Tatsch
autogenerated on Wed May 6 2020 03:15:44