kinect_2d_scanner_node.cpp
Go to the documentation of this file.
00001 
00002 #include <driver_base/driver.h>
00003 #include <driver_base/driver_node.h>
00004 #include <diagnostic_updater/publisher.h>
00005 
00006 #include <assert.h>
00007 #include <math.h>
00008 #include <iostream>
00009 #include <sstream>
00010 #include <iomanip>
00011 
00012 #include <ros/ros.h>
00013 
00014 #include <sensor_msgs/LaserScan.h>
00015 #include <std_msgs/String.h>
00016 
00017 #include "kinect_2d_scanner/TKinect2DScannerConfig.h"
00018 
00019 // Conversions ROS msgs <-> MRPT structs
00020 #include <mrpt_bridge/mrpt_bridge.h>
00021 
00022 // MRPT stuff:
00023 #include <mrpt/hwdrivers/CKinect.h>
00024 #include <mrpt/utils/CTicTac.h>
00025 #include <mrpt/system/threads.h>
00026 #include <mrpt/synch.h>
00027 #include <mrpt/gui.h>
00028 #include <mrpt/opengl.h>
00029 #include <mrpt/opengl/CPlanarLaserScan.h>
00030 
00031 #include <mrpt/version.h>
00032 #if MRPT_VERSION < 0x096
00033 #       error "This program requires MRPT >= 0.9.6"
00034 #endif
00035 
00036 
00037 using namespace std;
00038 using namespace mrpt::slam;  // for mrpt::CObservation*
00039 
00040 // Thread for grabbing: Do this is another thread to exploit multicore CPUs.
00041 struct TThreadParam
00042 {
00043         typedef kinect_2d_scanner::TKinect2DScannerConfig  TConfig;
00044 
00045         TThreadParam(TConfig  &_config) : quit(false), config(_config)  { }
00046 
00047         volatile bool   quit;
00048 
00049         volatile TConfig  &config; 
00050 
00051         mrpt::synch::CThreadSafeVariable<CObservation3DRangeScanPtr> new_obs;     // RGB+D (+3D points)
00052         mrpt::synch::CThreadSafeVariable<CObservationIMUPtr>         new_obs_imu; // Accelerometers
00053 
00054 };
00055 
00056 void thread_grabbing(TThreadParam &p)
00057 {
00058         try
00059         {
00060                 mrpt::hwdrivers::CKinect  kinect;
00061 
00062                 bool there_is_obs=true, hard_error=false;
00063 
00064                 while (!hard_error && !p.quit)
00065                 {
00066                         // Is kinect already open? If not open now:
00067                         if (!kinect.isOpen())
00068                         {
00069                                 kinect.enableGrab3DPoints(true);
00070                                 kinect.enableGrabRGB(true);
00071                                 kinect.enableGrabDepth(true);
00072 
00073                                 ROS_INFO("Calling CKinect::initialize()...");
00074                                 kinect.initialize();
00075                                 ROS_INFO("Kinect sensor opened OK!");
00076                         }
00077                         else
00078                         {
00079                                 // Grab new observation from the camera:
00080                                 CObservation3DRangeScanPtr  obs     = CObservation3DRangeScan::Create(); // Smart pointers to observations
00081                                 CObservationIMUPtr          obs_imu = CObservationIMU::Create();
00082 
00083                                 // This call doesn't lock CPU: puts the thread in sleep for tiny
00084                                 // intervals until a new obs arrives or it timeouts:
00085                                 kinect.getNextObservation(*obs,*obs_imu,there_is_obs,hard_error);
00086 
00087                                 if (!hard_error && there_is_obs)
00088                                 {
00089                                         p.new_obs.set(obs);
00090                                         p.new_obs_imu.set(obs_imu);
00091                                 }
00092                         }
00093                 } // end while loop
00094 
00095                 // Kinect is closed automatically on destruction
00096         }
00097         catch(std::exception &e)
00098         {
00099                 ROS_ERROR("Unexpected exception in Kinect thread: %s", e.what());
00100                 p.quit = true;
00101         }
00102 }
00103 
00105 struct TGUIData
00106 {
00107         mrpt::gui::CDisplayWindow3DPtr  win3D;  // Optional gui:
00108 
00109         mrpt::opengl::CPointCloudColouredPtr gl_points; 
00110         mrpt::opengl::CPlanarLaserScanPtr gl_2d_scan;   
00111         mrpt::opengl::CFrustumPtr gl_frustum;
00112         mrpt::opengl::COpenGLViewportPtr  viewInt; 
00113 
00114 
00115         TGUIData(const kinect_2d_scanner::TKinect2DScannerConfig &_config)
00116         {
00117                 updateFromConfig(_config);
00118         }
00119 
00120         void updateFromConfig(const kinect_2d_scanner::TKinect2DScannerConfig &_config);
00121 
00122 private:
00123         void start();
00124 };
00125 
00126 // All callbacks requird when dynamic reconfigure sends us something:
00127 void reconfigure_all(
00128         kinect_2d_scanner::TKinect2DScannerConfig &new_config,
00129         uint32_t level,
00130         kinect_2d_scanner::TKinect2DScannerConfig *config_placeholder,
00131         TGUIData &gui_data
00132         )
00133 {
00134         //ROS_INFO("Reconfigure called at level %x.", level);
00135 
00136         // The target object was copied by reference in all other places when it's needed.
00137         *config_placeholder = new_config;
00138 
00139         gui_data.updateFromConfig( *config_placeholder );
00140 }
00141 
00142 
00144 class CSimpleRateEstimator
00145 {
00146 public:
00147         CSimpleRateEstimator(const double smooth_factor = 0.99) :
00148                 m_count(),
00149                 m_rate_est(0),
00150                 m_alpha(smooth_factor),
00151                 m_alpha_comp(1.0-smooth_factor)
00152         {
00153                 m_last_tim = m_tictac.Tac();
00154         }
00155 
00157         void tick()
00158         {
00159                 const double t = m_tictac.Tac();
00160                 const double At = std::max(1e-10, t-m_last_tim);
00161                 const double freq = 1./At;
00162                 m_rate_est = m_alpha*m_rate_est + m_alpha_comp*freq;
00163                 m_last_tim=t;
00164         }
00165 
00167         double getEstimatedRate() const { return m_rate_est; }
00168 
00169 private:
00170         int      m_count;
00171         double   m_rate_est;
00172         double   m_last_tim;
00173         double   m_alpha, m_alpha_comp;
00174         mrpt::utils::CTicTac  m_tictac;
00175 };
00176 
00177 
00179 int main(int argc, char **argv)
00180 {
00181         ros::init(argc, argv, "kinect_2d_scanner_node");
00182 
00183         // ROS comms entry point:
00184         // -----------------------------------------
00185         ros::NodeHandle node_handle;
00186         ros::Publisher laser_pub = node_handle.advertise<sensor_msgs::LaserScan>("scan", 100 /* max buffer */ );
00187 
00188         ros::Rate loop_rate(200);  // Must be >= real sensor FPS (in Kinect=30Hz)
00189 
00190         // Sensor Diagnostics:
00191         // -----------------------------------------
00192 #if 0
00193         diagnostic_updater::Updater diagnostic (...);
00194         diagnostic_updater::DiagnosedPublisher<sensor_msgs::LaserScan> scan_pub(
00195                 node_handle.advertise<sensor_msgs::LaserScan>("scan", 100),
00196         diagnostic_,
00197         diagnostic_updater::FrequencyStatusParam(&desired_freq_, &desired_freq_, 0.05),
00198         diagnostic_updater::TimeStampStatusParam())
00199         diagnostic_updater::FunctionDiagnosticTask  my_diagnostic_task(
00200                 "Kinect-to-2D Diagnostics", boost::bind(&HokuyoNode::connectionStatus, this, _1))
00201 #endif
00202 
00203 
00204         // Set of node parameters (for dynamic reconfigure):
00205         // ---------------------------------------------------
00206         kinect_2d_scanner::TKinect2DScannerConfig  config = kinect_2d_scanner::TKinect2DScannerConfig::__getDefault__();
00207 
00208         // Aux data structs:
00209         TGUIData     gui_data(config);
00210         TThreadParam thrPar  (config);
00211 
00212         // Launch grabbing thread:
00213         // ---------------------------------------------------
00214         mrpt::system::TThreadHandle grab_thread_handle = mrpt::system::createThreadRef(thread_grabbing ,thrPar);
00215 
00216         // Attach reconfigure server:
00217         // ---------------------------------------------------
00218         dynamic_reconfigure::Server<kinect_2d_scanner::TKinect2DScannerConfig> reconfigure_server( ros::NodeHandle("~") );
00219 
00220     dynamic_reconfigure::Server<kinect_2d_scanner::TKinect2DScannerConfig>::CallbackType f
00221                 = boost::bind(&reconfigure_all,_1,_2, &config,gui_data);
00222 
00223     reconfigure_server.setCallback(f);
00224 
00225 
00226         // Main loop
00227         // -------------------------------------------------------------
00228         CObservation3DRangeScanPtr  last_obs;
00229         CObservationIMUPtr          last_obs_imu;
00230 
00231         CSimpleRateEstimator        rate_estimate;
00232         int  verbose_out_count = 0;
00233 
00234         while (ros::ok() && !thrPar.quit)
00235         {
00236                 CObservation3DRangeScanPtr possiblyNewObs = thrPar.new_obs.get();
00237                 if (possiblyNewObs && possiblyNewObs->timestamp!=INVALID_TIMESTAMP &&
00238                         (!last_obs  || possiblyNewObs->timestamp!=last_obs->timestamp ) )
00239                 {
00240                         // It IS a new observation:
00241                         last_obs     = possiblyNewObs;
00242                         last_obs_imu = thrPar.new_obs_imu.get();
00243 
00244                         // Convert to equivalent 2D laser scan:
00245                         mrpt::slam::CObservation2DRangeScan scan2d;
00246 
00247                         last_obs->convertTo2DScan(
00248                                 scan2d,
00249                                 "KINECT_2D",   // MRPT's SensorLabel
00250                                 DEG2RAD( config.vert_half_FOV_up ),
00251                                 DEG2RAD( config.vert_half_FOV_low ),
00252                                 config.oversampling_ratio );
00253 
00254                         // MRPT -> ROS:
00255                         sensor_msgs::LaserScan scan_msg;
00256 
00257                         if (mrpt_bridge::convert(scan2d,scan_msg))
00258                         {
00259                                 // Publish:
00260                                 laser_pub.publish(scan_msg);
00261                                 rate_estimate.tick();
00262                         }
00263                         // else // do warning?
00264 
00265                         // other GUI stuff:
00266                         if (config.show_GUI && gui_data.win3D && gui_data.win3D->isOpen())
00267                         {
00268                                 bool do_refresh = false;
00269 
00270                                 gui_data.win3D->get3DSceneAndLock();
00271                                 // Show intensity image:
00272                                 if (last_obs->hasIntensityImage)
00273                                 {
00274                                         gui_data.viewInt->setImageView(last_obs->intensityImage); // This is not "_fast" since the intensity image is used below in the coloured point cloud.
00275                                         do_refresh=true;
00276                                 }
00277 
00278                                 // Show 3D points:
00279                                 if (last_obs->hasPoints3D)
00280                                 {
00281                                         last_obs->project3DPointsFromDepthImageInto(*gui_data.gl_points, false /* ignore pose_on_robot of the sensor */ );
00282                                         do_refresh=true;
00283                                 }
00284 
00285                                 // Set 2d scan in OpenGL object for rendering:
00286                                 gui_data.gl_2d_scan->setScan(scan2d);
00287 
00288 
00289                                 gui_data.win3D->addTextMessage(
00290                                         10,10,
00291                                         mrpt::format("Sensor rate: %.02fHz",rate_estimate.getEstimatedRate()),
00292                                         TColorf(1,1,1),"sans",10, mrpt::opengl::FILL, 101 /* unique label ID */ );
00293 
00294                                 gui_data.win3D->unlockAccess3DScene();
00295 
00296                                 if (do_refresh) gui_data.win3D->repaint();
00297                         } // end update gui
00298 
00299                 }
00300 
00301                 // Some console output to let user know I'm alive:
00302                 if (++verbose_out_count>400) {
00303                         verbose_out_count=0;
00304                         ROS_INFO("Kinect-to-2D rate: %.02fHz",rate_estimate.getEstimatedRate());
00305                 }
00306 
00307                 // End loop:
00308                 ros::spinOnce();
00309                 loop_rate.sleep();
00310         }
00311 
00312         cout << "Waiting for grabbing thread to exit...\n";
00313         thrPar.quit = true;
00314         mrpt::system::joinThread(grab_thread_handle);
00315         cout << "Bye!";
00316 
00317         return 0;
00318 }
00319 
00320 
00321 // ------------ GUI stuff ------------
00322 void TGUIData::start()
00323 {
00324         win3D = mrpt::gui::CDisplayWindow3D::Create("kinect_2d_scanner_node GUI", 640,480);
00325 
00326         win3D->setCameraAzimuthDeg(140);
00327         win3D->setCameraElevationDeg(30);
00328         win3D->setCameraZoom(10.0);
00329         win3D->setFOV(90);
00330         win3D->setCameraPointingToPoint(2.5,0,0);
00331 
00332         // The 3D point cloud OpenGL object:
00333         gl_points = mrpt::opengl::CPointCloudColoured::Create();
00334         gl_points->setPointSize(2.5);
00335 
00336         // The 2D "laser scan" OpenGL object:
00337         gl_2d_scan = mrpt::opengl::CPlanarLaserScan::Create();
00338         gl_2d_scan->enablePoints(true);
00339         gl_2d_scan->enableLine(true);
00340         gl_2d_scan->enableSurface(true);
00341         gl_2d_scan->setSurfaceColor(0,0,1, 0.3);  // RGBA
00342 
00343         gl_frustum = mrpt::opengl::CFrustum::Create(0.2f, 5.0f, 90.0f, 5.0f, 2.0f, true, true );
00344 
00345         const double aspect_ratio =  480.0 / 640.0; // kinect.getRowCount() / double( kinect.getColCount() );
00346 
00347         {
00348                 mrpt::opengl::COpenGLScenePtr &scene = win3D->get3DSceneAndLock();
00349 
00350                 // Create the Opengl object for the point cloud:
00351                 scene->insert( gl_points );
00352                 scene->insert( gl_2d_scan );
00353                 scene->insert( gl_frustum );
00354 
00355                 {
00356                         mrpt::opengl::CGridPlaneXYPtr gl_grid = mrpt::opengl::CGridPlaneXY::Create();
00357                         gl_grid->setColor(0.6,0.6,0.6);
00358                         scene->insert( gl_grid );
00359                 }
00360                 {
00361                         mrpt::opengl::CSetOfObjectsPtr gl_corner = mrpt::opengl::stock_objects::CornerXYZ();
00362                         gl_corner->setScale(0.2);
00363                         scene->insert(gl_corner);
00364                 }
00365 
00366                 const int VW_WIDTH = 250;       // Size of the viewport into the window, in pixel units.
00367                 const int VW_HEIGHT = aspect_ratio*VW_WIDTH;
00368                 const int VW_GAP = 30;
00369 
00370                 // Create the Opengl objects for the planar images, as textured planes, each in a separate viewport:
00371                 win3D->addTextMessage(30, -25-1*(VW_GAP+VW_HEIGHT),"Intensity data",TColorf(1,1,1), 2, MRPT_GLUT_BITMAP_HELVETICA_12 );
00372                 viewInt = scene->createViewport("view2d_int");
00373                 viewInt->setViewportPosition(5, -10-1*(VW_GAP+VW_HEIGHT), VW_WIDTH,VW_HEIGHT );
00374 
00375                 win3D->unlockAccess3DScene();
00376                 win3D->repaint();
00377         }
00378 
00379 }
00380 
00381 void TGUIData::updateFromConfig(const kinect_2d_scanner::TKinect2DScannerConfig &config)
00382 {
00383         // Start/stop:
00384         if (config.show_GUI && !win3D) start();
00385         else if (!config.show_GUI && win3D && win3D->isOpen())
00386         {
00387                 // Close window & destroy object.
00388                 win3D.clear();
00389         }
00390 
00391         if (!config.show_GUI) return; // Nothing else to do.
00392 
00393         // Normal update of params:
00394         // -------------------------
00395         if (gl_frustum)
00396         {
00397                 gl_frustum->setVisibility( config.show_frustum );
00398                 gl_frustum->setVertFOVAsymmetric  ( config.vert_half_FOV_low, config.vert_half_FOV_up );
00399         }
00400         if (gl_2d_scan)
00401         {
00402                 gl_2d_scan->setVisibility( config.show_2d_scan );
00403         }
00404         if (gl_points)
00405         {
00406                 gl_points->setVisibility( config.show_3d_points );
00407         }
00408 
00409         win3D->repaint();
00410 }


kinect_2d_scanner
Author(s):
autogenerated on Mon Aug 11 2014 11:23:23