00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00040 #include <cmath>
00041 #include <iostream>
00042 #include <eigen3/Eigen/Core>
00043 #include <eigen3/Eigen/Geometry>
00044 #include <turtlebot_panorama/panorama.h>
00045
00046 namespace turtlebot_panorama
00047 {
00048
00049 PanoApp::PanoApp() : nh(), priv_nh("~")
00050 {
00051 std::string name = ros::this_node::getName();
00052
00053 ros::param::param<int>("~default_mode", default_mode, 1);
00054 ros::param::param<double>("~default_pano_angle", default_pano_angle, (2 * M_PI));
00055 ros::param::param<double>("~default_snap_interval", default_snap_interval, 2.0);
00056 ros::param::param<double>("~default_rotation_velocity", default_rotation_velocity, 0.3);
00057
00058 ros::param::param<std::string>("~camera_name", params["camera_name"], "/camera/rgb");
00059 ros::param::param<std::string>("~bag_location", params["bag_location"], "/home/turtlebot/pano.bag");
00060
00061 pub_log = priv_nh.advertise<std_msgs::String>("log", 100);
00062 }
00063
00064 PanoApp::~PanoApp()
00065 {
00066 delete pano_ros_client;
00067 }
00068
00069 void PanoApp::init()
00070 {
00071
00072
00073
00074 srv_start_pano = priv_nh.advertiseService("take_pano", &PanoApp::takePanoServiceCb, this);
00075 sub_start_pano = priv_nh.subscribe("take_pano", 1, &PanoApp::takePanoCb, this);
00076 sub_stop_pano = priv_nh.subscribe("stop_pano", 1, &PanoApp::stopPanoCb, this);
00077 image_transport::ImageTransport it_priv(priv_nh);
00078 pub_stitched = it_priv.advertise("panorama", 1, true);
00079
00080
00081
00082
00083 pub_cmd_vel = nh.advertise<geometry_msgs::Twist>("cmd_vel", 100);
00084 sub_odom = nh.subscribe("odom", 100, &PanoApp::odomCb, this);
00085
00086
00087
00088
00089 pano_ros_client = new actionlib::SimpleActionClient<pano_ros::PanoCaptureAction>("pano_server", true);
00090 log("Waiting for Pano ROS server ...");
00091 pano_ros_client->waitForServer();
00092 log("Connected to Pano ROS server.");
00093 pub_action_snap = nh.advertise<std_msgs::Empty>("pano_server/snap", 100);
00094 pub_action_stop = nh.advertise<std_msgs::Empty>("pano_server/stop", 100);
00095 image_transport::ImageTransport it_pano(nh);
00096 sub_stitched = it_pano.subscribe("pano_server/stitch", 1, &PanoApp::stitchedImageCb, this);
00097
00098 cmd_vel.linear.x = 0.0f;
00099 cmd_vel.linear.y = 0.0f;
00100 cmd_vel.linear.z = 0.0f;
00101 cmd_vel.angular.x = 0.0f;
00102 cmd_vel.angular.y = 0.0f;
00103 cmd_vel.angular.z = 0.0f;
00104 zero_cmd_vel = cmd_vel;
00105 is_active = false;
00106 continuous = false;
00107 ang_vel_cur = 0.0;
00108 given_angle = 0.0;
00109 angle = 0.0;
00110 last_angle = 0.0;
00111 }
00112
00113 void PanoApp::spin()
00114 {
00115 ros::Rate loop_rate(10);
00116 double start_time;
00117 start_time = 0.0;
00118 bool take_snapshot = false;
00119
00120 while (ros::ok())
00121 {
00122 if (is_active)
00123 {
00124 ROS_INFO_STREAM_THROTTLE(1.0, "Degrees to go: " << radians_to_degrees(std::abs(given_angle - angle)));
00125 if ((given_angle - angle) <= 0.0174)
00126 {
00127 snap();
00128 pub_cmd_vel.publish(zero_cmd_vel);
00129 ros::Duration(1.0).sleep();
00130 stopPanoAction();
00131 }
00132 else
00133 {
00134 if (continuous)
00135 {
00136 double now = ros::Time::now().toSec();
00137 if ((now - start_time) > snap_interval)
00138 {
00139 snap();
00140 start_time = now;
00141 }
00142 rotate();
00143 }
00144 else
00145 {
00146 if (hasReachedAngle())
00147 {
00148 pub_cmd_vel.publish(zero_cmd_vel);
00149 take_snapshot = true;
00150 }
00151 if (take_snapshot)
00152 {
00153 if (std::abs(ang_vel_cur) <= 0.000001)
00154 {
00155 snap();
00156 take_snapshot = false;
00157 }
00158 else
00159 {
00160 std::stringstream ss;
00161 std::string str;
00162 ss << "Waiting for robot to stop ... (speed = " << ang_vel_cur << ")";
00163 str = ss.str();
00164 log(str);
00165 }
00166 }
00167 else
00168 {
00169 rotate();
00170 }
00171 }
00172 }
00173 }
00174 ros::spinOnce();
00175 loop_rate.sleep();
00176 }
00177 }
00178
00179
00180 void PanoApp::snap()
00181 {
00182 log("snap");
00183 pub_action_snap.publish(empty_msg);
00184 }
00185
00186 void PanoApp::rotate()
00187 {
00188 log("rotate");
00189 pub_cmd_vel.publish(cmd_vel);
00190 }
00191
00192 bool PanoApp::hasReachedAngle()
00193 {
00194 if (angle > last_angle + degrees_to_radians(snap_interval))
00195 {
00196 last_angle = angle;
00197 return true;
00198 }
00199 else
00200 {
00201 return false;
00202 }
00203 }
00204
00205 void PanoApp::odomCb(const nav_msgs::OdometryConstPtr& msg)
00206 {
00207 static double heading_last = 0.0f;
00208 double heading = 0.0f;
00209
00210 Eigen::AngleAxisf angle_axis(Eigen::Quaternionf(msg->pose.pose.orientation.w,
00211 msg->pose.pose.orientation.x,
00212 msg->pose.pose.orientation.y,
00213 msg->pose.pose.orientation.z));
00214 Eigen::Vector3f axis = angle_axis.axis();
00215
00216 if (axis(2) > 0.0)
00217 {
00218 heading = angle_axis.angle();
00219 }
00220 else if (axis(2) < 0.0)
00221 {
00222 heading = -1.0 * angle_axis.angle();
00223 }
00224
00225 angle += std::abs(wrap_angle(heading - heading_last));
00226 heading_last = heading;
00227 ang_vel_cur = msg->twist.twist.angular.z;
00228 }
00229
00230
00231
00232
00233 bool PanoApp::takePanoServiceCb(TakePano::Request& request, TakePano::Response& response)
00234 {
00235 if (is_active && (request.mode == request.CONTINUOUS || request.mode == request.SNAPANDROTATE))
00236 {
00237 log("Panorama creation already in progress.");
00238 response.status = response.IN_PROGRESS;
00239 }
00240 else if (is_active && (request.mode == request.STOP))
00241 {
00242 stopPanoAction();
00243 is_active = false;
00244 log("Panorama creation stopped.");
00245 response.status = response.STOPPED;
00246 return true;
00247 }
00248 else if (!is_active && (request.mode == request.STOP))
00249 {
00250 log("No panorama creation in progress.");
00251 response.status = response.STOPPED;
00252 return true;
00253 }
00254 else
00255 {
00256 if (request.pano_angle <= 0.0)
00257 {
00258 log("Specified panorama angle is zero or negative! Panorama creation aborted.");
00259 return true;
00260 }
00261 else if (request.snap_interval <= 0.0)
00262 {
00263 log("Specified snapshot interval is zero or negative! Panorama creation aborted.");
00264 return true;
00265 }
00266 else if (request.rot_vel == 0.0)
00267 {
00268 log("Specified rotating speed is zero! Panorama creation aborted.");
00269 return true;
00270 }
00271 else
00272 {
00273 given_angle = degrees_to_radians(request.pano_angle);
00274 snap_interval = request.snap_interval;
00275 cmd_vel.angular.z = request.rot_vel;
00276 }
00277 if (request.mode == TakePanoRequest::CONTINUOUS)
00278 {
00279 continuous = true;
00280 }
00281 else
00282 {
00283 continuous = false;
00284 }
00285 log("Starting panorama creation.");
00286 startPanoAction();
00287 response.status = response.STARTED;
00288 }
00289 return true;
00290 }
00291
00292 void PanoApp::takePanoCb(const std_msgs::EmptyConstPtr& msg)
00293 {
00294 if (is_active)
00295 {
00296 log("Panorama creation already in progress.");
00297 return;
00298 }
00299 if (default_mode == 0)
00300 {
00301 continuous = false;
00302 }
00303 else if (default_mode == 1)
00304 {
00305 continuous = true;
00306 }
00307 else
00308 {
00309 log("No default panorama mode set. Will use continuous mode.");
00310 continuous = true;
00311 }
00312 given_angle = default_pano_angle;
00313 snap_interval = default_snap_interval;
00314 cmd_vel.angular.z = default_rotation_velocity;
00315 log("Starting panorama creation.");
00316 startPanoAction();
00317 }
00318
00319 void PanoApp::stopPanoCb(const std_msgs::EmptyConstPtr& msg)
00320 {
00321 if (is_active)
00322 {
00323 stopPanoAction();
00324 is_active = false;
00325 log("Panorama creation stopped.");
00326 }
00327 else
00328 {
00329 log("No panorama is currently being created.");
00330 }
00331 }
00332
00333
00334
00335
00336 void PanoApp::startPanoAction()
00337 {
00338 pano_ros::PanoCaptureGoal goal;
00339 goal.bag_filename = params["bag_location"];
00340 goal.camera_topic = params["camera_name"];
00341 pano_ros_client->sendGoal(goal,
00342 boost::bind(&PanoApp::doneCb, this, _1, _2),
00343 boost::bind(&PanoApp::activeCb, this),
00344 boost::bind(&PanoApp::feedbackCb, this, _1));
00345 log("Pano ROS action goal sent.");
00346 angle = 0.0;
00347 last_angle = 0.0;
00348 }
00349
00350 void PanoApp::stopPanoAction()
00351 {
00352 pub_action_stop.publish(empty_msg);
00353 is_active = false;
00354 log("Start of stitching triggered.");
00355 }
00356
00357 void PanoApp::doneCb(const actionlib::SimpleClientGoalState& state, const pano_ros::PanoCaptureResultConstPtr& result)
00358 {
00359 std::string str = "Pano action finished in state : " + state.toString();
00360 log(str);
00361 is_active = false;
00362 }
00363
00364 void PanoApp::activeCb()
00365 {
00366 log("Pano action goal just went active.");
00367 ros::Duration(1.0).sleep();
00368 snap();
00369 go_active = true;
00370 }
00371
00372 void PanoApp::feedbackCb(const pano_ros::PanoCaptureFeedbackConstPtr& feedback)
00373 {
00374 if (go_active)
00375 {
00376 is_active = true;
00377 go_active = false;
00378 }
00379 std::stringstream ss;
00380 std::string str;
00381 if (continuous)
00382 {
00383 ss << "Got pano action feedback: " << (int)feedback->n_captures << " pictures captured.";
00384 }
00385 else
00386 {
00387 ss << "Got pano action feedback: " << (int)feedback->n_captures << " of "
00388 << int(given_angle/degrees_to_radians(snap_interval))
00389 << " pictures captured.";
00390 }
00391 log(ss.str());
00392 }
00393
00394 void PanoApp::stitchedImageCb(const sensor_msgs::ImageConstPtr& msg)
00395 {
00396 pub_stitched.publish(msg);
00397 log("Published new panorama picture.");
00398 }
00399
00400
00401
00402
00403 void PanoApp::log(std::string log)
00404 {
00405 std_msgs::String msg;
00406 msg.data = log;
00407 pub_log.publish(msg);
00408 ROS_INFO_STREAM(log);
00409 }
00410
00411 }
00412
00413 int main(int argc, char **argv)
00414 {
00415 ros::init(argc, argv, "turtlebot_panorama");
00416
00417 turtlebot_panorama::PanoApp pano;
00418 pano.log("Panorama app starting...");
00419 pano.init();
00420 pano.log("Panorama application initialised.");
00421 pano.spin();
00422 pano.log("Bye, bye!");
00423
00424 return 0;
00425 }