distance_sensor.cpp
Go to the documentation of this file.
00001 
00009 /*
00010  * Copyright 2015 Nuno Marques.
00011  *
00012  * This file is part of the mavros package and subject to the license terms
00013  * in the top-level LICENSE file of the mavros repository.
00014  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
00015  */
00016 #include <unordered_map>
00017 #include <mavros/utils.h>
00018 #include <mavros/mavros_plugin.h>
00019 #include <pluginlib/class_list_macros.h>
00020 #include <eigen_conversions/eigen_msg.h>
00021 
00022 #include <sensor_msgs/Range.h>
00023 
00024 namespace mavplugin {
00025 class DistanceSensorPlugin;
00026 
00030 class DistanceSensorItem {
00031 public:
00032         typedef boost::shared_ptr<DistanceSensorItem> Ptr;
00033 
00034         DistanceSensorItem() :
00035                 is_subscriber(false),
00036                 send_tf(false),
00037                 sensor_id(0),
00038                 field_of_view(0),
00039                 orientation(-1),
00040                 covariance(0),
00041                 owner(nullptr),
00042                 data_index(0)
00043         { }
00044 
00045         // params
00046         bool is_subscriber;     
00047         bool send_tf;           
00048         uint8_t sensor_id;      
00049         double field_of_view;   
00050         Eigen::Vector3d position;       
00051         int orientation;        
00052         int covariance;         
00053         std::string frame_id;   
00054 
00055         // topic handle
00056         ros::Publisher pub;
00057         ros::Subscriber sub;
00058         std::string topic_name;
00059 
00060         DistanceSensorPlugin *owner;
00061 
00062         void range_cb(const sensor_msgs::Range::ConstPtr &msg);
00063         static Ptr create_item(DistanceSensorPlugin *owner, std::string topic_name);
00064 
00065 private:
00066         std::vector<float> data;        
00067         size_t data_index;              
00068 
00072         float calculate_variance(float range) {
00073                 if (data.size() < 50)
00074                         // limits the size of the array to 50 elements
00075                         data.push_back(range);
00076                 else {
00077                         data[data_index] = range;       // it starts rewriting the values from 1st element
00078                         if (++data_index > 49)
00079                                 data_index = 0;         // restarts the index when achieves the last element
00080                 }
00081 
00082                 float average, variance, sum = 0, sum_ = 0;
00083 
00084                 /*  Compute the sum of all elements */
00085                 for (auto d : data)
00086                         sum += d;
00087 
00088                 average = sum / data.size();
00089 
00090                 /*  Compute the variance */
00091                 for (auto d : data)
00092                         sum_ += (d - average) * (d - average);
00093 
00094                 variance = sum_ / data.size();
00095 
00096                 return variance;
00097         }
00098 };
00099 
00106 class DistanceSensorPlugin : public MavRosPlugin {
00107 public:
00108         DistanceSensorPlugin() :
00109                 dist_nh("~distance_sensor"),
00110                 uas(nullptr)
00111         { };
00112 
00113         void initialize(UAS &uas_)
00114         {
00115                 uas = &uas_;
00116 
00117                 XmlRpc::XmlRpcValue map_dict;
00118                 if (!dist_nh.getParam("", map_dict)) {
00119                         ROS_WARN_NAMED("distance_sensor", "DS: plugin not configured!");
00120                         return;
00121                 }
00122 
00123                 ROS_ASSERT(map_dict.getType() == XmlRpc::XmlRpcValue::TypeStruct);
00124 
00125                 for (auto &pair : map_dict) {
00126                         ROS_DEBUG_NAMED("distance_sensor", "DS: initializing mapping for %s", pair.first.c_str());
00127                         auto it = DistanceSensorItem::create_item(this, pair.first);
00128 
00129                         if (it)
00130                                 sensor_map[it->sensor_id] = it;
00131                         else
00132                                 ROS_ERROR_NAMED("distance_sensor", "DS: bad config for %s", pair.first.c_str());
00133                 }
00134         }
00135 
00136         const message_map get_rx_handlers() {
00137                 return {
00138                                MESSAGE_HANDLER(MAVLINK_MSG_ID_DISTANCE_SENSOR, &DistanceSensorPlugin::handle_distance_sensor)
00139                 };
00140         }
00141 
00142 private:
00143         friend class DistanceSensorItem;
00144 
00145         ros::NodeHandle dist_nh;
00146         UAS *uas;
00147 
00148         std::unordered_map<uint8_t, DistanceSensorItem::Ptr> sensor_map;
00149 
00150         /* -*- low-level send -*- */
00151         void distance_sensor(uint32_t time_boot_ms,
00152                         uint32_t min_distance,
00153                         uint32_t max_distance,
00154                         uint32_t current_distance,
00155                         uint8_t type, uint8_t id,
00156                         uint8_t orientation, uint8_t covariance) {
00157                 mavlink_message_t msg;
00158                 mavlink_msg_distance_sensor_pack_chan(UAS_PACK_CHAN(uas), &msg,
00159                                 time_boot_ms,
00160                                 min_distance,
00161                                 max_distance,
00162                                 current_distance,
00163                                 type,
00164                                 id,
00165                                 orientation,
00166                                 covariance);
00167                 UAS_FCU(uas)->send_message(&msg);
00168         }
00169 
00170         /* -*- mid-level helpers -*- */
00171 
00175         void handle_distance_sensor(const mavlink_message_t *msg, uint8_t sysid, uint8_t compid) {
00176                 mavlink_distance_sensor_t dist_sen;
00177                 mavlink_msg_distance_sensor_decode(msg, &dist_sen);
00178 
00179                 auto it = sensor_map.find(dist_sen.id);
00180                 if (it == sensor_map.end()) {
00181                         ROS_ERROR_NAMED("distance_sensor",
00182                                         "DS: no mapping for sensor id: %d, type: %d, orientation: %d",
00183                                         dist_sen.id, dist_sen.type, dist_sen.orientation);
00184                         return;
00185                 }
00186 
00187                 auto sensor = it->second;
00188                 if (sensor->is_subscriber) {
00189                         ROS_ERROR_NAMED("distance_sensor",
00190                                         "DS: %s (id %d) is subscriber, but i got sensor data for that id from FCU",
00191                                         sensor->topic_name.c_str(), sensor->sensor_id);
00192                         return;
00193                 }
00194 
00195                 if (sensor->orientation >= 0 && dist_sen.orientation != sensor->orientation) {
00196                         ROS_ERROR_NAMED("distance_sensor",
00197                                         "DS: %s: received sensor data has different orientation (%s) than in config (%s)!",
00198                                         sensor->topic_name.c_str(),
00199                                         UAS::str_sensor_orientation(static_cast<MAV_SENSOR_ORIENTATION>(dist_sen.orientation)).c_str(),
00200                                         UAS::str_sensor_orientation(static_cast<MAV_SENSOR_ORIENTATION>(sensor->orientation)).c_str());
00201                 }
00202 
00203                 auto range = boost::make_shared<sensor_msgs::Range>();
00204 
00205                 range->header = uas->synchronized_header(sensor->frame_id, dist_sen.time_boot_ms);
00206 
00207                 range->min_range = dist_sen.min_distance * 1E-2; // in meters
00208                 range->max_range = dist_sen.max_distance * 1E-2;
00209                 range->field_of_view = sensor->field_of_view;
00210 
00211                 if (dist_sen.type == MAV_DISTANCE_SENSOR_LASER) {
00212                         range->radiation_type = sensor_msgs::Range::INFRARED;
00213                 }
00214                 else if (dist_sen.type == MAV_DISTANCE_SENSOR_ULTRASOUND) {
00215                         range->radiation_type = sensor_msgs::Range::ULTRASOUND;
00216                 }
00217                 else {
00218                         ROS_ERROR_NAMED("distance_sensor",
00219                                         "DS: %s: Wrong/undefined type of sensor (type: %d). Droping!...",
00220                                         sensor->topic_name.c_str(), dist_sen.type);
00221                         return;
00222                 }
00223 
00224                 range->range = dist_sen.current_distance * 1E-2;        // in meters
00225 
00226                 if (sensor->send_tf) {
00227                         /* variables init */
00228                         auto q = UAS::sensor_orientation_matching(static_cast<MAV_SENSOR_ORIENTATION>(dist_sen.orientation));
00229 
00230                         geometry_msgs::TransformStamped transform;
00231 
00232                         transform.header = uas->synchronized_header("fcu", dist_sen.time_boot_ms);
00233                         transform.child_frame_id = sensor->frame_id;
00234 
00235                         /* rotation and position set */
00236                         tf::quaternionEigenToMsg(q, transform.transform.rotation);
00237                         tf::vectorEigenToMsg(sensor->position, transform.transform.translation);
00238 
00239                         /* transform broadcast */
00240                         uas->tf2_broadcaster.sendTransform(transform);
00241                 }
00242 
00243                 sensor->pub.publish(range);
00244         }
00245 };
00246 
00247 void DistanceSensorItem::range_cb(const sensor_msgs::Range::ConstPtr &msg)
00248 {
00249         uint8_t type = 0;
00250         uint8_t covariance_ = 0;
00251 
00252         if (covariance > 0) covariance_ = covariance;
00253         else covariance_ = uint8_t(calculate_variance(msg->range) * 1E2);       // in cm
00255         ROS_DEBUG_NAMED("distance_sensor", "DS: %d: sensor variance: %f", sensor_id, calculate_variance(msg->range) * 1E2);
00256 
00257         // current mapping, may change later
00258         if (msg->radiation_type == sensor_msgs::Range::INFRARED)
00259                 type = MAV_DISTANCE_SENSOR_LASER;
00260         else if (msg->radiation_type == sensor_msgs::Range::ULTRASOUND)
00261                 type = MAV_DISTANCE_SENSOR_ULTRASOUND;
00262 
00263         owner->distance_sensor(
00264                         msg->header.stamp.toNSec() / 1000000,
00265                         msg->min_range / 1E-2,
00266                         msg->max_range / 1E-2,
00267                         msg->range / 1E-2,
00268                         type,
00269                         sensor_id,
00270                         orientation,
00271                         covariance_);
00272 }
00273 
00274 DistanceSensorItem::Ptr DistanceSensorItem::create_item(DistanceSensorPlugin *owner, std::string topic_name)
00275 {
00276         auto p = boost::make_shared<DistanceSensorItem>();
00277         std::string orientation_str;
00278 
00279         ros::NodeHandle pnh(owner->dist_nh, topic_name);
00280 
00281         p->owner = owner;
00282         p->topic_name = topic_name;
00283 
00284         // load and parse params
00285         // first decide the type of topic (sub or pub)
00286         pnh.param("subscriber", p->is_subscriber, false);
00287 
00288         // sensor id
00289         int id;
00290         if (!pnh.getParam("id", id)) {
00291                 ROS_ERROR_NAMED("distance_sensor", "DS: %s: `id` not set!", topic_name.c_str());
00292                 p.reset(); return p;    // return nullptr because of a bug related to gcc 4.6
00293         }
00294         p->sensor_id = id;
00295 
00296         // orientation, checks later
00297         if (!pnh.getParam("orientation", orientation_str))
00298                 p->orientation = -1;    // not set
00299         else
00300                 // lookup for numeric value
00301                 p->orientation = UAS::orientation_from_str(orientation_str);
00302 
00303 
00304         if (!p->is_subscriber) {
00305                 // publisher params
00306                 // frame_id and FOV is required
00307                 if (!pnh.getParam("frame_id", p->frame_id)) {
00308                         ROS_ERROR_NAMED("distance_sensor", "DS: %s: `frame_id` not set!", topic_name.c_str());
00309                         p.reset(); return p;    // nullptr
00310                 }
00311 
00312                 if (!pnh.getParam("field_of_view", p->field_of_view)) {
00313                         ROS_ERROR_NAMED("distance_sensor", "DS: %s: sensor FOV not set!", topic_name.c_str());
00314                         p.reset(); return p;    // nullptr
00315                 }
00316 
00317                 // unset allowed, setted wrong - not
00318                 if (p->orientation == -1 && !orientation_str.empty()) {
00319                         ROS_ERROR_NAMED("distance_sensor", "DS: %s: defined orientation (%s) is not valid!",
00320                                         topic_name.c_str(), orientation_str.c_str());
00321                         p.reset(); return p;    // nullptr
00322                 }
00323 
00324                 // optional
00325                 pnh.param("send_tf", p->send_tf, false);
00326                 if (p->send_tf) {       // sensor position defined if 'send_tf' set to TRUE
00327                         pnh.param("sensor_position/x", p->position.x(), 0.0);
00328                         pnh.param("sensor_position/y", p->position.y(), 0.0);
00329                         pnh.param("sensor_position/z", p->position.z(), 0.0);
00330                         ROS_DEBUG_NAMED("sensor_position", "DS: %s: Sensor position at: %f, %f, %f", topic_name.c_str(),
00331                                         p->position.x(), p->position.y(), p->position.z());
00332                 }
00333         }
00334         else {
00335                 // subscriber params
00336                 // orientation is required
00337                 if (orientation_str.empty()) {
00338                         ROS_ERROR_NAMED("distance_sensor", "DS: %s: orientation not set!", topic_name.c_str());
00339                         p.reset(); return p;    // nullptr
00340                 }
00341                 else if (p->orientation == -1) {
00342                         ROS_ERROR_NAMED("distance_sensor", "DS: %s: defined orientation (%s) is not valid!", topic_name.c_str(), orientation_str.c_str());
00343                         p.reset(); return p;    // nullptr
00344                 }
00345 
00346                 // optional
00347                 pnh.param("covariance", p->covariance, 0);
00348         }
00349 
00350         // create topic handles
00351         if (!p->is_subscriber)
00352                 p->pub = owner->dist_nh.advertise<sensor_msgs::Range>(topic_name, 10);
00353         else
00354                 p->sub = owner->dist_nh.subscribe(topic_name, 10, &DistanceSensorItem::range_cb, p.get());
00355 
00356         return p;
00357 }
00358 }; // namespace mavplugin
00359 
00360 PLUGINLIB_EXPORT_CLASS(mavplugin::DistanceSensorPlugin, mavplugin::MavRosPlugin)


mavros_extras
Author(s): Vladimir Ermakov
autogenerated on Thu Feb 9 2017 04:00:23