nodelet.cpp
Go to the documentation of this file.
00001 /*
00002 This code was developed by the National Robotics Engineering Center (NREC), part of the Robotics Institute at Carnegie Mellon University.
00003 Its development was funded by DARPA under the LS3 program and submitted for public release on June 7th, 2012.
00004 Release was granted on August, 21st 2012 with Distribution Statement "A" (Approved for Public Release, Distribution Unlimited).
00005 
00006 This software is released under a BSD license:
00007 
00008 Copyright (c) 2012, Carnegie Mellon University. All rights reserved.
00009 
00010 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
00011 
00012 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
00013 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
00014 Neither the name of the Carnegie Mellon University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
00015 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00016 */
00017 
00018 
00019 
00031 // ROS and associated nodelet interface and PLUGINLIB declaration header
00032 #include "ros/ros.h"
00033 #include <pluginlib/class_list_macros.h>
00034 #include <nodelet/nodelet.h>
00035 
00036 #include "pointgrey_camera_driver/PointGreyCamera.h" // The actual standalone library for the PointGreys
00037 
00038 #include <image_transport/image_transport.h> // ROS library that allows sending compressed images
00039 #include <camera_info_manager/camera_info_manager.h> // ROS library that publishes CameraInfo topics
00040 #include <sensor_msgs/CameraInfo.h> // ROS message header for CameraInfo
00041 
00042 #include <wfov_camera_msgs/WFOVImage.h>
00043 #include <image_exposure_msgs/ExposureSequence.h> // Message type for configuring gain and white balance.
00044 
00045 #include <diagnostic_updater/diagnostic_updater.h> // Headers for publishing diagnostic messages.
00046 #include <diagnostic_updater/publisher.h>
00047 
00048 #include <boost/thread.hpp> // Needed for the nodelet to launch the reading thread.
00049 
00050 #include <dynamic_reconfigure/server.h> // Needed for the dynamic_reconfigure gui service to run
00051 
00052 namespace pointgrey_camera_driver
00053 {
00054 
00055 class PointGreyCameraNodelet: public nodelet::Nodelet
00056 {
00057 public:
00058   PointGreyCameraNodelet() {}
00059 
00060   ~PointGreyCameraNodelet()
00061   {
00062     if(pubThread_)
00063     {
00064       pubThread_->interrupt();
00065       pubThread_->join();
00066     }
00067 
00068     try
00069     {
00070       NODELET_DEBUG("Stopping camera capture.");
00071       pg_.stop();
00072       NODELET_DEBUG("Disconnecting from camera.");
00073       pg_.disconnect();
00074     }
00075     catch(std::runtime_error& e)
00076     {
00077       NODELET_ERROR("%s", e.what());
00078     }
00079   }
00080 
00081 private:
00089   void paramCallback(pointgrey_camera_driver::PointGreyConfig &config, uint32_t level)
00090   {
00091     try
00092     {
00093       NODELET_DEBUG("Dynamic reconfigure callback with level: %d", level);
00094       pg_.setNewConfiguration(config, level);
00095 
00096       // Store needed parameters for the metadata message
00097       gain_ = config.gain;
00098       wb_blue_ = config.white_balance_blue;
00099       wb_red_ = config.white_balance_red;
00100 
00101       // Store CameraInfo binning information
00102       binning_x_ = 1;
00103       binning_y_ = 1;
00104       /*
00105       if(config.video_mode == "640x480_mono8" || config.video_mode == "format7_mode1")
00106       {
00107         binning_x_ = 2;
00108         binning_y_ = 2;
00109       }
00110       else if(config.video_mode == "format7_mode2")
00111       {
00112         binning_x_ = 0;
00113         binning_y_ = 2;
00114       }
00115       else
00116       {
00117         binning_x_ = 0;
00118         binning_y_ = 0;
00119       }
00120       */
00121 
00122       // Store CameraInfo RegionOfInterest information
00123       if(config.video_mode == "format7_mode0" || config.video_mode == "format7_mode1" || config.video_mode == "format7_mode2")
00124       {
00125         roi_x_offset_ = config.format7_x_offset;
00126         roi_y_offset_ = config.format7_y_offset;
00127         roi_width_ = config.format7_roi_width;
00128         roi_height_ = config.format7_roi_height;
00129         do_rectify_ = true; // Set to true if an ROI is used.
00130       }
00131       else
00132       {
00133         // Zeros mean the full resolution was captured.
00134         roi_x_offset_ = 0;
00135         roi_y_offset_ = 0;
00136         roi_height_ = 0;
00137         roi_width_ = 0;
00138         do_rectify_ = false; // Set to false if the whole image is captured.
00139       }
00140     }
00141     catch(std::runtime_error& e)
00142     {
00143       NODELET_ERROR("Reconfigure Callback failed with error: %s", e.what());
00144     }
00145   }
00146 
00152   void connectCb()
00153   {
00154     NODELET_DEBUG("Connect callback!");
00155     boost::mutex::scoped_lock scopedLock(connect_mutex_); // Grab the mutex.  Wait until we're done initializing before letting this function through.
00156     // Check if we should disconnect (there are 0 subscribers to our data)
00157     if(it_pub_.getNumSubscribers() == 0 && pub_->getPublisher().getNumSubscribers() == 0)
00158     {
00159       try
00160       {
00161         NODELET_DEBUG("Disconnecting.");
00162         pubThread_->interrupt();
00163         pubThread_->join();
00164         sub_.shutdown();
00165         NODELET_DEBUG("Stopping camera capture.");
00166         pg_.stop();
00167         /*NODELET_DEBUG("Disconnecting from camera.");
00168         pg_.disconnect();*/
00169       }
00170       catch(std::runtime_error& e)
00171       {
00172         NODELET_ERROR("%s", e.what());
00173       }
00174     }
00175     else if(!sub_)     // We need to connect
00176     {
00177       NODELET_DEBUG("Connecting");
00178       // Try connecting to the camera
00179       volatile bool connected = false;
00180       while(!connected && ros::ok())
00181       {
00182         try
00183         {
00184           NODELET_DEBUG("Connecting to camera.");
00185           pg_.connect(); // Probably already connected from the reconfigure thread.  This will will not throw if successfully connected.
00186           connected = true;
00187         }
00188         catch(std::runtime_error& e)
00189         {
00190           NODELET_ERROR("%s", e.what());
00191           ros::Duration(1.0).sleep(); // sleep for one second each time
00192         }
00193       }
00194 
00195       // Set the timeout for grabbing images.
00196       double timeout;
00197       getMTPrivateNodeHandle().param("timeout", timeout, 1.0);
00198       try
00199       {
00200         NODELET_DEBUG("Setting timeout to: %f.", timeout);
00201         pg_.setTimeout(timeout);
00202       }
00203       catch(std::runtime_error& e)
00204       {
00205         NODELET_ERROR("%s", e.what());
00206       }
00207 
00208       // Subscribe to gain and white balance changes
00209       sub_ = getMTNodeHandle().subscribe("image_exposure_sequence", 10, &pointgrey_camera_driver::PointGreyCameraNodelet::gainWBCallback, this);
00210 
00211       volatile bool started = false;
00212       while(!started && ros::ok())
00213       {
00214         try
00215         {
00216           NODELET_DEBUG("Starting camera capture.");
00217           pg_.start();
00218           started = true;
00219         }
00220         catch(std::runtime_error& e)
00221         {
00222           NODELET_ERROR("%s", e.what());
00223           ros::Duration(1.0).sleep(); // sleep for one second each time
00224         }
00225       }
00226 
00227       // Start the thread to loop through and publish messages
00228       pubThread_.reset(new boost::thread(boost::bind(&pointgrey_camera_driver::PointGreyCameraNodelet::devicePoll, this)));
00229     }
00230     else
00231     {
00232       NODELET_DEBUG("Do nothing in callback.");
00233     }
00234   }
00235 
00241   void onInit()
00242   {
00243         // Get nodeHandles
00244     ros::NodeHandle &nh = getMTNodeHandle();
00245     ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00246 
00247     // Get a serial number through ros
00248     int serial;
00249     pnh.param<int>("serial", serial, 0);
00250     pg_.setDesiredCamera((uint32_t)serial);
00251 
00252     // Get GigE camera parameters:
00253     pnh.param<int>("packet_size", packet_size_, 1400);
00254     pnh.param<bool>("auto_packet_size", auto_packet_size_, true);
00255     pnh.param<int>("packet_delay", packet_delay_, 4000);
00256 
00257     // Set GigE parameters:
00258     pg_.setGigEParameters(auto_packet_size_, packet_size_, packet_delay_);
00259 
00260     // Get the location of our camera config yaml
00261     std::string camera_info_url;
00262     pnh.param<std::string>("camera_info_url", camera_info_url, "");
00263     // Get the desired frame_id, set to 'camera' if not found
00264     pnh.param<std::string>("frame_id", frame_id_, "camera");
00265 
00266     // Do not call the connectCb function until after we are done initializing.
00267     boost::mutex::scoped_lock scopedLock(connect_mutex_);
00268 
00269     // Start up the dynamic_reconfigure service, note that this needs to stick around after this function ends
00270     srv_ = boost::make_shared <dynamic_reconfigure::Server<pointgrey_camera_driver::PointGreyConfig> > (pnh);
00271     dynamic_reconfigure::Server<pointgrey_camera_driver::PointGreyConfig>::CallbackType f =
00272       boost::bind(&pointgrey_camera_driver::PointGreyCameraNodelet::paramCallback, this, _1, _2);
00273     srv_->setCallback(f);
00274 
00275     // Start the camera info manager and attempt to load any configurations
00276     std::stringstream cinfo_name;
00277     cinfo_name << serial;
00278     cinfo_.reset(new camera_info_manager::CameraInfoManager(nh, cinfo_name.str(), camera_info_url));
00279 
00280     // Publish topics using ImageTransport through camera_info_manager (gives cool things like compression)
00281     it_.reset(new image_transport::ImageTransport(nh));
00282     image_transport::SubscriberStatusCallback cb = boost::bind(&PointGreyCameraNodelet::connectCb, this);
00283     it_pub_ = it_->advertiseCamera("image_raw", 5, cb, cb);
00284 
00285     // Set up diagnostics
00286     updater_.setHardwareID("pointgrey_camera " + cinfo_name.str());
00287 
00288     // Set up a diagnosed publisher
00289     double desired_freq;
00290     pnh.param<double>("desired_freq", desired_freq, 7.0);
00291     pnh.param<double>("min_freq", min_freq_, desired_freq);
00292     pnh.param<double>("max_freq", max_freq_, desired_freq);
00293     double freq_tolerance; // Tolerance before stating error on publish frequency, fractional percent of desired frequencies.
00294     pnh.param<double>("freq_tolerance", freq_tolerance, 0.1);
00295     int window_size; // Number of samples to consider in frequency
00296     pnh.param<int>("window_size", window_size, 100);
00297     double min_acceptable; // The minimum publishing delay (in seconds) before warning.  Negative values mean future dated messages.
00298     pnh.param<double>("min_acceptable_delay", min_acceptable, 0.0);
00299     double max_acceptable; // The maximum publishing delay (in seconds) before warning.
00300     pnh.param<double>("max_acceptable_delay", max_acceptable, 0.2);
00301     ros::SubscriberStatusCallback cb2 = boost::bind(&PointGreyCameraNodelet::connectCb, this);
00302     pub_.reset(new diagnostic_updater::DiagnosedPublisher<wfov_camera_msgs::WFOVImage>(nh.advertise<wfov_camera_msgs::WFOVImage>("image", 5, cb2, cb2),
00303                updater_,
00304                diagnostic_updater::FrequencyStatusParam(&min_freq_, &max_freq_, freq_tolerance, window_size),
00305                diagnostic_updater::TimeStampStatusParam(min_acceptable, max_acceptable)));
00306   }
00307 
00313   void devicePoll()
00314   {
00315     while(!boost::this_thread::interruption_requested())   // Block until we need to stop this thread.
00316     {
00317       try
00318       {
00319         wfov_camera_msgs::WFOVImagePtr wfov_image(new wfov_camera_msgs::WFOVImage);
00320         // Get the image from the camera library
00321         NODELET_DEBUG("Starting a new grab from camera.");
00322         pg_.grabImage(wfov_image->image, frame_id_);
00323 
00324         // Set other values
00325         wfov_image->header.frame_id = frame_id_;
00326 
00327         wfov_image->gain = gain_;
00328         wfov_image->white_balance_blue = wb_blue_;
00329         wfov_image->white_balance_red = wb_red_;
00330 
00331         wfov_image->temperature = pg_.getCameraTemperature();
00332 
00333         ros::Time time = ros::Time::now();
00334         wfov_image->header.stamp = time;
00335         wfov_image->image.header.stamp = time;
00336 
00337         // Set the CameraInfo message
00338         ci_.reset(new sensor_msgs::CameraInfo(cinfo_->getCameraInfo()));
00339         ci_->header.stamp = wfov_image->image.header.stamp;
00340         ci_->header.frame_id = wfov_image->header.frame_id;
00341         // The height, width, distortion model, and parameters are all filled in by camera info manager.
00342         ci_->binning_x = binning_x_;
00343         ci_->binning_y = binning_y_;
00344         ci_->roi.x_offset = roi_x_offset_;
00345         ci_->roi.y_offset = roi_y_offset_;
00346         ci_->roi.height = roi_height_;
00347         ci_->roi.width = roi_width_;
00348         ci_->roi.do_rectify = do_rectify_;
00349 
00350         wfov_image->info = *ci_;
00351 
00352         // Publish the full message
00353         pub_->publish(wfov_image);
00354 
00355         // Publish the message using standard image transport
00356         if(it_pub_.getNumSubscribers() > 0)
00357         {
00358           sensor_msgs::ImagePtr image(new sensor_msgs::Image(wfov_image->image));
00359           it_pub_.publish(image, ci_);
00360         }
00361 
00362 
00363       }
00364       catch(CameraTimeoutException& e)
00365       {
00366         NODELET_WARN("%s", e.what());
00367       }
00368       catch(std::runtime_error& e)
00369       {
00370         NODELET_ERROR("%s", e.what());
00372         /*try{
00373           // Something terrible has happened, so let's just disconnect and reconnect to see if we can recover.
00374           pg_.disconnect();
00375           ros::Duration(1.0).sleep(); // sleep for one second each time
00376           pg_.connect();
00377           pg_.start();
00378         } catch(std::runtime_error& e2){
00379           NODELET_ERROR("%s", e2.what());
00380         }*/
00381       }
00382       // Update diagnostics
00383       updater_.update();
00384     }
00385     NODELET_DEBUG("Leaving thread.");
00386   }
00387 
00388   void gainWBCallback(const image_exposure_msgs::ExposureSequence &msg)
00389   {
00390     try
00391     {
00392       NODELET_DEBUG("Gain callback:  Setting gain to %f and white balances to %u, %u", msg.gain, msg.white_balance_blue, msg.white_balance_red);
00393       gain_ = msg.gain;
00394       pg_.setGain(gain_);
00395       wb_blue_ = msg.white_balance_blue;
00396       wb_red_ = msg.white_balance_red;
00397       pg_.setBRWhiteBalance(false, wb_blue_, wb_red_);
00398     }
00399     catch(std::runtime_error& e)
00400     {
00401       NODELET_ERROR("gainWBCallback failed with error: %s", e.what());
00402     }
00403   }
00404 
00405   boost::shared_ptr<dynamic_reconfigure::Server<pointgrey_camera_driver::PointGreyConfig> > srv_; 
00406   boost::shared_ptr<image_transport::ImageTransport> it_; 
00407   boost::shared_ptr<camera_info_manager::CameraInfoManager> cinfo_; 
00408   image_transport::CameraPublisher it_pub_; 
00409   boost::shared_ptr<diagnostic_updater::DiagnosedPublisher<wfov_camera_msgs::WFOVImage> > pub_; 
00410   ros::Subscriber sub_; 
00411 
00412   boost::mutex connect_mutex_;
00413 
00414   diagnostic_updater::Updater updater_; 
00415   double min_freq_;
00416   double max_freq_;
00417 
00418   PointGreyCamera pg_; 
00419   sensor_msgs::CameraInfoPtr ci_; 
00420   std::string frame_id_; 
00421   boost::shared_ptr<boost::thread> pubThread_; 
00422 
00423   double gain_;
00424   uint16_t wb_blue_;
00425   uint16_t wb_red_;
00426 
00427   // Parameters for cameraInfo
00428   size_t binning_x_; 
00429   size_t binning_y_; 
00430   size_t roi_x_offset_; 
00431   size_t roi_y_offset_; 
00432   size_t roi_height_; 
00433   size_t roi_width_; 
00434   bool do_rectify_; 
00435 
00436   // For GigE cameras:
00438   bool auto_packet_size_;
00440   int packet_size_;
00442   int packet_delay_;
00443 };
00444 
00445 PLUGINLIB_DECLARE_CLASS(pointgrey_camera_driver, PointGreyCameraNodelet, pointgrey_camera_driver::PointGreyCameraNodelet, nodelet::Nodelet);  // Needed for Nodelet declaration
00446 }


pointgrey_camera_driver
Author(s): Chad Rockey
autogenerated on Wed Aug 26 2015 15:32:44