00001
00009
00010
00011
00012
00013
00014
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
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
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
00075 data.push_back(range);
00076 else {
00077 data[data_index] = range;
00078 if (++data_index > 49)
00079 data_index = 0;
00080 }
00081
00082 float average, variance, sum = 0, sum_ = 0;
00083
00084
00085 for (auto d : data)
00086 sum += d;
00087
00088 average = sum / data.size();
00089
00090
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
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
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;
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;
00225
00226 if (sensor->send_tf) {
00227
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
00236 tf::quaternionEigenToMsg(q, transform.transform.rotation);
00237 tf::vectorEigenToMsg(sensor->position, transform.transform.translation);
00238
00239
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);
00255 ROS_DEBUG_NAMED("distance_sensor", "DS: %d: sensor variance: %f", sensor_id, calculate_variance(msg->range) * 1E2);
00256
00257
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
00285
00286 pnh.param("subscriber", p->is_subscriber, false);
00287
00288
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;
00293 }
00294 p->sensor_id = id;
00295
00296
00297 if (!pnh.getParam("orientation", orientation_str))
00298 p->orientation = -1;
00299 else
00300
00301 p->orientation = UAS::orientation_from_str(orientation_str);
00302
00303
00304 if (!p->is_subscriber) {
00305
00306
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;
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;
00315 }
00316
00317
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;
00322 }
00323
00324
00325 pnh.param("send_tf", p->send_tf, false);
00326 if (p->send_tf) {
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
00336
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;
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;
00344 }
00345
00346
00347 pnh.param("covariance", p->covariance, 0);
00348 }
00349
00350
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 };
00359
00360 PLUGINLIB_EXPORT_CLASS(mavplugin::DistanceSensorPlugin, mavplugin::MavRosPlugin)