urg_c_wrapper.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2013, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 /* 
00031  * Author: Chad Rockey
00032  */
00033 
00034 #include <urg_node/urg_c_wrapper.h>
00035 
00036 using namespace urg_node;
00037 
00038 URGCWrapper::URGCWrapper(const std::string& ip_address, const int ip_port, bool& using_intensity, bool& using_multiecho){
00039   // Store for comprehensive diagnostics
00040   ip_address_ = ip_address;
00041   ip_port_ = ip_port;
00042   serial_port_ = "";
00043   serial_baud_ = 0;
00044 
00045 
00046   long baudrate_or_port = (long)ip_port;
00047   const char *device = ip_address.c_str();
00048 
00049   int result = urg_open(&urg_, URG_ETHERNET, device, baudrate_or_port);
00050   if (result < 0) {
00051     std::stringstream ss;
00052     ss << "Could not open network Hokuyo:\n";
00053     ss << ip_address << ":" << ip_port << "\n";
00054     ss << urg_error(&urg_);
00055     throw std::runtime_error(ss.str());
00056   }
00057 
00058   initialize(using_intensity, using_multiecho);
00059 }
00060 
00061 URGCWrapper::URGCWrapper(const int serial_baud, const std::string& serial_port, bool& using_intensity, bool& using_multiecho){
00062   // Store for comprehensive diagnostics
00063   serial_baud_ = serial_baud;
00064   serial_port_ = serial_port;
00065   ip_address_ = "";
00066   ip_port_ = 0;
00067 
00068   long baudrate_or_port = (long)serial_baud;
00069   const char *device = serial_port.c_str();
00070 
00071   int result = urg_open(&urg_, URG_SERIAL, device, baudrate_or_port);
00072   if (result < 0) {
00073     std::stringstream ss;
00074     ss << "Could not open serial Hokuyo:\n";
00075     ss << serial_port << " @ " << serial_baud << "\n";
00076     ss << urg_error(&urg_);
00077     throw std::runtime_error(ss.str());
00078   }
00079 
00080   initialize(using_intensity, using_multiecho);
00081 }
00082 
00083 void URGCWrapper::initialize(bool& using_intensity, bool& using_multiecho){
00084   int urg_data_size = urg_max_data_size(&urg_);
00085   if(urg_data_size  > 5000){  // Ocassionally urg_max_data_size returns a string pointer, make sure we don't allocate too much space, the current known max is 1440 steps
00086     urg_data_size = 5000;
00087   }
00088   data_.resize(urg_data_size * URG_MAX_ECHO);
00089   intensity_.resize(urg_data_size * URG_MAX_ECHO);
00090 
00091   started_ = false;
00092   frame_id_ = "";
00093   first_step_ = 0;
00094   last_step_ = 0;
00095   cluster_ = 1;
00096   skip_ = 0;
00097 
00098   if(using_intensity){
00099         using_intensity = isIntensitySupported();
00100   }
00101 
00102   if(using_multiecho){
00103         using_multiecho = isMultiEchoSupported();
00104   }
00105 
00106   use_intensity_ = using_intensity;
00107         use_multiecho_ = using_multiecho;
00108 
00109         measurement_type_ = URG_DISTANCE;
00110         if(use_intensity_ && use_multiecho_){
00111                 measurement_type_ = URG_MULTIECHO_INTENSITY;
00112         } else if(use_intensity_){
00113             measurement_type_ = URG_DISTANCE_INTENSITY;
00114         } else if(use_multiecho_){
00115                 measurement_type_ = URG_MULTIECHO;
00116         }
00117 }
00118 
00119 void URGCWrapper::start(){
00120         if(!started_){
00121                 int result = urg_start_measurement(&urg_, measurement_type_, 0, skip_);
00122                 if (result < 0) {
00123               std::stringstream ss;
00124               ss << "Could not start Hokuyo measurement:\n";
00125               if(use_intensity_){
00126                 ss << "With Intensity" << "\n";
00127               }
00128               if(use_multiecho_){
00129                 ss << "With MultiEcho" << "\n";
00130               }
00131               ss << urg_error(&urg_);
00132               throw std::runtime_error(ss.str());
00133             }
00134     }
00135     started_ = true;
00136 }
00137 
00138 void URGCWrapper::stop(){
00139         urg_stop_measurement(&urg_);
00140         started_ = false;
00141 }
00142 
00143 URGCWrapper::~URGCWrapper(){
00144         stop();
00145         urg_close(&urg_);
00146 }
00147 
00148 bool URGCWrapper::grabScan(const sensor_msgs::LaserScanPtr& msg){
00149   msg->header.frame_id = frame_id_;
00150   msg->angle_min = getAngleMin();
00151   msg->angle_max = getAngleMax();
00152   msg->angle_increment = getAngleIncrement();
00153   msg->scan_time = getScanPeriod();
00154   msg->time_increment = getTimeIncrement();
00155   msg->range_min = getRangeMin();
00156   msg->range_max = getRangeMax();
00157   
00158   // Grab scan
00159   int num_beams = 0;
00160   long time_stamp = 0;
00161   unsigned long long system_time_stamp = 0;
00162 
00163   if(use_intensity_){
00164         num_beams = urg_get_distance_intensity(&urg_, &data_[0], &intensity_[0], &time_stamp, &system_time_stamp);
00165   } else {
00166         num_beams = urg_get_distance(&urg_, &data_[0], &time_stamp, &system_time_stamp);
00167   } 
00168   if (num_beams <= 0) {
00169     return false;
00170   }
00171 
00172   // Fill scan
00173   msg->header.stamp.fromNSec((uint64_t)system_time_stamp);
00174   msg->header.stamp = msg->header.stamp + system_latency_ + user_latency_ + getAngularTimeOffset();
00175   msg->ranges.resize(num_beams);
00176   if(use_intensity_){
00177         msg->intensities.resize(num_beams);
00178   }
00179   
00180   for (int i = 0; i < num_beams; i++) {
00181     if(data_[(i) + 0] != 0){
00182       msg->ranges[i] = (float)data_[i]/1000.0;
00183       if(use_intensity_){
00184         msg->intensities[i] = intensity_[i];
00185           }
00186     } else {
00187       msg->ranges[i] = std::numeric_limits<float>::quiet_NaN();
00188       continue;
00189     }  
00190   }
00191   return true;
00192 }
00193 
00194 bool URGCWrapper::grabScan(const sensor_msgs::MultiEchoLaserScanPtr& msg){
00195   msg->header.frame_id = frame_id_;
00196   msg->angle_min = getAngleMin();
00197   msg->angle_max = getAngleMax();
00198   msg->angle_increment = getAngleIncrement();
00199   msg->scan_time = getScanPeriod();
00200   msg->time_increment = getTimeIncrement();
00201   msg->range_min = getRangeMin();
00202   msg->range_max = getRangeMax();
00203   
00204   // Grab scan
00205   int num_beams = 0;
00206   long time_stamp = 0;
00207   unsigned long long system_time_stamp;
00208 
00209   if(use_intensity_){
00210         num_beams = urg_get_multiecho_intensity(&urg_, &data_[0], &intensity_[0], &time_stamp, &system_time_stamp);
00211   } else {
00212         num_beams = urg_get_multiecho(&urg_, &data_[0], &time_stamp, &system_time_stamp);
00213   } 
00214   if (num_beams <= 0) {
00215     return false;
00216   }
00217 
00218   // Fill scan (uses vector.reserve wherever possible to avoid initalization and unecessary memory expansion)
00219   msg->header.stamp.fromNSec((uint64_t)system_time_stamp);
00220   msg->header.stamp = msg->header.stamp + system_latency_ + user_latency_ + getAngularTimeOffset();
00221   msg->ranges.reserve(num_beams);
00222   if(use_intensity_){
00223         msg->intensities.reserve(num_beams);
00224   } 
00225 
00226   for (size_t i = 0; i < num_beams; i++) {
00227     sensor_msgs::LaserEcho range_echo;
00228     range_echo.echoes.reserve(URG_MAX_ECHO);
00229     sensor_msgs::LaserEcho intensity_echo;
00230     if(use_intensity_){
00231       intensity_echo.echoes.reserve(URG_MAX_ECHO);
00232     }
00233     for(size_t j = 0; j < URG_MAX_ECHO; j++){
00234       if(data_[(URG_MAX_ECHO * i) + j] != 0){
00235         range_echo.echoes.push_back((float)data_[(URG_MAX_ECHO * i) + j]/1000.0f);
00236         if(use_intensity_){
00237                 intensity_echo.echoes.push_back(intensity_[(URG_MAX_ECHO * i) + j]);
00238           }
00239       } else {
00240         break;
00241       }
00242     }
00243     msg->ranges.push_back(range_echo);
00244     if(use_intensity_){
00245       msg->intensities.push_back(intensity_echo);
00246     }
00247   }
00248 
00249   return true;
00250 }
00251 
00252 bool URGCWrapper::isStarted() const{
00253   return started_;
00254 }
00255 
00256 double URGCWrapper::getRangeMin() const{
00257   long minr;
00258   long maxr;
00259   urg_distance_min_max(&urg_, &minr, &maxr);
00260   return (double)minr/1000.0;
00261 }
00262 
00263 double URGCWrapper::getRangeMax() const{
00264   long minr;
00265   long maxr;
00266   urg_distance_min_max(&urg_, &minr, &maxr);
00267   return (double)maxr/1000.0;
00268 }
00269 
00270 double URGCWrapper::getAngleMin() const{
00271   return urg_step2rad(&urg_, first_step_);
00272 }
00273 
00274 double URGCWrapper::getAngleMax() const{
00275   return urg_step2rad(&urg_, last_step_);
00276 }
00277 
00278 double URGCWrapper::getAngleMinLimit() const{
00279   int min_step;
00280   int max_step;
00281   urg_step_min_max(&urg_, &min_step, &max_step);
00282   return urg_step2rad(&urg_, min_step);
00283 }
00284 
00285 double URGCWrapper::getAngleMaxLimit() const{
00286   int min_step;
00287   int max_step;
00288   urg_step_min_max(&urg_, &min_step, &max_step);
00289   return urg_step2rad(&urg_, max_step);
00290 }
00291 
00292 double URGCWrapper::getAngleIncrement() const{
00293   double angle_min = getAngleMin();
00294   double angle_max = getAngleMax();
00295   return cluster_*(angle_max-angle_min)/(double)(last_step_-first_step_);
00296 }
00297 
00298 double URGCWrapper::getScanPeriod() const{
00299   long scan_usec = urg_scan_usec(&urg_);
00300   return 1.e-6*(double)(scan_usec);
00301 }
00302 
00303 double URGCWrapper::getTimeIncrement() const{
00304   int min_step;
00305   int max_step;
00306   urg_step_min_max(&urg_, &min_step, &max_step);
00307   double scan_period = getScanPeriod();
00308   double circle_fraction = (getAngleMaxLimit()-getAngleMinLimit())/(2.0*3.141592);
00309   return cluster_*circle_fraction*scan_period/(double)(max_step-min_step);
00310 }
00311 
00312 
00313 std::string URGCWrapper::getIPAddress() const{
00314   return ip_address_;
00315 }
00316 
00317 int URGCWrapper::getIPPort() const{
00318   return ip_port_;
00319 }
00320 
00321 std::string URGCWrapper::getSerialPort() const{
00322   return serial_port_;
00323 }
00324 
00325 int URGCWrapper::getSerialBaud() const{
00326   return serial_baud_;
00327 }
00328 
00329 std::string URGCWrapper::getVendorName(){
00330   return std::string(urg_sensor_vendor(&urg_));
00331 }
00332 
00333 std::string URGCWrapper::getProductName(){
00334   return std::string(urg_sensor_product_type(&urg_));
00335 }
00336 
00337 std::string URGCWrapper::getFirmwareVersion(){
00338   return std::string(urg_sensor_firmware_version(&urg_));
00339 }
00340 
00341 std::string URGCWrapper::getFirmwareDate(){
00342   return std::string(urg_sensor_firmware_date(&urg_));
00343 }
00344 
00345 std::string URGCWrapper::getProtocolVersion(){
00346   return std::string(urg_sensor_protocol_version(&urg_));
00347 }
00348 
00349 std::string URGCWrapper::getDeviceID(){
00350   return std::string(urg_sensor_serial_id(&urg_));
00351 }
00352 
00353 ros::Duration URGCWrapper::getComputedLatency() const{
00354   return system_latency_;
00355 }
00356 
00357 ros::Duration URGCWrapper::getUserTimeOffset() const{
00358   return user_latency_;
00359 }
00360 
00361 std::string URGCWrapper::getSensorStatus(){
00362   return std::string(urg_sensor_status(&urg_));
00363 }
00364 
00365 std::string URGCWrapper::getSensorState(){
00366   return std::string(urg_sensor_state(&urg_));
00367 }
00368 
00369 void URGCWrapper::setFrameId(const std::string& frame_id){
00370   frame_id_ = frame_id;
00371 }
00372 
00373 void URGCWrapper::setUserLatency(const double latency){
00374   user_latency_.fromSec(latency);
00375 }
00376 
00377 // Must be called before urg_start
00378 bool URGCWrapper::setAngleLimitsAndCluster(double& angle_min, double& angle_max, int cluster){
00379   if(started_){
00380         return false; // Must not be streaming
00381   }
00382 
00383   // Set step limits
00384   first_step_ = urg_rad2step(&urg_, angle_min);
00385   last_step_ = urg_rad2step(&urg_, angle_max);
00386   cluster_ = cluster;
00387 
00388   // Make sure step limits are not the same
00389   if(first_step_ == last_step_){
00390         // Make sure we're not at a limit
00391         int min_step;
00392         int max_step;
00393         urg_step_min_max(&urg_, &min_step, &max_step);
00394         if(first_step_ == min_step){ // At beginning of range
00395                 last_step_ = first_step_ + 1;
00396         } else { // At end of range (or all other cases)
00397                 first_step_ = last_step_ - 1;
00398         } 
00399   }
00400 
00401   // Make sure angle_max is greater than angle_min (should check this after end limits)
00402   if(last_step_ < first_step_){
00403         double temp = first_step_;
00404         first_step_ = last_step_;
00405         last_step_ = temp;
00406   }
00407 
00408   angle_min = urg_step2rad(&urg_, first_step_);
00409   angle_max = urg_step2rad(&urg_, last_step_);
00410   int result = urg_set_scanning_parameter(&urg_, first_step_, last_step_, cluster);
00411   if(result < 0){
00412     return false;
00413   }
00414   return true;
00415 }
00416 
00417 bool URGCWrapper::setSkip(int skip){
00418         skip_ = skip;
00419 }
00420 
00421 bool URGCWrapper::isIntensitySupported(){
00422   if(started_){
00423         return false; // Must not be streaming
00424   }
00425 
00426   urg_start_measurement(&urg_, URG_DISTANCE_INTENSITY, 0, 0);
00427   int ret = urg_get_distance_intensity(&urg_, &data_[0], &intensity_[0], NULL, NULL);
00428   if(ret <= 0){
00429         return false; // Failed to start measurement with intensity: must not support it
00430   }
00431   urg_stop_measurement(&urg_);
00432   return true;
00433 }
00434 
00435 bool URGCWrapper::isMultiEchoSupported(){
00436   if(started_){
00437         return false; // Must not be streaming
00438   }
00439 
00440   urg_start_measurement(&urg_, URG_MULTIECHO, 0, 0);
00441   int ret = urg_get_multiecho(&urg_, &data_[0], NULL, NULL);
00442   if(ret <= 0){
00443         return false; // Failed to start measurement with multiecho: must not support it
00444   }
00445   urg_stop_measurement(&urg_);
00446   return true;
00447 }
00448 
00449 ros::Duration URGCWrapper::getAngularTimeOffset() const{
00450   // Adjust value for Hokuyo's timestamps
00451   // Hokuyo's timestamps start from the rear center of the device (at Pi according to ROS standards)
00452   double circle_fraction = 0.0;
00453   if(first_step_ == 0 && last_step_ == 0){
00454     circle_fraction = (getAngleMinLimit()+3.141592)/(2.0*3.141592);
00455   } else {
00456     circle_fraction = (getAngleMin()+3.141592)/(2.0*3.141592);
00457   }
00458   return ros::Duration(circle_fraction * getScanPeriod());
00459 }
00460 
00461 ros::Duration URGCWrapper::computeLatency(size_t num_measurements){
00462   system_latency_.fromNSec(0);
00463 
00464         ros::Duration start_offset = getNativeClockOffset(1);
00465         ros::Duration previous_offset;
00466 
00467         std::vector<ros::Duration> time_offsets(num_measurements);
00468         for (size_t i = 0; i < num_measurements; i++){
00469                 ros::Duration scan_offset = getTimeStampOffset(1);
00470                 ros::Duration post_offset = getNativeClockOffset(1);
00471                 ros::Duration adjusted_scan_offset = scan_offset - start_offset;
00472                 ros::Duration adjusted_post_offset = post_offset - start_offset;
00473                 ros::Duration average_offset;
00474                 average_offset.fromSec((adjusted_post_offset.toSec() + previous_offset.toSec())/2.0);
00475 
00476                 time_offsets[i] = adjusted_scan_offset - average_offset;
00477 
00478                 previous_offset = adjusted_post_offset;
00479         }
00480 
00481   // Get median value
00482   // Sort vector using nth_element (partially sorts up to the median index)
00483   std::nth_element(time_offsets.begin(), time_offsets.begin() + time_offsets.size()/2, time_offsets.end());
00484   system_latency_ = time_offsets[time_offsets.size()/2];
00485   return system_latency_ + getAngularTimeOffset(); // Angular time offset makes the output comparable to that of hokuyo_node
00486 }
00487 
00488 ros::Duration URGCWrapper::getNativeClockOffset(size_t num_measurements){
00489   if(started_){
00490     std::stringstream ss;
00491     ss << "Cannot get native clock offset while started.";
00492     throw std::runtime_error(ss.str());
00493   }
00494 
00495   if(urg_start_time_stamp_mode(&urg_) < 0){
00496     std::stringstream ss;
00497     ss << "Cannot start time stamp mode.";
00498     throw std::runtime_error(ss.str());
00499   }
00500 
00501   std::vector<ros::Duration> time_offsets(num_measurements);
00502   for (size_t i = 0; i < num_measurements; i++)
00503   {
00504         ros::Time request_time = ros::Time::now();
00505         ros::Time laser_time;
00506         laser_time.fromNSec(1e6*(uint64_t)urg_time_stamp(&urg_)); // 1e6 * milliseconds = nanoseconds
00507         ros::Time response_time = ros::Time::now();
00508         ros::Time average_time;
00509         average_time.fromSec((response_time.toSec()+request_time.toSec())/2.0);
00510         time_offsets[i] = laser_time - average_time;
00511   }
00512 
00513   if(urg_stop_time_stamp_mode(&urg_) < 0){
00514     std::stringstream ss;
00515     ss << "Cannot stop time stamp mode.";
00516     throw std::runtime_error(ss.str());
00517   };
00518 
00519   // Return median value
00520   // Sort vector using nth_element (partially sorts up to the median index)
00521   std::nth_element(time_offsets.begin(), time_offsets.begin() + time_offsets.size()/2, time_offsets.end());
00522   return time_offsets[time_offsets.size()/2];
00523 }
00524 
00525 ros::Duration URGCWrapper::getTimeStampOffset(size_t num_measurements){
00526   if(started_){
00527     std::stringstream ss;
00528     ss << "Cannot get time stamp offset while started.";
00529     throw std::runtime_error(ss.str());
00530   }
00531 
00532   start();
00533 
00534   std::vector<ros::Duration> time_offsets(num_measurements);
00535   for(size_t i = 0; i < num_measurements; i++){
00536         long time_stamp;
00537     unsigned long long system_time_stamp;
00538         int ret = 0;
00539 
00540         if(measurement_type_ == URG_DISTANCE){
00541                 ret = urg_get_distance(&urg_, &data_[0], &time_stamp, &system_time_stamp);
00542         } else if(measurement_type_ == URG_DISTANCE_INTENSITY){
00543                 ret = urg_get_distance_intensity(&urg_, &data_[0], &intensity_[0], &time_stamp, &system_time_stamp);
00544         } else if(measurement_type_ == URG_MULTIECHO){
00545                 ret = urg_get_multiecho(&urg_, &data_[0], &time_stamp, &system_time_stamp);
00546         } else if(measurement_type_ == URG_MULTIECHO_INTENSITY){
00547                 ret = urg_get_multiecho_intensity(&urg_, &data_[0], &intensity_[0], &time_stamp, &system_time_stamp);
00548         }
00549 
00550         if(ret <= 0){
00551                 std::stringstream ss;
00552           ss << "Cannot get scan to measure time stamp offset.";
00553           throw std::runtime_error(ss.str());
00554         }
00555 
00556         ros::Time laser_timestamp;
00557         laser_timestamp.fromNSec(1e6*(uint64_t)time_stamp);
00558     ros::Time system_time;
00559     system_time.fromNSec((uint64_t)system_time_stamp);
00560 
00561         time_offsets[i] = laser_timestamp - system_time;
00562   }
00563 
00564   stop();
00565 
00566   // Return median value
00567   // Sort vector using nth_element (partially sorts up to the median index)
00568   std::nth_element(time_offsets.begin(), time_offsets.begin() + time_offsets.size()/2, time_offsets.end());
00569   return time_offsets[time_offsets.size()/2];
00570 }


urg_node
Author(s): Chad Rockey
autogenerated on Fri Aug 28 2015 13:35:14