00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "driver_base/driver.h"
00036 #include "driver_base/driver_node.h"
00037 #include <diagnostic_updater/publisher.h>
00038
00039 #include <assert.h>
00040 #include <math.h>
00041 #include <iostream>
00042 #include <sstream>
00043 #include <iomanip>
00044
00045 #include "ros/ros.h"
00046
00047 #include "sensor_msgs/LaserScan.h"
00048
00049 #include "hokuyo_node/HokuyoConfig.h"
00050
00051 #include "hokuyo/hokuyo.h"
00052
00053 using namespace std;
00054
00055 class HokuyoDriver : public driver_base::Driver
00056 {
00057 friend class HokuyoNode;
00058
00059 typedef boost::function<void(const hokuyo::LaserScan &)> UseScanFunction;
00060 UseScanFunction useScan_;
00061
00062 boost::shared_ptr<boost::thread> scan_thread_;
00063
00064 std::string device_status_;
00065 std::string device_id_;
00066 std::string last_seen_device_id_;
00067
00068 bool first_scan_;
00069
00070 std::string vendor_name_;
00071 std::string product_name_;
00072 std::string protocol_version_;
00073 std::string firmware_version_;
00074
00075 std::string connect_fail_;
00076
00077 hokuyo::LaserScan scan_;
00078 hokuyo::Laser laser_;
00079 hokuyo::LaserConfig laser_config_;
00080
00081 bool calibrated_;
00082 int lost_scan_thread_count_;
00083 int corrupted_scan_count_;
00084
00085 public:
00086 hokuyo_node::HokuyoConfig config_;
00087 typedef hokuyo_node::HokuyoConfig Config;
00088
00089 HokuyoDriver()
00090 {
00091 calibrated_ = false;
00092 lost_scan_thread_count_ = 0;
00093 corrupted_scan_count_ = 0;
00094 }
00095
00096 bool checkAngleRange(Config &conf)
00097 {
00098 bool changed = false;
00099
00100 if (conf.min_ang < laser_config_.min_angle)
00101 {
00102 changed = true;
00103 if (laser_config_.min_angle - conf.min_ang > 1e-10)
00104 {
00105 ROS_WARN("Requested angle (%f rad) out of range, using minimum scan angle supported by device: %f rad.",
00106 conf.min_ang, laser_config_.min_angle);
00107 }
00108 conf.min_ang = laser_config_.min_angle;
00109 }
00110
00111 double max_safe_angular_range_per_cluster_deg = 95;
00112 if (firmware_version_ == "1.16.01(16/Nov./2009)")
00113 max_safe_angular_range_per_cluster_deg = 190;
00114
00115 int real_cluster = conf.cluster == 0 ? 1 : conf.cluster;
00116 double max_safe_angular_range = (real_cluster * max_safe_angular_range_per_cluster_deg) * M_PI / 180;
00117
00118 if (conf.intensity && (conf.max_ang - conf.min_ang) > max_safe_angular_range + 1e-8 &&
00119 !config_.allow_unsafe_settings && laser_.getProductName() ==
00120 "SOKUIKI Sensor TOP-URG UTM-30LX")
00121 {
00122 changed = true;
00123 conf.max_ang = conf.min_ang + max_safe_angular_range;
00124 ROS_WARN("More than %f degree/cluster scan range requested on UTM-30LX firmware version %s in intensity mode with cluster=%i. The max_ang was adjusted to limit the range. You may extend the scanner's angular range using the allow_unsafe_settings option, but this may result in incorrect data or laser crashes that will require a power cycle of the laser.", max_safe_angular_range_per_cluster_deg, firmware_version_.c_str(), real_cluster);
00125 }
00126
00127 if (conf.max_ang - laser_config_.max_angle > 1e-10)
00128 {
00129 changed = true;
00130 ROS_WARN("Requested angle (%f rad) out of range, using maximum scan angle supported by device: %f rad.",
00131 conf.max_ang, laser_config_.max_angle);
00132 conf.max_ang = laser_config_.max_angle;
00133 }
00134
00135 if (conf.min_ang > conf.max_ang)
00136 {
00137 changed = true;
00138 if (conf.max_ang < laser_config_.min_angle)
00139 {
00140 if (laser_config_.min_angle - conf.max_ang > 1e-10)
00141 ROS_WARN("Requested angle (%f rad) out of range, using minimum scan angle supported by device: %f rad.",
00142 conf.max_ang, laser_config_.min_angle);
00143 conf.max_ang = laser_config_.min_angle;
00144 }
00145 ROS_WARN("Minimum angle must be greater than maximum angle. Adjusting min_ang.");
00146 conf.min_ang = conf.max_ang;
00147 }
00148
00149 return changed;
00150 }
00151
00152 bool checkIntensitySupport(Config &conf)
00153 {
00154 if (conf.intensity && !laser_.isIntensitySupported())
00155 {
00156 ROS_WARN("This unit does not appear to support intensity mode. Turning intensity off.");
00157 conf.intensity = false;
00158 return true;
00159 }
00160 return false;
00161 }
00162
00163 void doOpen()
00164 {
00165 try
00166 {
00167 std::string old_device_id = device_id_;
00168 device_id_ = "unknown";
00169 device_status_ = "unknown";
00170 first_scan_ = true;
00171
00172 laser_.open(config_.port.c_str());
00173
00174 device_id_ = getID();
00175 vendor_name_ = laser_.getVendorName();
00176 firmware_version_ = laser_.getFirmwareVersion();
00177 product_name_ = laser_.getProductName();
00178 protocol_version_ = laser_.getProtocolVersion();
00179
00180 device_status_ = laser_.getStatus();
00181 if (device_status_ != std::string("Sensor works well."))
00182 {
00183 doClose();
00184 setStatusMessagef("Laser returned abnormal status message, aborting: %s You may be able to find further information at http://www.ros.org/wiki/hokuyo_node/Troubleshooting/", device_status_.c_str());
00185 return;
00186 }
00187
00188 if (old_device_id != device_id_)
00189 {
00190 ROS_INFO("Connected to device with ID: %s", device_id_.c_str());
00191
00192 if (last_seen_device_id_ != device_id_)
00193 {
00194
00195 last_seen_device_id_ = device_id_;
00196 calibrated_ = false;
00197 }
00198
00199
00200 for (int retries = 10;; retries--)
00201 try {
00202 laser_.laserOn();
00203 break;
00204 }
00205 catch (hokuyo::Exception &e)
00206 {
00207 if (!retries)
00208 throw e;
00209 else if (retries == 10)
00210 ROS_WARN("Could not turn on laser. This may happen just after the device is plugged in. Will retry for 10 seconds.");
00211 ros::Duration(1).sleep();
00212 }
00213 }
00214 else
00215 laser_.laserOn();
00216
00217 if (config_.calibrate_time && !calibrated_)
00218 {
00219 ROS_INFO("Starting calibration. This will take up a few seconds.");
00220 double latency = laser_.calcLatency(false && config_.intensity, config_.min_ang, config_.max_ang, config_.cluster, config_.skip) * 1e-9;
00221 calibrated_ = true;
00222 ROS_INFO("Calibration finished. Latency is: %0.4f", latency);
00223 }
00224 else
00225 {
00226 calibrated_ = false;
00227 laser_.clearLatency();
00228 }
00229
00230 setStatusMessage("Device opened successfully.", true);
00231 laser_.getConfig(laser_config_);
00232
00233 state_ = OPENED;
00234 }
00235 catch (hokuyo::Exception& e)
00236 {
00237 doClose();
00238 setStatusMessagef("Exception thrown while opening Hokuyo.\n%s", e.what());
00239 return;
00240 }
00241 }
00242
00243 void doClose()
00244 {
00245 try
00246 {
00247 laser_.close();
00248 setStatusMessage("Device closed successfully.", true);
00249 } catch (hokuyo::Exception& e) {
00250 setStatusMessagef("Exception thrown while trying to close:\n%s",e.what());
00251 }
00252
00253 state_ = CLOSED;
00254 }
00255
00256 void doStart()
00257 {
00258 try
00259 {
00260 laser_.laserOn();
00261
00262 int status = laser_.requestScans(config_.intensity, config_.min_ang, config_.max_ang, config_.cluster, config_.skip);
00263
00264 if (status != 0) {
00265 setStatusMessagef("Failed to request scans from device. Status: %d.", status);
00266 corrupted_scan_count_++;
00267 return;
00268 }
00269
00270 setStatusMessagef("Waiting for first scan.", true);
00271 state_ = RUNNING;
00272 scan_thread_.reset(new boost::thread(boost::bind(&HokuyoDriver::scanThread, this)));
00273 }
00274 catch (hokuyo::Exception& e)
00275 {
00276 doClose();
00277 setStatusMessagef("Exception thrown while starting Hokuyo.\n%s", e.what());
00278 connect_fail_ = e.what();
00279 return;
00280 }
00281 }
00282
00283 void doStop()
00284 {
00285 if (state_ != RUNNING)
00286 return;
00287
00288 state_ = OPENED;
00289
00290 if (scan_thread_ && !scan_thread_->timed_join((boost::posix_time::milliseconds) 2000))
00291 {
00292 ROS_ERROR("scan_thread_ did not die after two seconds. Pretending that it did. This is probably a bad sign.");
00293 lost_scan_thread_count_++;
00294 }
00295 scan_thread_.reset();
00296
00297 setStatusMessagef("Stopped.", true);
00298 }
00299
00300 virtual std::string getID()
00301 {
00302 std::string id = laser_.getID();
00303 if (id == std::string("H0000000"))
00304 return "unknown";
00305 return id;
00306 }
00307
00308 void config_update(Config &new_config, int level = 0)
00309 {
00310 ROS_DEBUG("Reconfigure called from state %i", state_);
00311
00312 if (state_ == OPENED)
00313
00314
00315 {
00316 checkIntensitySupport(new_config);
00317 checkAngleRange(new_config);
00318 }
00319
00320 config_ = new_config;
00321 }
00322
00323 void scanThread()
00324 {
00325 while (state_ == RUNNING)
00326 {
00327 try
00328 {
00329 int status = laser_.serviceScan(scan_);
00330
00331 if(status != 0)
00332 {
00333 ROS_WARN("Error getting scan: %d", status);
00334 break;
00335 }
00336 } catch (hokuyo::CorruptedDataException &e) {
00337 ROS_WARN("Skipping corrupted data");
00338 continue;
00339 } catch (hokuyo::Exception& e) {
00340 ROS_WARN("Exception thrown while trying to get scan.\n%s", e.what());
00341 doClose();
00342 return;
00343 }
00344
00345 if (first_scan_)
00346 {
00347 first_scan_ = false;
00348 setStatusMessage("Streaming data.", true, true);
00349 ROS_INFO("Streaming data.");
00350 }
00351
00352 useScan_(scan_);
00353 }
00354
00355 try
00356 {
00357 laser_.stopScanning();
00358 } catch (hokuyo::Exception &e)
00359 {
00360 ROS_WARN("Exception thrown while trying to stop scan.\n%s", e.what());
00361 }
00362 state_ = OPENED;
00363 }
00364 };
00365
00366 class HokuyoNode : public driver_base::DriverNode<HokuyoDriver>
00367 {
00368 private:
00369 string connect_fail_;
00370
00371 double desired_freq_;
00372
00373 ros::NodeHandle node_handle_;
00374 diagnostic_updater::DiagnosedPublisher<sensor_msgs::LaserScan> scan_pub_;
00375 sensor_msgs::LaserScan scan_msg_;
00376 diagnostic_updater::FunctionDiagnosticTask hokuyo_diagnostic_task_;
00377
00378
00379 bool use_rep_117_;
00380
00381 public:
00382 HokuyoNode(ros::NodeHandle &nh) :
00383 driver_base::DriverNode<HokuyoDriver>(nh),
00384 node_handle_(nh),
00385 scan_pub_(node_handle_.advertise<sensor_msgs::LaserScan>("scan", 100),
00386 diagnostic_,
00387 diagnostic_updater::FrequencyStatusParam(&desired_freq_, &desired_freq_, 0.05),
00388 diagnostic_updater::TimeStampStatusParam()),
00389 hokuyo_diagnostic_task_("Hokuyo Diagnostics", boost::bind(&HokuyoNode::connectionStatus, this, _1))
00390 {
00391 desired_freq_ = 0;
00392 driver_.useScan_ = boost::bind(&HokuyoNode::publishScan, this, _1);
00393 driver_.setPostOpenHook(boost::bind(&HokuyoNode::postOpenHook, this));
00394
00395
00396 std::string key;
00397 if (nh.searchParam("use_rep_117", key))
00398 {
00399 nh.getParam(key, use_rep_117_);
00400 } else {
00401 ROS_WARN("The use_rep_117 parameter has not been specified and has been automatically set to true. This parameter will be removed in Hydromedusa. Please see: http://ros.org/wiki/rep_117/migration");
00402 use_rep_117_ = true;
00403 }
00404
00405 if(!use_rep_117_){
00406 ROS_WARN("The use_rep_117 parameter is set to false. This parameter will be removed in Hydromedusa. Please see: http://ros.org/wiki/rep_117/migration");
00407 }
00408 }
00409
00410 void postOpenHook()
00411 {
00412 private_node_handle_.setParam("min_ang_limit", (double) (driver_.laser_config_.min_angle));
00413 private_node_handle_.setParam("max_ang_limit", (double) (driver_.laser_config_.max_angle));
00414 private_node_handle_.setParam("min_range", (double) (driver_.laser_config_.min_range));
00415 private_node_handle_.setParam("max_range", (double) (driver_.laser_config_.max_range));
00416
00417 diagnostic_.setHardwareID(driver_.getID());
00418
00419 if (driver_.checkIntensitySupport(driver_.config_) ||
00420 driver_.checkAngleRange(driver_.config_))
00421 reconfigure_server_.updateConfig(driver_.config_);
00422
00423 scan_pub_.clear_window();
00424 }
00425
00426 virtual void addOpenedTests()
00427 {
00428 self_test_.add( "Status Test", this, &HokuyoNode::statusTest );
00429 self_test_.add( "Laser Test", this, &HokuyoNode::laserTest );
00430 self_test_.add( "Polled Data Test", this, &HokuyoNode::polledDataTest );
00431 self_test_.add( "Streamed Data Test", this, &HokuyoNode::streamedDataTest );
00432 self_test_.add( "Streamed Intensity Data Test", this, &HokuyoNode::streamedIntensityDataTest );
00433 self_test_.add( "Laser Off Test", this, &HokuyoNode::laserOffTest );
00434 }
00435
00436 virtual void addStoppedTests()
00437 {
00438 }
00439
00440 virtual void addRunningTests()
00441 {
00442 }
00443
00444 virtual void addDiagnostics()
00445 {
00446 driver_status_diagnostic_.addTask(&hokuyo_diagnostic_task_);
00447 }
00448
00449 void reconfigureHook(int level)
00450 {
00451 if (private_node_handle_.hasParam("frameid"))
00452 {
00453 ROS_WARN("~frameid is deprecated, please use ~frame_id instead");
00454 private_node_handle_.getParam("frameid", driver_.config_.frame_id);
00455 }
00456
00457 if (private_node_handle_.hasParam("min_ang_degrees"))
00458 {
00459 ROS_WARN("~min_ang_degrees is deprecated, please use ~min_ang instead");
00460 private_node_handle_.getParam("min_ang_degrees", driver_.config_.min_ang);
00461 driver_.config_.min_ang *= M_PI/180;
00462 }
00463
00464 if (private_node_handle_.hasParam("max_ang_degrees"))
00465 {
00466 ROS_WARN("~max_ang_degrees is deprecated, please use ~max_ang instead");
00467 private_node_handle_.getParam("max_ang_degrees", driver_.config_.max_ang);
00468 driver_.config_.max_ang *= M_PI/180;
00469 }
00470
00471 diagnostic_.force_update();
00472
00473 scan_pub_.clear_window();
00474 }
00475
00476 int publishScan(const hokuyo::LaserScan &scan)
00477 {
00478
00479
00480 scan_msg_.angle_min = scan.config.min_angle;
00481 scan_msg_.angle_max = scan.config.max_angle;
00482 scan_msg_.angle_increment = scan.config.ang_increment;
00483 scan_msg_.time_increment = scan.config.time_increment;
00484 scan_msg_.scan_time = scan.config.scan_time;
00485 scan_msg_.range_min = scan.config.min_range;
00486 scan_msg_.range_max = scan.config.max_range;
00487 scan_msg_.ranges = scan.ranges;
00488 scan_msg_.intensities = scan.intensities;
00489 scan_msg_.header.stamp = ros::Time().fromNSec((uint64_t)scan.system_time_stamp) + ros::Duration(driver_.config_.time_offset);
00490 scan_msg_.header.frame_id = driver_.config_.frame_id;
00491
00492 desired_freq_ = (1. / scan.config.scan_time);
00493
00494 if(!use_rep_117_){
00495 for(uint i = 0; i < scan_msg_.ranges.size(); i++){
00496 if(std::isnan(scan_msg_.ranges[i]) || std::isinf(scan_msg_.ranges[i])){
00497 scan_msg_.ranges[i] = 0;
00498 }
00499 }
00500 }
00501
00502 scan_pub_.publish(scan_msg_);
00503
00504
00505
00506 return(0);
00507 }
00508
00509 void connectionStatus(diagnostic_updater::DiagnosticStatusWrapper& status)
00510 {
00511 if (driver_.state_ == driver_.CLOSED)
00512 status.summary(2, "Not connected. " + driver_.device_status_ + " " + connect_fail_);
00513 else if (driver_.device_status_ != std::string("Sensor works well."))
00514 status.summaryf(2, "Abnormal status: %s", driver_.device_status_.c_str());
00515 else if (driver_.state_ == driver_.RUNNING)
00516 {
00517 if (driver_.first_scan_)
00518 status.summary(0, "Waiting for first scan");
00519 else
00520 status.summary(0, "Streaming");
00521 }
00522 else if (driver_.state_ == driver_.OPENED)
00523 status.summary(0, "Open");
00524 else
00525 status.summary(2, "Unknown state");
00526
00527 status.add("Device Status", driver_.device_status_);
00528 status.add("Port", driver_.config_.port);
00529 status.add("Device ID", driver_.device_id_);
00530 status.add("Scan Thread Lost Count", driver_.lost_scan_thread_count_);
00531 status.add("Corrupted Scan Count", driver_.corrupted_scan_count_);
00532 status.add("Vendor Name", driver_.vendor_name_);
00533 status.add("Product Name", driver_.product_name_);
00534 status.add("Firmware Version", driver_.firmware_version_);
00535 status.add("Protocol Version", driver_.protocol_version_);
00536 status.add("Computed Latency", driver_.laser_.getLatency());
00537 status.add("User Time Offset", driver_.config_.time_offset);
00538 }
00539
00540 void statusTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00541 {
00542 std::string stat = driver_.laser_.getStatus();
00543
00544 if (stat != std::string("Sensor works well."))
00545 {
00546 status.level = 2;
00547 } else {
00548 status.level = 0;
00549 }
00550
00551 status.message = stat;
00552 }
00553
00554 void laserTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00555 {
00556 driver_.laser_.laserOn();
00557
00558 status.level = 0;
00559 status.message = "Laser turned on successfully.";
00560 }
00561
00562 void polledDataTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00563 {
00564 hokuyo::LaserScan scan;
00565
00566 int res = driver_.laser_.pollScan(scan, driver_.laser_config_.min_angle, driver_.laser_config_.max_angle, 1, 1000);
00567
00568 if (res != 0)
00569 {
00570 status.level = 2;
00571 ostringstream oss;
00572 oss << "Hokuyo error code: " << res << ". Consult manual for meaning.";
00573 status.message = oss.str();
00574
00575 } else {
00576 status.level = 0;
00577 status.message = "Polled Hokuyo for data successfully.";
00578 }
00579 }
00580
00581 void streamedDataTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00582 {
00583 hokuyo::LaserScan scan;
00584
00585 int res = driver_.laser_.requestScans(false, driver_.laser_config_.min_angle, driver_.laser_config_.max_angle, 1, 1, 99, 1000);
00586
00587 if (res != 0)
00588 {
00589 status.level = 2;
00590 ostringstream oss;
00591 oss << "Hokuyo error code: " << res << ". Consult manual for meaning.";
00592 status.message = oss.str();
00593
00594 } else {
00595
00596 for (int i = 0; i < 99; i++)
00597 {
00598 driver_.laser_.serviceScan(scan, 1000);
00599 }
00600
00601 status.level = 0;
00602 status.message = "Streamed data from Hokuyo successfully.";
00603
00604 }
00605 }
00606
00607 void streamedIntensityDataTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00608 {
00609 hokuyo::LaserScan scan;
00610
00611 int res = driver_.laser_.requestScans(false, driver_.laser_config_.min_angle, driver_.laser_config_.max_angle, 1, 1, 99, 1000);
00612
00613 if (res != 0)
00614 {
00615 status.level = 2;
00616 ostringstream oss;
00617 oss << "Hokuyo error code: " << res << ". Consult manual for meaning.";
00618 status.message = oss.str();
00619
00620 } else {
00621
00622 int corrupted_data = 0;
00623
00624 for (int i = 0; i < 99; i++)
00625 {
00626 try {
00627 driver_.laser_.serviceScan(scan, 1000);
00628 } catch (hokuyo::CorruptedDataException &e) {
00629 corrupted_data++;
00630 }
00631 }
00632 if (corrupted_data == 1)
00633 {
00634 status.level = 1;
00635 status.message = "Single corrupted message. This is acceptable and unavoidable";
00636 } else if (corrupted_data > 1)
00637 {
00638 status.level = 2;
00639 ostringstream oss;
00640 oss << corrupted_data << " corrupted messages.";
00641 status.message = oss.str();
00642 } else
00643 {
00644 status.level = 0;
00645 status.message = "Stramed data with intensity from Hokuyo successfully.";
00646 }
00647 }
00648 }
00649
00650 void laserOffTest(diagnostic_updater::DiagnosticStatusWrapper& status)
00651 {
00652 driver_.laser_.laserOff();
00653
00654 status.level = 0;
00655 status.message = "Laser turned off successfully.";
00656 }
00657 };
00658
00659 int main(int argc, char **argv)
00660 {
00661 return driver_base::main<HokuyoNode>(argc, argv, "hokuyo_node");
00662 }
00663