mavros.cpp
Go to the documentation of this file.
1 
6 /*
7  * Copyright 2013,2014,2015,2016 Vladimir Ermakov.
8  *
9  * This file is part of the mavros package and subject to the license terms
10  * in the top-level LICENSE file of the mavros repository.
11  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
12  */
13 
14 #include <ros/console.h>
15 #include <mavros/mavros.h>
16 #include <mavros/utils.h>
17 #include <fnmatch.h>
18 
19 // MAVLINK_VERSION string
20 #include <mavlink/config.h>
21 #include <mavconn/mavlink_dialect.h>
22 
23 #include <mavconn/udp.h>
24 #include <std_msgs/String.h>
25 
26 using namespace mavros;
28 using mavconn::Framing;
29 using mavlink::mavlink_message_t;
30 using plugin::PluginBase;
31 using utils::enum_value;
32 
33 
35  mavlink_nh("mavlink"), // allow to namespace it
36  last_message_received_from_gcs(0),
37  fcu_link_diag("FCU connection"),
38  gcs_link_diag("GCS bridge"),
39  plugin_loader("mavros", "mavros::plugin::PluginBase"),
40  plugin_subscriptions{}
41 {
42  std::string fcu_url, gcs_url;
43  std::string fcu_protocol;
44  int system_id, component_id;
45  int tgt_system_id, tgt_component_id;
46  bool px4_usb_quirk;
47  double conn_timeout_d;
48  ros::V_string plugin_blacklist{}, plugin_whitelist{};
49  MAVConnInterface::Ptr fcu_link;
50 
51  ros::NodeHandle nh("~");
52 
53  nh.param<std::string>("fcu_url", fcu_url, "serial:///dev/ttyACM0");
54  nh.param<std::string>("gcs_url", gcs_url, "udp://@");
55  nh.param<bool>("gcs_quiet_mode", gcs_quiet_mode, false);
56  nh.param("conn/timeout", conn_timeout_d, 30.0);
57 
58  nh.param<std::string>("fcu_protocol", fcu_protocol, "v2.0");
59  nh.param("system_id", system_id, 1);
60  nh.param<int>("component_id", component_id, mavconn::MAV_COMP_ID_UDP_BRIDGE);
61  nh.param("target_system_id", tgt_system_id, 1);
62  nh.param("target_component_id", tgt_component_id, 1);
63  nh.param("startup_px4_usb_quirk", px4_usb_quirk, false);
64  nh.getParam("plugin_blacklist", plugin_blacklist);
65  nh.getParam("plugin_whitelist", plugin_whitelist);
66 
67  // Get frame_id
68  std::string base_link_frame_id, odom_frame_id, map_frame_id;
69  if (nh.param<std::string>("base_link_frame_id", base_link_frame_id, "base_link"))
70  ROS_INFO("Find param base_link_frame_id: %s", base_link_frame_id.c_str());
71  if (nh.param<std::string>("odom_frame_id", odom_frame_id, "odom"))
72  ROS_INFO("Find param odom_frame_id: %s", odom_frame_id.c_str());
73  if (nh.param<std::string>("map_frame_id", map_frame_id, "map"))
74  ROS_INFO("Find param map_frame_id: %s", map_frame_id.c_str());
75  mav_uas.set_base_link_frame_id(base_link_frame_id);
76  mav_uas.set_odom_frame_id(odom_frame_id);
77  mav_uas.set_map_frame_id(map_frame_id);
78  mav_uas.setup_static_tf();
79 
80  conn_timeout = ros::Duration(conn_timeout_d);
81 
82  // Now we use FCU URL as a hardware Id
83  UAS_DIAG(&mav_uas).setHardwareID(fcu_url);
84 
85  ROS_INFO_STREAM("FCU URL: " << fcu_url);
86  try {
87  fcu_link = MAVConnInterface::open_url_no_connect(fcu_url, system_id, component_id);
88  // may be overridden by URL
89  system_id = fcu_link->get_system_id();
90  component_id = fcu_link->get_component_id();
91 
92  fcu_link_diag.set_mavconn(fcu_link);
93  UAS_DIAG(&mav_uas).add(fcu_link_diag);
94  }
95  catch (mavconn::DeviceError &ex) {
96  ROS_FATAL("FCU: %s", ex.what());
97  ros::shutdown();
98  return;
99  }
100 
101  if (fcu_protocol == "v1.0") {
102  fcu_link->set_protocol_version(mavconn::Protocol::V10);
103  }
104  else if (fcu_protocol == "v2.0") {
105  fcu_link->set_protocol_version(mavconn::Protocol::V20);
106  }
107  //else if (fcu_protocol == "auto") { // XXX TODO
108  // fcu_link->set_protocol_version(mavconn::Protocol::V20);
109  //}
110  else {
111  ROS_WARN("Unknown FCU protocol: \"%s\", should be: \"v1.0\" or \"v2.0\". Used default v1.0.", fcu_protocol.c_str());
112  fcu_link->set_protocol_version(mavconn::Protocol::V10);
113  }
114 
115  if (gcs_url != "") {
116  ROS_INFO_STREAM("GCS URL: " << gcs_url);
117  try {
118  gcs_link = MAVConnInterface::open_url_no_connect(gcs_url, system_id, component_id);
119 
120  gcs_link_diag.set_mavconn(gcs_link);
121  gcs_diag_updater.setHardwareID(gcs_url);
122  gcs_diag_updater.add(gcs_link_diag);
123  }
124  catch (mavconn::DeviceError &ex) {
125  ROS_FATAL("GCS: %s", ex.what());
126  ros::shutdown();
127  return;
128  }
129  }
130  else
131  ROS_INFO("GCS bridge disabled");
132 
133  // ROS mavlink bridge
134  mavlink_pub = mavlink_nh.advertise<mavros_msgs::Mavlink>("from", 100);
135  mavlink_sub = mavlink_nh.subscribe("to", 100, &MavRos::mavlink_sub_cb, this,
137  .unreliable().maxDatagramSize(1024)
138  .reliable());
139 
140  // setup UAS and diag
141  mav_uas.set_tgt(tgt_system_id, tgt_component_id);
142  UAS_FCU(&mav_uas) = fcu_link;
143 
144  mav_uas.add_connection_change_handler(std::bind(&MavlinkDiag::set_connection_status, &fcu_link_diag, std::placeholders::_1));
145  mav_uas.add_connection_change_handler(std::bind(&MavRos::log_connect_change, this, std::placeholders::_1));
146 
147  // prepare plugin lists
148  // issue #257 2: assume that all plugins blacklisted
149  if (plugin_blacklist.empty() and !plugin_whitelist.empty())
150  plugin_blacklist.emplace_back("*");
151 
152  for (auto &name : plugin_loader.getDeclaredClasses())
153  add_plugin(name, plugin_blacklist, plugin_whitelist);
154 
155  // connect FCU link
156 
157  // XXX TODO: move workers to ROS Spinner, let mavconn threads to do only IO
158  fcu_link->connect(
159  [this](const mavlink_message_t *msg, const Framing framing) {
160  mavlink_pub_cb(msg, framing);
161  plugin_route_cb(msg, framing);
162 
163  if (gcs_link) {
164  if (this->gcs_quiet_mode && msg->msgid != mavlink::minimal::msg::HEARTBEAT::MSG_ID &&
165  (ros::Time::now() - this->last_message_received_from_gcs > this->conn_timeout)) {
166  return;
167  }
168 
169  gcs_link->send_message_ignore_drop(msg);
170  }
171  },
172  []() {
173  ROS_ERROR("FCU connection closed, mavros will be terminated.");
175  });
176 
177  if (gcs_link) {
178  // setup GCS link bridge
179  gcs_link->connect([this, fcu_link](const mavlink_message_t *msg, const Framing framing) {
180  this->last_message_received_from_gcs = ros::Time::now();
181  fcu_link->send_message_ignore_drop(msg);
182  });
183 
184  gcs_link_diag.set_connection_status(true);
185  }
186 
187  if (px4_usb_quirk)
188  startup_px4_usb_quirk();
189 
190  std::stringstream ss;
192  ss << " " << s;
193 
194  ROS_INFO("Built-in SIMD instructions: %s", Eigen::SimdInstructionSetsInUse());
195  ROS_INFO("Built-in MAVLink package version: %s", MAVLINK_VERSION);
196  ROS_INFO("Known MAVLink dialects:%s", ss.str().c_str());
197  ROS_INFO("MAVROS started. MY ID %u.%u, TARGET ID %u.%u",
198  system_id, component_id,
199  tgt_system_id, tgt_component_id);
200 }
201 
203 {
204  ros::AsyncSpinner spinner(4 /* threads */);
205 
206  auto diag_timer = mavlink_nh.createTimer(
207  ros::Duration(0.5),
208  [&](const ros::TimerEvent &) {
209  UAS_DIAG(&mav_uas).update();
210 
211  if (gcs_link)
213  });
214  diag_timer.start();
215 
216  auto remote_endpoint_timer = mavlink_nh.createTimer(
217  ros::Duration(1.0), [&](const ros::TimerEvent&) {
218  static auto pub = mavlink_nh.advertise<std_msgs::String>("gcs_ip", 1, true /* latch */);
219  static auto finished = false;
220 
221  if (finished)
222  return;
223 
224  if (const auto* p = dynamic_cast<mavconn::MAVConnUDP*>(gcs_link.get())) {
225  const auto endpoint = p->get_remote_endpoint();
226  const auto endpoint_valid = endpoint.find("255.255.255.255") == std::string::npos;
227 
228  if (endpoint_valid) {
229  std_msgs::String msg;
230  msg.data = endpoint.substr(0, endpoint.find(":"));
231  pub.publish(msg);
232  finished = true;
233  }
234  }
235  });
236  remote_endpoint_timer.start();
237 
238  spinner.start();
240 
241  ROS_INFO("Stopping mavros...");
242  spinner.stop();
243 }
244 
245 void MavRos::mavlink_pub_cb(const mavlink_message_t *mmsg, Framing framing)
246 {
247  auto rmsg = boost::make_shared<mavros_msgs::Mavlink>();
248 
249  if (mavlink_pub.getNumSubscribers() == 0)
250  return;
251 
252  rmsg->header.stamp = ros::Time::now();
253  mavros_msgs::mavlink::convert(*mmsg, *rmsg, enum_value(framing));
254  mavlink_pub.publish(rmsg);
255 }
256 
258 {
259  mavlink_message_t mmsg;
260 
261  if (mavros_msgs::mavlink::convert(*rmsg, mmsg))
262  UAS_FCU(&mav_uas)->send_message_ignore_drop(&mmsg);
263  else
264  ROS_ERROR("Drop mavlink packet: convert error.");
265 }
266 
267 void MavRos::plugin_route_cb(const mavlink_message_t *mmsg, const Framing framing)
268 {
269  auto it = plugin_subscriptions.find(mmsg->msgid);
270  if (it == plugin_subscriptions.end())
271  return;
272 
273  for (auto &info : it->second)
274  std::get<3>(info)(mmsg, framing);
275 }
276 
277 static bool pattern_match(std::string &pattern, std::string &pl_name)
278 {
279  int cmp = fnmatch(pattern.c_str(), pl_name.c_str(), FNM_CASEFOLD);
280  if (cmp == 0)
281  return true;
282  else if (cmp != FNM_NOMATCH) {
283  // never see that, i think that it is fatal error.
284  ROS_FATAL("Plugin list check error! fnmatch('%s', '%s', FNM_CASEFOLD) -> %d",
285  pattern.c_str(), pl_name.c_str(), cmp);
286  ros::shutdown();
287  }
288 
289  return false;
290 }
291 
304 static bool is_blacklisted(std::string &pl_name, ros::V_string &blacklist, ros::V_string &whitelist)
305 {
306  for (auto &bl_pattern : blacklist) {
307  if (pattern_match(bl_pattern, pl_name)) {
308  for (auto &wl_pattern : whitelist) {
309  if (pattern_match(wl_pattern, pl_name))
310  return false;
311  }
312 
313  return true;
314  }
315  }
316 
317  return false;
318 }
319 
320 inline bool is_mavlink_message_t(const size_t rt)
321 {
322  static const auto h = typeid(mavlink_message_t).hash_code();
323  return h == rt;
324 }
325 
329 void MavRos::add_plugin(std::string &pl_name, ros::V_string &blacklist, ros::V_string &whitelist)
330 {
331  if (is_blacklisted(pl_name, blacklist, whitelist)) {
332  ROS_INFO_STREAM("Plugin " << pl_name << " blacklisted");
333  return;
334  }
335 
336  try {
337  auto plugin = plugin_loader.createInstance(pl_name);
338 
339  ROS_INFO_STREAM("Plugin " << pl_name << " loaded");
340 
341  for (auto &info : plugin->get_subscriptions()) {
342  auto msgid = std::get<0>(info);
343  auto msgname = std::get<1>(info);
344  auto type_hash_ = std::get<2>(info);
345 
346  std::string log_msgname;
347 
348  if (is_mavlink_message_t(type_hash_))
349  log_msgname = utils::format("MSG-ID (%u) <%zu>", msgid, type_hash_);
350  else
351  log_msgname = utils::format("%s (%u) <%zu>", msgname, msgid, type_hash_);
352 
353  ROS_DEBUG_STREAM("Route " << log_msgname << " to " << pl_name);
354 
355  auto it = plugin_subscriptions.find(msgid);
356  if (it == plugin_subscriptions.end()) {
357  // new entry
358 
359  ROS_DEBUG_STREAM(log_msgname << " - new element");
360  plugin_subscriptions[msgid] = PluginBase::Subscriptions{{info}};
361  }
362  else {
363  // existing: check handler message type
364 
365  bool append_allowed = is_mavlink_message_t(type_hash_);
366  if (!append_allowed) {
367  append_allowed = true;
368  for (auto &e : it->second) {
369  auto t2 = std::get<2>(e);
370  if (!is_mavlink_message_t(t2) && t2 != type_hash_) {
371  ROS_ERROR_STREAM(log_msgname << " routed to different message type (hash: " << t2 << ")");
372  append_allowed = false;
373  }
374  }
375  }
376 
377  if (append_allowed) {
378  ROS_DEBUG_STREAM(log_msgname << " - emplace");
379  it->second.emplace_back(info);
380  }
381  else
382  ROS_ERROR_STREAM(log_msgname << " handler dropped because this ID are used for another message type");
383  }
384  }
385 
386  plugin->initialize(mav_uas);
387  loaded_plugins.push_back(plugin);
388 
389  ROS_INFO_STREAM("Plugin " << pl_name << " initialized");
390  } catch (pluginlib::PluginlibException &ex) {
391  ROS_ERROR_STREAM("Plugin " << pl_name << " load exception: " << ex.what());
392  }
393 }
394 
396 {
397  /* sample code from QGC */
398  const uint8_t init[] = {0x0d, 0x0d, 0x0d, 0};
399  const uint8_t nsh[] = "sh /etc/init.d/rc.usb\n";
400 
401  ROS_INFO("Autostarting mavlink via USB on PX4");
402  UAS_FCU(&mav_uas)->send_bytes(init, 3);
403  UAS_FCU(&mav_uas)->send_bytes(nsh, sizeof(nsh) - 1);
404  UAS_FCU(&mav_uas)->send_bytes(init, 4); /* NOTE in original init[3] */
405 }
406 
407 void MavRos::log_connect_change(bool connected)
408 {
410 
411  /* note: sys_status plugin required */
412  if (connected)
413  ROS_INFO("CON: Got HEARTBEAT, connected. FCU: %s", ap.c_str());
414  else
415  ROS_WARN("CON: Lost connection, HEARTBEAT timed out.");
416 }
ros::TransportHints::unreliable
TransportHints & unreliable()
mavros::MavRos::plugin_route_cb
void plugin_route_cb(const mavlink::mavlink_message_t *mmsg, const mavconn::Framing framing)
message router
Definition: mavros.cpp:267
msg
msg
mavros::MavRos::startup_px4_usb_quirk
void startup_px4_usb_quirk()
start mavlink app on USB
Definition: mavros.cpp:395
mavros::MavRos::mavlink_sub_cb
void mavlink_sub_cb(const mavros_msgs::Mavlink::ConstPtr &rmsg)
ros -> fcu link
Definition: mavros.cpp:257
format
std::string format(const std::string &fmt, Args ... args)
ROS_ERROR_STREAM
#define ROS_ERROR_STREAM(args)
mavros.h
MavRos node implementation class.
mavros::MavRos::loaded_plugins
std::vector< plugin::PluginBase::Ptr > loaded_plugins
Definition: mavros.h:58
s
XmlRpcServer s
mavconn::MAVConnUDP
mavros::MavlinkDiag::set_connection_status
void set_connection_status(bool connected)
Definition: mavlink_diag.h:34
UAS_FCU
#define UAS_FCU(uasobjptr)
helper accessor to FCU link interface
Definition: mavros_uas.h:42
mavconn::DeviceError
ros::AsyncSpinner
ros::TransportHints
ConstPtr
std::shared_ptr< MAVConnInterface const > ConstPtr
ros::shutdown
ROSCPP_DECL void shutdown()
mavros::MavRos::mavlink_nh
ros::NodeHandle mavlink_nh
Definition: mavros.h:43
MAVConnInterface
MAVConnInterface(const MAVConnInterface &)=delete
open_url_no_connect
static Ptr open_url_no_connect(std::string url, uint8_t system_id=1, uint8_t component_id=MAV_COMP_ID_UDP_BRIDGE)
ROS_DEBUG_STREAM
#define ROS_DEBUG_STREAM(args)
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
ros::requestShutdown
ROSCPP_DECL void requestShutdown()
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
pluginlib::PluginlibException
spinner
void spinner()
ros::TransportHints::reliable
TransportHints & reliable()
console.h
utils.h
some useful utils
mavconn::Protocol::V10
@ V10
mavros::MavRos::plugin_loader
pluginlib::ClassLoader< plugin::PluginBase > plugin_loader
Definition: mavros.h:57
mavconn::MAVConnInterface::get_known_dialects
static std::vector< std::string > get_known_dialects()
mavros::utils::to_string
std::string to_string(timesync_mode e)
Definition: enum_to_string.cpp:331
UAS_DIAG
#define UAS_DIAG(uasobjptr)
helper accessor to diagnostic updater
Definition: mavros_uas.h:48
mavconn::Framing
Framing
is_mavlink_message_t
bool is_mavlink_message_t(const size_t rt)
Definition: mavros.cpp:320
mavlink_pub
ros::Publisher mavlink_pub
Definition: gcs_bridge.cpp:24
mavros::UAS::get_autopilot
MAV_AUTOPILOT get_autopilot()
Returns autopilot type.
Definition: mavros_uas.h:131
mavconn::Protocol::V20
@ V20
ROS_WARN
#define ROS_WARN(...)
mavros::MavRos::log_connect_change
void log_connect_change(bool connected)
Definition: mavros.cpp:407
mavconn::MAV_COMP_ID_UDP_BRIDGE
static constexpr auto MAV_COMP_ID_UDP_BRIDGE
ros::TimerEvent
ROS_INFO_STREAM
#define ROS_INFO_STREAM(args)
Ptr
std::shared_ptr< MAVConnInterface > Ptr
mavros::MavRos::plugin_subscriptions
std::unordered_map< mavlink::msgid_t, plugin::PluginBase::Subscriptions > plugin_subscriptions
FCU link -> router -> plugin handler.
Definition: mavros.h:61
ROS_FATAL
#define ROS_FATAL(...)
udp.h
mavros::MavRos::mav_uas
UAS mav_uas
UAS object passed to all plugins.
Definition: mavros.h:64
mavros::MavRos::MavRos
MavRos()
Definition: mavros.cpp:34
mavros
Definition: frame_tf.h:34
diagnostic_updater::Updater::update
void update()
is_blacklisted
static bool is_blacklisted(std::string &pl_name, ros::V_string &blacklist, ros::V_string &whitelist)
Checks that plugin blacklisted.
Definition: mavros.cpp:304
ros::TransportHints::maxDatagramSize
TransportHints & maxDatagramSize(int size)
mavros::MavRos::mavlink_pub_cb
void mavlink_pub_cb(const mavlink::mavlink_message_t *mmsg, const mavconn::Framing framing)
fcu link -> ros
Definition: mavros.cpp:245
mavros::MavRos::spin
void spin()
Definition: mavros.cpp:202
ros::waitForShutdown
ROSCPP_DECL void waitForShutdown()
mavros::MavRos::add_plugin
void add_plugin(std::string &pl_name, ros::V_string &blacklist, ros::V_string &whitelist)
load plugin
Definition: mavros.cpp:329
mavros::plugin::PluginBase
MAVROS Plugin base class.
Definition: mavros_plugin.h:36
ROS_ERROR
#define ROS_ERROR(...)
mavlink_pub_cb
void mavlink_pub_cb(const mavlink::mavlink_message_t *mmsg, const mavconn::Framing framing)
Definition: gcs_bridge.cpp:29
init
void init(const M_string &remappings)
ros::V_string
std::vector< std::string > V_string
ros::Publisher::getNumSubscribers
uint32_t getNumSubscribers() const
mavros::MavRos::gcs_link
mavconn::MAVConnInterface::Ptr gcs_link
Definition: mavros.h:45
mavlink_sub
ros::Subscriber mavlink_sub
Definition: gcs_bridge.cpp:25
ros::Timer::start
void start()
ROS_INFO
#define ROS_INFO(...)
gcs_link
MAVConnInterface::Ptr gcs_link
Definition: gcs_bridge.cpp:26
mavros::MavRos::mavlink_pub
ros::Publisher mavlink_pub
Definition: mavros.h:50
ros::NodeHandle::createTimer
Timer createTimer(Duration period, const TimerCallback &callback, bool oneshot=false, bool autostart=true) const
ros::Duration
mavros::utils::enum_value
constexpr std::underlying_type< _T >::type enum_value(_T e)
Definition: utils.h:55
mavros::MavRos::gcs_diag_updater
diagnostic_updater::Updater gcs_diag_updater
Definition: mavros.h:53
pattern_match
static bool pattern_match(std::string &pattern, std::string &pl_name)
Definition: mavros.cpp:277
ros::NodeHandle
ros::Time::now
static Time now()


mavros
Author(s): Vladimir Ermakov
autogenerated on Tue May 6 2025 02:34:03