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
00020 #include <mrpt_bridge/mrpt_bridge.h>
00021
00022
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;
00039
00040
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;
00052 mrpt::synch::CThreadSafeVariable<CObservationIMUPtr> new_obs_imu;
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
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
00080 CObservation3DRangeScanPtr obs = CObservation3DRangeScan::Create();
00081 CObservationIMUPtr obs_imu = CObservationIMU::Create();
00082
00083
00084
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 }
00094
00095
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;
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
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
00135
00136
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
00184
00185 ros::NodeHandle node_handle;
00186 ros::Publisher laser_pub = node_handle.advertise<sensor_msgs::LaserScan>("scan", 100 );
00187
00188 ros::Rate loop_rate(200);
00189
00190
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
00205
00206 kinect_2d_scanner::TKinect2DScannerConfig config = kinect_2d_scanner::TKinect2DScannerConfig::__getDefault__();
00207
00208
00209 TGUIData gui_data(config);
00210 TThreadParam thrPar (config);
00211
00212
00213
00214 mrpt::system::TThreadHandle grab_thread_handle = mrpt::system::createThreadRef(thread_grabbing ,thrPar);
00215
00216
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
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
00241 last_obs = possiblyNewObs;
00242 last_obs_imu = thrPar.new_obs_imu.get();
00243
00244
00245 mrpt::slam::CObservation2DRangeScan scan2d;
00246
00247 last_obs->convertTo2DScan(
00248 scan2d,
00249 "KINECT_2D",
00250 DEG2RAD( config.vert_half_FOV_up ),
00251 DEG2RAD( config.vert_half_FOV_low ),
00252 config.oversampling_ratio );
00253
00254
00255 sensor_msgs::LaserScan scan_msg;
00256
00257 if (mrpt_bridge::convert(scan2d,scan_msg))
00258 {
00259
00260 laser_pub.publish(scan_msg);
00261 rate_estimate.tick();
00262 }
00263
00264
00265
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
00272 if (last_obs->hasIntensityImage)
00273 {
00274 gui_data.viewInt->setImageView(last_obs->intensityImage);
00275 do_refresh=true;
00276 }
00277
00278
00279 if (last_obs->hasPoints3D)
00280 {
00281 last_obs->project3DPointsFromDepthImageInto(*gui_data.gl_points, false );
00282 do_refresh=true;
00283 }
00284
00285
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 );
00293
00294 gui_data.win3D->unlockAccess3DScene();
00295
00296 if (do_refresh) gui_data.win3D->repaint();
00297 }
00298
00299 }
00300
00301
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
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
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
00333 gl_points = mrpt::opengl::CPointCloudColoured::Create();
00334 gl_points->setPointSize(2.5);
00335
00336
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);
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;
00346
00347 {
00348 mrpt::opengl::COpenGLScenePtr &scene = win3D->get3DSceneAndLock();
00349
00350
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;
00367 const int VW_HEIGHT = aspect_ratio*VW_WIDTH;
00368 const int VW_GAP = 30;
00369
00370
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
00384 if (config.show_GUI && !win3D) start();
00385 else if (!config.show_GUI && win3D && win3D->isOpen())
00386 {
00387
00388 win3D.clear();
00389 }
00390
00391 if (!config.show_GUI) return;
00392
00393
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 }