cob_sick_s300.cpp
Go to the documentation of this file.
00001 /****************************************************************
00002  *
00003  * Copyright (c) 2010
00004  *
00005  * Fraunhofer Institute for Manufacturing Engineering   
00006  * and Automation (IPA)
00007  *
00008  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00009  *
00010  * Project name: care-o-bot
00011  * ROS stack name: cob_driver
00012  * ROS package name: cob_sick_s300
00013  * Description:
00014  *                                                              
00015  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00016  *                      
00017  * Author: Florian Weisshardt, email:florian.weisshardt@ipa.fhg.de
00018  * Supervised by: Christian Connette, email:christian.connette@ipa.fhg.de
00019  *
00020  * Date of creation: Jan 2010
00021  * ToDo:
00022  *
00023  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00024  *
00025  * Redistribution and use in source and binary forms, with or without
00026  * modification, are permitted provided that the following conditions are met:
00027  *
00028  *       * Redistributions of source code must retain the above copyright
00029  *         notice, this list of conditions and the following disclaimer.
00030  *       * Redistributions in binary form must reproduce the above copyright
00031  *         notice, this list of conditions and the following disclaimer in the
00032  *         documentation and/or other materials provided with the distribution.
00033  *       * Neither the name of the Fraunhofer Institute for Manufacturing 
00034  *         Engineering and Automation (IPA) nor the names of its
00035  *         contributors may be used to endorse or promote products derived from
00036  *         this software without specific prior written permission.
00037  *
00038  * This program is free software: you can redistribute it and/or modify
00039  * it under the terms of the GNU Lesser General Public License LGPL as 
00040  * published by the Free Software Foundation, either version 3 of the 
00041  * License, or (at your option) any later version.
00042  * 
00043  * This program is distributed in the hope that it will be useful,
00044  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00045  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00046  * GNU Lesser General Public License LGPL for more details.
00047  * 
00048  * You should have received a copy of the GNU Lesser General Public 
00049  * License LGPL along with this program. 
00050  * If not, see <http://www.gnu.org/licenses/>.
00051  *
00052  ****************************************************************/
00053 
00054 //##################
00055 //#### includes ####
00056 
00057 // standard includes
00058 //--
00059 
00060 // ROS includes
00061 #include <ros/ros.h>
00062 
00063 // ROS message includes
00064 #include <std_msgs/Bool.h>
00065 #include <sensor_msgs/LaserScan.h>
00066 #include <diagnostic_msgs/DiagnosticArray.h>
00067 
00068 // ROS service includes
00069 //--
00070 
00071 // external includes
00072 #include <cob_sick_s300/ScannerSickS300.h>
00073 
00074 #include <boost/date_time/posix_time/posix_time.hpp>
00075 #include <boost/thread/thread.hpp>
00076 #include <boost/lexical_cast.hpp> 
00077 
00078 #define ROS_LOG_FOUND
00079 
00080 //####################
00081 //#### node class ####
00082 class NodeClass
00083 {
00084         //
00085         public:
00086                   
00087                 ros::NodeHandle nh;   
00088                 // topics to publish
00089                 ros::Publisher topicPub_LaserScan;
00090                 ros::Publisher topicPub_InStandby;
00091                 ros::Publisher topicPub_Diagnostic_;
00092                 
00093                 // topics to subscribe, callback is called for new messages arriving
00094                 //--
00095                 
00096                 // service servers
00097                 //--
00098                         
00099                 // service clients
00100                 //--
00101                 
00102                 // global variables
00103                 std::string port;
00104                 std::string node_name;
00105                 int baud, scan_id, publish_frequency;
00106                 bool inverted;
00107                 double scan_duration, scan_cycle_time;
00108                 std::string frame_id;
00109                 ros::Time syncedROSTime;
00110                 unsigned int syncedSICKStamp;
00111                 bool syncedTimeReady;
00112                 bool debug_;
00113                 ScannerSickS300 scanner_;
00114                 ros::Time loop_rate_;
00115                 std_msgs::Bool inStandby_;
00116 
00117                 // Constructor
00118                 NodeClass() 
00119                 {
00120                         // create a handle for this node, initialize node
00121                         nh = ros::NodeHandle("~");
00122                         
00123                         if(!nh.hasParam("port")) ROS_WARN("Used default parameter for port");
00124                         nh.param("port", port, std::string("/dev/ttyUSB0"));
00125                         
00126                         if(!nh.hasParam("baud")) ROS_WARN("Used default parameter for baud");
00127                         nh.param("baud", baud, 500000);
00128                         
00129                         if(!nh.hasParam("scan_id")) ROS_WARN("Used default parameter for scan_id");
00130                         nh.param("scan_id", scan_id, 7);
00131                         
00132                         if(!nh.hasParam("inverted")) ROS_WARN("Used default parameter for inverted");
00133                         nh.param("inverted", inverted, false);
00134                         
00135                         if(!nh.hasParam("frame_id")) ROS_WARN("Used default parameter for frame_id");
00136                         nh.param("frame_id", frame_id, std::string("/base_laser_link"));
00137                         
00138                         if(!nh.hasParam("scan_duration")) ROS_WARN("Used default parameter for scan_duration");
00139                         nh.param("scan_duration", scan_duration, 0.025); //no info about that in SICK-docu, but 0.025 is believable and looks good in rviz
00140                         
00141                         if(!nh.hasParam("scan_cycle_time")) ROS_WARN("Used default parameter for scan_cycle_time");
00142                         nh.param("scan_cycle_time", scan_cycle_time, 0.040); //SICK-docu says S300 scans every 40ms
00143 
00144                         if (!nh.hasParam("publish_frequency")) ROS_WARN("Used default parameter for publish_frequency");
00145                         nh.param("publish_frequency", publish_frequency, 12); //Hz
00146 
00147                         if(nh.hasParam("debug")) nh.param("debug", debug_, false);
00148 
00149                         //get params for each measurement
00150                         XmlRpc::XmlRpcValue field_params;
00151                         if(nh.getParam("fields",field_params) && field_params.getType() == XmlRpc::XmlRpcValue::TypeStruct)
00152                         {
00153                                 for(XmlRpc::XmlRpcValue::iterator field=field_params.begin(); field!=field_params.end(); field++)
00154                                 {
00155                                         int field_number = boost::lexical_cast<int>(field->first);
00156                                         ROS_DEBUG("Found field %d in params", field_number);
00157 
00158                                         if(!field->second.hasMember("scale"))
00159                                         {
00160                                                 ROS_ERROR("Missing parameter scale");
00161                                                 continue;
00162                                         }
00163                                         if(!field->second.hasMember("start_angle"))
00164                                         {
00165                                                 ROS_ERROR("Missing parameter start_angle");
00166                                                 continue;
00167                                         }
00168                                         if(!field->second.hasMember("stop_angle"))
00169                                         {
00170                                                 ROS_ERROR("Missing parameter stop_angle");
00171                                                 continue;
00172                                         }
00173 
00174                                         ScannerSickS300::ParamType param;
00175                                         param.dScale = field->second["scale"];
00176                                         param.dStartAngle = field->second["start_angle"];
00177                                         param.dStopAngle = field->second["stop_angle"];
00178                                         scanner_.setRangeField(field_number, param);
00179 
00180                                         ROS_DEBUG("params %f %f %f", param.dScale, param.dStartAngle, param.dStopAngle);
00181                                 }
00182                         }
00183                         else
00184                         {
00185                                 ROS_WARN("No params for the Sick S300 fieldset were specified --> will using default, but it's deprecated now, please adjust parameters!!!");
00186 
00187                                 //setting defaults to be backwards compatible
00188                                 ScannerSickS300::ParamType param;
00189                                 param.dScale = 0.01;
00190                                 param.dStartAngle = -135.0/180.0*M_PI;
00191                                 param.dStopAngle = 135.0/180.0*M_PI;
00192                                 scanner_.setRangeField(1, param);
00193                         }
00194 
00195                         syncedSICKStamp = 0;
00196                         syncedROSTime = ros::Time::now();
00197                         syncedTimeReady = false;
00198 
00199                         node_name = ros::this_node::getName();
00200 
00201                         // implementation of topics to publish
00202                         topicPub_LaserScan = nh.advertise<sensor_msgs::LaserScan>("scan", 1);
00203                         topicPub_InStandby = nh.advertise<std_msgs::Bool>("scan_standby", 1);
00204                         topicPub_Diagnostic_ = nh.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 1);
00205 
00206                         loop_rate_ = ros::Time::now(); // Hz
00207                 }
00208 
00209                 bool open() {
00210                         return scanner_.open(port.c_str(), baud, scan_id);
00211                 }
00212 
00213                 void receiveScan() {
00214                         std::vector< double > ranges, rangeAngles, intensities;
00215                         unsigned int iSickTimeStamp, iSickNow;
00216 
00217                         if(scanner_.getScan(ranges, rangeAngles, intensities, iSickTimeStamp, iSickNow, debug_))
00218                         {
00219                                 if(scanner_.isInStandby())
00220                                 {
00221                                         publishWarn("scanner in standby");
00222                                         ROS_WARN_THROTTLE(30, "scanner %s on port %s in standby", node_name.c_str(), port.c_str());
00223                                         publishStandby(true);
00224                                 }
00225                                 else
00226                                 {
00227                                         publishStandby(false);
00228                                         publishLaserScan(ranges, rangeAngles, intensities, iSickTimeStamp, iSickNow);
00229                                 }
00230                         }
00231                 }
00232                 
00233                 // Destructor
00234                 ~NodeClass() 
00235                 {
00236                 }
00237                 
00238                 void publishStandby(bool inStandby)
00239                 {
00240                         this->inStandby_.data = inStandby;
00241                         topicPub_InStandby.publish(this->inStandby_);
00242                 }
00243 
00244                 // other function declarations
00245                 void publishLaserScan(std::vector<double> vdDistM, std::vector<double> vdAngRAD, std::vector<double> vdIntensAU, unsigned int iSickTimeStamp, unsigned int iSickNow)
00246                 {
00247                         if(ros::Time::now()-loop_rate_.now()>=ros::Duration(1./publish_frequency))
00248                                 return;
00249                         loop_rate_ = ros::Time::now();
00250                         
00251                         // fill message
00252                         int start_scan, stop_scan;
00253                         int num_readings = vdDistM.size(); // initialize with max scan size
00254                         start_scan = 0;
00255                         stop_scan = vdDistM.size();
00256                         
00257                         // Sync handling: find out exact scan time by using the syncTime-syncStamp pair:
00258                         // Timestamp: "This counter is internally incremented at each scan, i.e. every 40 ms (S300)"
00259                         if(iSickNow != 0) {
00260                                 syncedROSTime = ros::Time::now() - ros::Duration(scan_cycle_time); 
00261                                 syncedSICKStamp = iSickNow;
00262                                 syncedTimeReady = true;
00263                                 
00264                                 ROS_DEBUG("Got iSickNow, store sync-stamp: %d", syncedSICKStamp);
00265                         } else syncedTimeReady = false;
00266                         
00267                         // create LaserScan message
00268                         sensor_msgs::LaserScan laserScan;
00269                         if(syncedTimeReady) {
00270                                 double timeDiff = (int)(iSickTimeStamp - syncedSICKStamp) * scan_cycle_time;
00271                                 laserScan.header.stamp = syncedROSTime + ros::Duration(timeDiff);
00272                                 
00273                                 ROS_DEBUG("Time::now() - calculated sick time stamp = %f",(ros::Time::now() - laserScan.header.stamp).toSec());
00274                         } else {
00275                                 laserScan.header.stamp = ros::Time::now();
00276                         }
00277                         
00278                         // fill message
00279                         laserScan.header.frame_id = frame_id;
00280                         laserScan.angle_increment = vdAngRAD[start_scan + 1] - vdAngRAD[start_scan];
00281                         laserScan.range_min = 0.001;
00282                         laserScan.range_max = 30.0;
00283                         laserScan.time_increment = (scan_duration) / (vdDistM.size());
00284 
00285                         // rescale scan
00286                         num_readings = vdDistM.size();
00287                         laserScan.angle_min = vdAngRAD[start_scan]; // first ScanAngle
00288                         laserScan.angle_max = vdAngRAD[stop_scan - 1]; // last ScanAngle
00289                         laserScan.ranges.resize(num_readings);
00290                         laserScan.intensities.resize(num_readings);
00291 
00292                         
00293                         // check for inverted laser
00294                         if(inverted) {
00295                                 // to be really accurate, we now invert time_increment
00296                                 // laserScan.header.stamp = laserScan.header.stamp + ros::Duration(scan_duration); //adding of the sum over all negative increments would be mathematically correct, but looks worse.
00297                                 laserScan.time_increment = - laserScan.time_increment;
00298                         } else {
00299                                 laserScan.header.stamp = laserScan.header.stamp - ros::Duration(scan_duration); //to be consistent with the omission of the addition above
00300                         }
00301 
00302                         for(int i = 0; i < (stop_scan - start_scan); i++)
00303                         {
00304                                 if(inverted)
00305                                 {
00306                                         laserScan.ranges[i] = vdDistM[stop_scan-1-i];
00307                                         laserScan.intensities[i] = vdIntensAU[stop_scan-1-i];
00308                                 }
00309                                 else
00310                                 {
00311                                         laserScan.ranges[i] = vdDistM[start_scan + i];
00312                                         laserScan.intensities[i] = vdIntensAU[start_scan + i];
00313                                 }
00314                         }
00315         
00316                         // publish Laserscan-message
00317                         topicPub_LaserScan.publish(laserScan);
00318                         
00319                         //Diagnostics
00320                         diagnostic_msgs::DiagnosticArray diagnostics;
00321                         diagnostics.status.resize(1);
00322                         diagnostics.status[0].level = 0;
00323                         diagnostics.status[0].name = nh.getNamespace();
00324                         diagnostics.status[0].message = "sick scanner running";
00325                         topicPub_Diagnostic_.publish(diagnostics);
00326                         }
00327 
00328                                 void publishError(std::string error_str) {
00329                                         diagnostic_msgs::DiagnosticArray diagnostics;
00330                                         diagnostics.status.resize(1);
00331                                         diagnostics.status[0].level = 2;
00332                                         diagnostics.status[0].name = nh.getNamespace();
00333                                         diagnostics.status[0].message = error_str;
00334                                         topicPub_Diagnostic_.publish(diagnostics);     
00335                                 }
00336 
00337                                 void publishWarn(std::string warn_str) {
00338                                         diagnostic_msgs::DiagnosticArray diagnostics;
00339                                         diagnostics.status.resize(1);
00340                                         diagnostics.status[0].level = 1;
00341                                         diagnostics.status[0].name = nh.getNamespace();
00342                                         diagnostics.status[0].message = warn_str;
00343                                         topicPub_Diagnostic_.publish(diagnostics);     
00344                                 }
00345 };
00346 
00347 //#######################
00348 //#### main programm ####
00349 int main(int argc, char** argv)
00350 {
00351         // initialize ROS, spezify name of node
00352         ros::init(argc, argv, "sick_s300");
00353         
00354         NodeClass nodeClass;
00355 
00356         bool bOpenScan = false;
00357         while (!bOpenScan && ros::ok()) {
00358                 ROS_INFO("Opening scanner... (port:%s)", nodeClass.port.c_str());
00359 
00360                 bOpenScan = nodeClass.open();
00361                 //bOpenScan = sickS300.open(errors, nodeClass.debug_);
00362 
00363                 // check, if it is the first try to open scanner
00364                 if (!bOpenScan) {
00365                         ROS_ERROR("...scanner not available on port %s. Will retry every second.", nodeClass.port.c_str());
00366                         nodeClass.publishError("...scanner not available on port");
00367                 }
00368                 sleep(1); // wait for scan to get ready if successfull, or wait befor retrying
00369         }
00370         ROS_INFO("...scanner opened successfully on port %s", nodeClass.port.c_str());
00371 
00372         // main loop
00373         while (ros::ok()) {
00374                 // read scan
00375                 nodeClass.receiveScan();
00376                 ros::spinOnce();
00377         }
00378         return 0;
00379 }


cob_sick_s300
Author(s): Florian Weisshardt
autogenerated on Thu Aug 27 2015 12:45:42