mvsim_node.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2024 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #include <mrpt/core/lock_helper.h>
11 #include <mrpt/system/filesystem.h>
12 #include <mrpt/system/os.h> // kbhit()
13 #include <mrpt/version.h>
15 #include <mvsim/mvsim_node_core.h>
16 
17 #include "rapidxml_utils.hpp"
18 
19 #if MRPT_VERSION >= 0x020b04 // >=2.11.4?
20 #define HAVE_POINTS_XYZIRT
21 #endif
22 
23 #if defined(HAVE_POINTS_XYZIRT)
24 #include <mrpt/maps/CPointsMapXYZIRT.h>
25 #endif
26 
27 #if PACKAGE_ROS_VERSION == 1
28 // ===========================================
29 // ROS 1
30 // ===========================================
31 #include <mrpt/ros1bridge/image.h>
32 #include <mrpt/ros1bridge/imu.h>
33 #include <mrpt/ros1bridge/laser_scan.h>
34 #include <mrpt/ros1bridge/map.h>
35 #include <mrpt/ros1bridge/point_cloud2.h>
36 #include <mrpt/ros1bridge/pose.h>
37 #include <sensor_msgs/Image.h>
38 #include <sensor_msgs/Imu.h>
39 #include <sensor_msgs/LaserScan.h>
40 #include <sensor_msgs/NavSatFix.h>
41 #include <sensor_msgs/PointCloud2.h>
43 
44 // usings:
45 using ros::ok;
46 
47 using Msg_Header = std_msgs::Header;
48 
49 using Msg_Pose = geometry_msgs::Pose;
50 using Msg_TransformStamped = geometry_msgs::TransformStamped;
51 
52 using Msg_GPS = sensor_msgs::NavSatFix;
54 using Msg_Imu = sensor_msgs::Imu;
55 using Msg_LaserScan = sensor_msgs::LaserScan;
56 using Msg_PointCloud2 = sensor_msgs::PointCloud2;
57 
58 using Msg_Marker = visualization_msgs::Marker;
59 #else
60 // ===========================================
61 // ROS 2
62 // ===========================================
63 #include <mrpt/ros2bridge/image.h>
64 #include <mrpt/ros2bridge/imu.h>
65 #include <mrpt/ros2bridge/laser_scan.h>
66 #include <mrpt/ros2bridge/map.h>
67 #include <mrpt/ros2bridge/point_cloud2.h>
68 #include <mrpt/ros2bridge/pose.h>
69 
70 #include <sensor_msgs/msg/image.hpp>
71 #include <sensor_msgs/msg/imu.hpp>
72 #include <sensor_msgs/msg/laser_scan.hpp>
73 #include <sensor_msgs/msg/nav_sat_fix.hpp>
74 #include <sensor_msgs/msg/point_cloud2.hpp>
75 
76 // see: https://github.com/ros2/geometry2/pull/416
77 #if defined(MVSIM_HAS_TF2_GEOMETRY_MSGS_HPP)
78 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
79 #else
81 #endif
82 
83 #include <tf2_ros/qos.hpp> // DynamicBroadcasterQoS(), etc.
84 
85 // usings:
86 using rclcpp::ok;
87 
88 using Msg_Header = std_msgs::msg::Header;
89 
90 using Msg_Pose = geometry_msgs::msg::Pose;
91 using Msg_TransformStamped = geometry_msgs::msg::TransformStamped;
92 
93 using Msg_GPS = sensor_msgs::msg::NavSatFix;
95 using Msg_Imu = sensor_msgs::msg::Imu;
96 using Msg_LaserScan = sensor_msgs::msg::LaserScan;
97 using Msg_PointCloud2 = sensor_msgs::msg::PointCloud2;
98 
99 using Msg_Marker = visualization_msgs::msg::Marker;
100 #endif
101 
102 #if PACKAGE_ROS_VERSION == 1
103 namespace mrpt2ros = mrpt::ros1bridge;
104 #else
105 namespace mrpt2ros = mrpt::ros2bridge;
106 #endif
107 
108 #if PACKAGE_ROS_VERSION == 1
109 #define ROS12_INFO(...) ROS_INFO(__VA_ARGS__)
110 #define ROS12_WARN_THROTTLE(...) ROS_WARN_THROTTLE(__VA_ARGS__)
111 #define ROS12_WARN_STREAM_THROTTLE(...) ROS_WARN_STREAM_THROTTLE(__VA_ARGS__)
112 #define ROS12_ERROR(...) ROS_ERROR(__VA_ARGS__)
113 #else
114 #define ROS12_INFO(...) RCLCPP_INFO(n_->get_logger(), __VA_ARGS__)
115 #define ROS12_WARN_THROTTLE(...) RCLCPP_WARN_THROTTLE(n_->get_logger(), *clock_, __VA_ARGS__)
116 #define ROS12_WARN_STREAM_THROTTLE(...) \
117  RCLCPP_WARN_STREAM_THROTTLE(n_->get_logger(), *clock_, __VA_ARGS__)
118 #define ROS12_ERROR(...) RCLCPP_ERROR(n_->get_logger(), __VA_ARGS__)
119 #endif
120 
121 const double MAX_CMD_VEL_AGE_SECONDS = 1.0;
122 
123 /*------------------------------------------------------------------------------
124  * MVSimNode()
125  * Constructor.
126  *----------------------------------------------------------------------------*/
127 #if PACKAGE_ROS_VERSION == 1
129 #else
130 MVSimNode::MVSimNode(rclcpp::Node::SharedPtr& n)
131 #endif
132  : n_(n)
133 {
134  // Node parameters:
135 #if PACKAGE_ROS_VERSION == 1
136  double t;
137  if (!localn_.getParam("base_watchdog_timeout", t)) t = 0.2;
138  base_watchdog_timeout_.fromSec(t);
139  localn_.param("realtime_factor", realtime_factor_, 1.0);
140  localn_.param("gui_refresh_period", gui_refresh_period_ms_, gui_refresh_period_ms_);
141  localn_.param("headless", headless_, headless_);
142  localn_.param("period_ms_publish_tf", period_ms_publish_tf_, period_ms_publish_tf_);
143  localn_.param("do_fake_localization", do_fake_localization_, do_fake_localization_);
144  localn_.param("publish_tf_odom2baselink", publish_tf_odom2baselink_, publish_tf_odom2baselink_);
145  localn_.param(
146  "force_publish_vehicle_namespace", force_publish_vehicle_namespace_,
147  force_publish_vehicle_namespace_);
148 
149  // JLBC: At present, mvsim does not use sim_time for neither ROS 1 nor
150  // ROS 2.
151  // n_.setParam("use_sim_time", false);
152  if (true == localn_.param("use_sim_time", false))
153  {
154  THROW_EXCEPTION("At present, MVSIM can only work with use_sim_time=false");
155  }
156 #else
157  clock_ = n_->get_clock();
158  ts_.attachClock(clock_);
159 
160  // ROS2: needs to declare parameters:
161  n_->declare_parameter<std::string>("world_file", "default.world.xml");
162  n_->declare_parameter<double>("simul_rate", 100);
163  n_->declare_parameter<double>("base_watchdog_timeout", 0.2);
164  {
165  double t;
166  base_watchdog_timeout_ =
167  std::chrono::milliseconds(1000 * n_->get_parameter_or("base_watchdog_timeout", t, 0.2));
168  }
169 
170  realtime_factor_ = n_->declare_parameter<double>("realtime_factor", realtime_factor_);
171 
172  gui_refresh_period_ms_ =
173  n_->declare_parameter<double>("gui_refresh_period", gui_refresh_period_ms_);
174 
175  headless_ = n_->declare_parameter<bool>("headless", headless_);
176 
177  period_ms_publish_tf_ =
178  n_->declare_parameter<double>("period_ms_publish_tf", period_ms_publish_tf_);
179 
180  do_fake_localization_ =
181  n_->declare_parameter<bool>("do_fake_localization", do_fake_localization_);
182 
183  publish_tf_odom2baselink_ =
184  n_->declare_parameter<bool>("publish_tf_odom2baselink", publish_tf_odom2baselink_);
185 
186  publisher_history_len_ =
187  n_->declare_parameter<int>("publisher_history_len", publisher_history_len_);
188 
189  force_publish_vehicle_namespace_ = n_->declare_parameter<bool>(
190  "force_publish_vehicle_namespace", force_publish_vehicle_namespace_);
191 
192  // n_->declare_parameter("use_sim_time"); // already declared error?
193  if (true == n_->get_parameter_or("use_sim_time", false))
194  {
195  THROW_EXCEPTION("At present, MVSIM can only work with use_sim_time=false");
196  }
197 #endif
198 
199  // Launch GUI thread:
200  thread_params_.obj = this;
201  thGUI_ = std::thread(&MVSimNode::thread_update_GUI, std::ref(thread_params_));
202 
203  // Init ROS publishers:
204 #if PACKAGE_ROS_VERSION == 1
205  // pub_clock_ =
206  // mvsim_node::make_shared<ros::Publisher>(n_.advertise<rosgraph_msgs::Clock>("/clock",
207  // 10));
208 #endif
209 
210 #if PACKAGE_ROS_VERSION == 1
211  // sim_time_.fromSec(0.0);
212  base_last_cmd_.fromSec(0.0);
213 #else
214  // sim_time_ = rclcpp::Time(0);
215  base_last_cmd_ = rclcpp::Time(0);
216 #endif
217 
218  mvsim_world_->registerCallbackOnObservation(
219  [this](const mvsim::Simulable& veh, const mrpt::obs::CObservation::Ptr& obs)
220  {
221  if (!obs) return;
222 
223  mrpt::system::CTimeLoggerEntry tle(profiler_, "lambda_onNewObservation");
224 
225  const mvsim::Simulable* vehPtr = &veh;
226  const mrpt::obs::CObservation::Ptr obsCopy = obs;
227  const auto fut = ros_publisher_workers_.enqueue(
228  [this, vehPtr, obsCopy]()
229  {
230  try
231  {
232  onNewObservation(*vehPtr, obsCopy);
233  }
234  catch (const std::exception& e)
235  {
236  ROS12_ERROR(
237  "[MVSimNode] Error processing observation with "
238  "label "
239  "'%s':\n%s",
240  obsCopy ? obsCopy->sensorLabel.c_str() : "(nullptr)", e.what());
241  }
242  });
243  });
244 }
245 
247 {
248  ROS12_INFO("[MVSimNode] launch_mvsim_server()");
249 #if defined(MVSIM_HAS_ZMQ) && defined(MVSIM_HAS_PROTOBUF)
250 
251  ASSERT_(!mvsim_server_);
252 
253  // Start network server:
254  mvsim_server_ = mvsim_node::make_shared<mvsim::Server>();
255 
256  mvsim_server_->start();
257 #endif
258 }
259 
260 void MVSimNode::loadWorldModel(const std::string& world_xml_file)
261 {
262  ROS12_INFO("[MVSimNode] Loading world file: %s", world_xml_file.c_str());
263 
264  ASSERT_FILE_EXISTS_(world_xml_file);
265 
266  // Load from XML:
267  rapidxml::file<> fil_xml(world_xml_file.c_str());
268  mvsim_world_->load_from_XML(fil_xml.data(), world_xml_file);
269 
270  ROS12_INFO("[MVSimNode] World file load done.");
271  world_init_ok_ = true;
272 
273  // Notify the ROS system about the good news:
275 }
276 
277 /*------------------------------------------------------------------------------
278  * ~MVSimNode()
279  * Destructor.
280  *----------------------------------------------------------------------------*/
282 
284 {
285  if (!mvsim_world_) return;
286 
287  mvsim_world_->simulator_must_close(true);
288 
289  thread_params_.closing = true;
290  if (thGUI_.joinable()) thGUI_.join();
291 
292  mvsim_world_->free_opengl_resources();
293 
294  ros_publisher_workers_.clear();
295  // Don't destroy mvsim_server_ yet, since "world" needs to unregister.
296  mvsim_world_.reset();
297  std::cout << "[MVSimNode::terminateSimulation] All done." << std::endl;
298 }
299 
300 #if PACKAGE_ROS_VERSION == 1
301 /*------------------------------------------------------------------------------
302  * configCallback()
303  * Callback function for dynamic reconfigure server.
304  *----------------------------------------------------------------------------*/
305 void MVSimNode::configCallback(mvsim::mvsimNodeConfig& config, [[maybe_unused]] uint32_t level)
306 {
307  // Set class variables to new values. They should match what is input at the
308  // dynamic reconfigure GUI.
309  // message = config.message.c_str();
310  ROS12_INFO("MVSimNode::configCallback() called.");
311 
312  if (mvsim_world_->is_GUI_open() && !config.show_gui) mvsim_world_->close_GUI();
313 }
314 #endif
315 
316 // Process pending msgs, run real-time simulation, etc.
318 {
319  using namespace mvsim;
320  using namespace std::string_literals;
321 
322  if (!mvsim_world_) return;
323 
324  // Do simulation itself:
325  // ========================================================================
326  // Handle 1st iter:
327  if (t_old_ < 0) t_old_ = realtime_tictac_.Tac();
328  // Compute how much time has passed to simulate in real-time:
329  const double t_new = realtime_tictac_.Tac();
330  const double incr_time = realtime_factor_ * (t_new - t_old_);
331 
332  // Just in case the computer is *really fast*...
333  if (incr_time < mvsim_world_->get_simul_timestep()) return;
334 
335  // Simulate:
336  mvsim_world_->run_simulation(incr_time);
337 
338  // t_old_simul = world.get_simul_time();
339  t_old_ = t_new;
340 
341  const auto& vehs = mvsim_world_->getListOfVehicles();
342 
343  // Publish new state to ROS
344  // ========================================================================
345  this->spinNotifyROS();
346 
347  // GUI msgs, teleop, etc.
348  // ========================================================================
350  {
351  tim_teleop_refresh_.Tic();
352 
353  std::string txt2gui_tmp;
355 
356  // Global keys:
357  switch (keyevent.keycode)
358  {
359  // case 27: do_exit=true; break;
360  case '1':
361  case '2':
362  case '3':
363  case '4':
364  case '5':
365  case '6':
366  teleop_idx_veh_ = keyevent.keycode - '1';
367  break;
368  };
369 
370  { // Test: Differential drive: Control raw forces
371  txt2gui_tmp += mrpt::format(
372  "Selected vehicle: %u/%u\n", static_cast<unsigned>(teleop_idx_veh_ + 1),
373  static_cast<unsigned>(vehs.size()));
374  if (vehs.size() > teleop_idx_veh_)
375  {
376  // Get iterator to selected vehicle:
377  auto it_veh = vehs.begin();
378  std::advance(it_veh, teleop_idx_veh_);
379 
380  // Get speed: ground truth
381  txt2gui_tmp += "gt. vel: "s + it_veh->second->getVelocityLocal().asString();
382 
383  // Get speed: ground truth
384  txt2gui_tmp +=
385  "\nodo vel: "s + it_veh->second->getVelocityLocalOdoEstimate().asString();
386 
387  // Generic teleoperation interface for any controller that
388  // supports it:
389  {
390  auto* controller = it_veh->second->getControllerInterface();
393  teleop_in.keycode = keyevent.keycode;
394  teleop_in.js = mvsim_world_->getJoystickState();
395  controller->teleop_interface(teleop_in, teleop_out);
396  txt2gui_tmp += teleop_out.append_gui_lines;
397  }
398  }
399  }
400 
401  msg2gui_ = txt2gui_tmp; // send txt msgs to show in the GUI
402 
403  // Clear the keystroke buffer
404  if (keyevent.keycode != 0) gui_key_events_ = World::TGUIKeyEvent();
405 
406  } // end refresh teleop stuff
407 
408  // Check cmd_vel timeout:
409  const double rosNow = myNowSec();
410  std::set<mvsim::VehicleBase*> toRemove;
411  for (const auto& [veh, cmdVelTimestamp] : lastCmdVelTimestamp_)
412  {
413  if (rosNow - cmdVelTimestamp > MAX_CMD_VEL_AGE_SECONDS)
414  {
415  auto* controller = veh->getControllerInterface();
416 
417  controller->setTwistCommand({0, 0, 0});
418  toRemove.insert(veh);
419  }
420  }
421  for (auto* veh : toRemove)
422  {
423  lastCmdVelTimestamp_.erase(veh);
424  }
425 }
426 
427 /*------------------------------------------------------------------------------
428  * thread_update_GUI()
429  *----------------------------------------------------------------------------*/
431 {
432  try
433  {
434  MVSimNode* obj = thread_params.obj;
435 
436  while (!thread_params.closing)
437  {
438  if (obj->world_init_ok_ && !obj->headless_)
439  {
441  guiparams.msg_lines = obj->msg2gui_;
442 
443  obj->mvsim_world_->update_GUI(&guiparams);
444 
445  // Send key-strokes to the main thread:
446  if (guiparams.keyevent.keycode != 0) obj->gui_key_events_ = guiparams.keyevent;
447 
448  std::this_thread::sleep_for(std::chrono::milliseconds(obj->gui_refresh_period_ms_));
449  }
450  else if (obj->world_init_ok_ && obj->headless_)
451  {
452  obj->mvsim_world_->internalGraphicsLoopTasksForSimulation();
453 
454  std::this_thread::sleep_for(std::chrono::microseconds(
455  static_cast<size_t>(obj->mvsim_world_->get_simul_timestep() * 1000000)));
456  }
457  else
458  {
459  std::this_thread::sleep_for(std::chrono::milliseconds(obj->gui_refresh_period_ms_));
460  }
461  }
462  }
463  catch (const std::exception& e)
464  {
465  std::cerr << "[MVSimNode::thread_update_GUI] Exception:\n" << e.what();
466  }
467 }
468 
469 // Visitor: Vehicles
470 // ----------------------------------------
472 {
473  //
474 }
475 
476 // Visitor: World elements
477 // ----------------------------------------
479 {
480  // GridMaps --------------
481  if (mvsim::OccupancyGridMap* grid = dynamic_cast<mvsim::OccupancyGridMap*>(&obj); grid)
482  {
483  Msg_OccupancyGrid ros_map;
484  mrpt2ros::toROS(grid->getOccGrid(), ros_map);
485 
486 #if PACKAGE_ROS_VERSION == 1
487  static size_t loop_count = 0;
488  ros_map.header.seq = loop_count++;
489 #else
490  ros_map.header.frame_id = "map";
491 #endif
492  ros_map.header.stamp = myNow();
493 
494  worldPubs_.pub_map_ros->publish(ros_map);
495  worldPubs_.pub_map_metadata->publish(ros_map.info);
496 
497  } // end gridmap
498 
499 } // end visit(World Elements)
500 
501 // ROS: Publish grid map for visualization purposes:
503 {
504  mvsim_world_->runVisitorOnVehicles([this](mvsim::VehicleBase& v) { publishVehicles(v); });
505 
506  // Create subscribers & publishers for each vehicle's stuff:
507  // ----------------------------------------------------
508  const auto& vehs = mvsim_world_->getListOfVehicles();
509  pubsub_vehicles_.clear();
510  pubsub_vehicles_.resize(vehs.size());
511  size_t idx = 0;
512  for (auto it = vehs.begin(); it != vehs.end(); ++it, ++idx)
513  {
514  mvsim::VehicleBase* veh = dynamic_cast<mvsim::VehicleBase*>(it->second.get());
515  if (!veh) continue;
516 
517  auto& pubsubs = pubsub_vehicles_[idx];
518 
519  initPubSubs(pubsubs, veh);
520  }
521 
522 #if PACKAGE_ROS_VERSION == 1
523  // pub: simul_map, simul_map_metadata
524  worldPubs_.pub_map_ros = mvsim_node::make_shared<ros::Publisher>(
525  n_.advertise<Msg_OccupancyGrid>("simul_map", 1 /*queue len*/, true /*latch*/));
526  worldPubs_.pub_map_metadata = mvsim_node::make_shared<ros::Publisher>(
527  n_.advertise<Msg_MapMetaData>("simul_map_metadata", 1 /*queue len*/, true /*latch*/));
528 #else
529  // pub: <VEH>/simul_map, <VEH>/simul_map_metadata
530  // REP-2003: https://ros.org/reps/rep-2003.html
531  // Maps: reliable transient-local
532  auto qosLatched = rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable();
533 
534  worldPubs_.pub_map_ros = n_->create_publisher<Msg_OccupancyGrid>("simul_map", qosLatched);
536  n_->create_publisher<Msg_MapMetaData>("simul_map_metadata", qosLatched);
537 #endif
538 
539  // Publish maps and static stuff:
540  mvsim_world_->runVisitorOnWorldElements([this](mvsim::WorldElementBase& obj)
541  { publishWorldElements(obj); });
542 }
543 
545 {
546 #if PACKAGE_ROS_VERSION == 1
547  return ros::Time::now();
548 #else
549  return clock_->now();
550 #endif
551 }
552 
553 double MVSimNode::myNowSec() const
554 {
555 #if PACKAGE_ROS_VERSION == 1
556  return ros::Time::now().toSec();
557 #else
558  return clock_->now().nanoseconds() * 1e-9;
559 #endif
560 }
561 
565 {
566  // sub: <VEH>/cmd_vel
567 #if PACKAGE_ROS_VERSION == 1
568  pubsubs.sub_cmd_vel = mvsim_node::make_shared<ros::Subscriber>(n_.subscribe<Msg_Twist>(
569  vehVarName("cmd_vel", *veh), 10,
570  [this, veh](Msg_Twist_CSPtr msg) { return this->onROSMsgCmdVel(msg, veh); }));
571 #else
572  pubsubs.sub_cmd_vel = n_->create_subscription<Msg_Twist>(
573  vehVarName("cmd_vel", *veh), 10,
574  [this, veh](Msg_Twist_CSPtr msg) { return this->onROSMsgCmdVel(msg, veh); });
575 #endif
576 
577 #if PACKAGE_ROS_VERSION == 1
578  // pub: <VEH>/odom
579  pubsubs.pub_odom = mvsim_node::make_shared<ros::Publisher>(
580  n_.advertise<Msg_Odometry>(vehVarName("odom", *veh), publisher_history_len_));
581 
582  // pub: <VEH>/base_pose_ground_truth
583  pubsubs.pub_ground_truth = mvsim_node::make_shared<ros::Publisher>(n_.advertise<Msg_Odometry>(
584  vehVarName("base_pose_ground_truth", *veh), publisher_history_len_));
585 
586  // pub: <VEH>/collision
587  pubsubs.pub_collision = mvsim_node::make_shared<ros::Publisher>(
588  n_.advertise<Msg_Bool>(vehVarName("collision", *veh), publisher_history_len_));
589 
590  // pub: <VEH>/tf, <VEH>/tf_static
591  pubsubs.pub_tf = mvsim_node::make_shared<ros::Publisher>(
592  n_.advertise<Msg_TFMessage>(vehVarName("tf", *veh), publisher_history_len_));
593  pubsubs.pub_tf_static = mvsim_node::make_shared<ros::Publisher>(
594  n_.advertise<Msg_TFMessage>(vehVarName("tf_static", *veh), publisher_history_len_));
595 #else
596  // pub: <VEH>/odom
597  pubsubs.pub_odom =
598  n_->create_publisher<Msg_Odometry>(vehVarName("odom", *veh), publisher_history_len_);
599 
600  // pub: <VEH>/base_pose_ground_truth
601  pubsubs.pub_ground_truth = n_->create_publisher<Msg_Odometry>(
602  vehVarName("base_pose_ground_truth", *veh), publisher_history_len_);
603 
604  // pub: <VEH>/collision
605  pubsubs.pub_collision =
606  n_->create_publisher<Msg_Bool>(vehVarName("collision", *veh), publisher_history_len_);
607 
608  // pub: <VEH>/tf, <VEH>/tf_static
609  const auto qos = tf2_ros::DynamicBroadcasterQoS();
610  const auto qos_static = tf2_ros::StaticBroadcasterQoS();
611 
612  pubsubs.pub_tf = n_->create_publisher<Msg_TFMessage>(vehVarName("tf", *veh), qos);
613  pubsubs.pub_tf_static =
614  n_->create_publisher<Msg_TFMessage>(vehVarName("tf_static", *veh), qos_static);
615 #endif
616 
617  // pub: <VEH>/chassis_markers
618  {
619 #if PACKAGE_ROS_VERSION == 1
620  pubsubs.pub_chassis_markers = mvsim_node::make_shared<ros::Publisher>(
621  n_.advertise<Msg_MarkerArray>(vehVarName("chassis_markers", *veh), 5, true /*latch*/));
622 #else
623  rclcpp::QoS qosLatched5(rclcpp::KeepLast(5));
624  qosLatched5.durability(
625  rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
626 
627  pubsubs.pub_chassis_markers =
628  n_->create_publisher<Msg_MarkerArray>(vehVarName("chassis_markers", *veh), qosLatched5);
629 #endif
630  const auto& poly = veh->getChassisShape();
631 
632  // Create one "ROS marker" for each wheel + 1 for the chassis:
633  auto& msg_shapes = pubsubs.chassis_shape_msg;
634  msg_shapes.markers.resize(1 + veh->getNumWheels());
635 
636  // [0] Chassis shape:
637  auto& chassis_shape_msg = msg_shapes.markers[0];
638 
639  chassis_shape_msg.pose = mrpt2ros::toROS_Pose(mrpt::poses::CPose3D::Identity());
640 
641  chassis_shape_msg.action = Msg_Marker::MODIFY;
642  chassis_shape_msg.type = Msg_Marker::LINE_STRIP;
643 
644  chassis_shape_msg.header.frame_id = "base_link";
645  chassis_shape_msg.ns = "mvsim.chassis_shape";
646  chassis_shape_msg.id = veh->getVehicleIndex();
647  chassis_shape_msg.scale.x = 0.05;
648  chassis_shape_msg.scale.y = 0.05;
649  chassis_shape_msg.scale.z = 0.02;
650  chassis_shape_msg.points.resize(poly.size() + 1);
651  for (size_t i = 0; i <= poly.size(); i++)
652  {
653  size_t k = i % poly.size();
654  chassis_shape_msg.points[i].x = poly[k].x;
655  chassis_shape_msg.points[i].y = poly[k].y;
656  chassis_shape_msg.points[i].z = 0;
657  }
658  chassis_shape_msg.color.a = 0.9;
659  chassis_shape_msg.color.r = 0.9;
660  chassis_shape_msg.color.g = 0.9;
661  chassis_shape_msg.color.b = 0.9;
662  chassis_shape_msg.frame_locked = true;
663 
664  // [1:N] Wheel shapes
665  for (size_t i = 0; i < veh->getNumWheels(); i++)
666  {
667  auto& wheel_shape_msg = msg_shapes.markers[1 + i];
668  const auto& w = veh->getWheelInfo(i);
669 
670  const double lx = w.diameter * 0.5, ly = w.width * 0.5;
671 
672  // Init values. Copy the contents from the chassis msg
673  wheel_shape_msg = msg_shapes.markers[0];
674 
675  wheel_shape_msg.ns =
676  mrpt::format("mvsim.chassis_shape.wheel%u", static_cast<unsigned int>(i));
677  wheel_shape_msg.points.resize(5);
678  wheel_shape_msg.points[0].x = lx;
679  wheel_shape_msg.points[0].y = ly;
680  wheel_shape_msg.points[0].z = 0;
681  wheel_shape_msg.points[1].x = lx;
682  wheel_shape_msg.points[1].y = -ly;
683  wheel_shape_msg.points[1].z = 0;
684  wheel_shape_msg.points[2].x = -lx;
685  wheel_shape_msg.points[2].y = -ly;
686  wheel_shape_msg.points[2].z = 0;
687  wheel_shape_msg.points[3].x = -lx;
688  wheel_shape_msg.points[3].y = ly;
689  wheel_shape_msg.points[3].z = 0;
690  wheel_shape_msg.points[4] = wheel_shape_msg.points[0];
691 
692  wheel_shape_msg.color.r = w.color.R / 255.0f;
693  wheel_shape_msg.color.g = w.color.G / 255.0f;
694  wheel_shape_msg.color.b = w.color.B / 255.0f;
695  wheel_shape_msg.color.a = 1.0f;
696 
697  // Set local pose of the wheel wrt the vehicle:
698  wheel_shape_msg.pose = mrpt2ros::toROS_Pose(w.pose());
699  } // end for each wheel
700 
701  // Publish Initial pose
702  pubsubs.pub_chassis_markers->publish(msg_shapes);
703  }
704 
705  // pub: <VEH>/chassis_polygon
706  {
707 #if PACKAGE_ROS_VERSION == 1
708  pubsubs.pub_chassis_shape = mvsim_node::make_shared<ros::Publisher>(
709  n_.advertise<Msg_Polygon>(vehVarName("chassis_polygon", *veh), 1, true /*latch*/));
710 #else
711  rclcpp::QoS qosLatched1(rclcpp::KeepLast(1));
712  qosLatched1.durability(
713  rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
714 
715  pubsubs.pub_chassis_shape =
716  n_->create_publisher<Msg_Polygon>(vehVarName("chassis_polygon", *veh), qosLatched1);
717 #endif
718  Msg_Polygon poly_msg;
719 
720  // Do the first (and unique) publish:
721  const auto& poly = veh->getChassisShape();
722  poly_msg.points.resize(poly.size());
723  for (size_t i = 0; i < poly.size(); i++)
724  {
725  poly_msg.points[i].x = poly[i].x;
726  poly_msg.points[i].y = poly[i].y;
727  poly_msg.points[i].z = 0;
728  }
729  pubsubs.pub_chassis_shape->publish(poly_msg);
730  }
731 
733  {
734 #if PACKAGE_ROS_VERSION == 1
735  // pub: <VEH>/amcl_pose
736  pubsubs.pub_amcl_pose =
737  mvsim_node::make_shared<ros::Publisher>(n_.advertise<Msg_PoseWithCovarianceStamped>(
738  vehVarName("amcl_pose", *veh), 1, true /*latch*/));
739  // pub: <VEH>/particlecloud
740  pubsubs.pub_particlecloud = mvsim_node::make_shared<ros::Publisher>(
741  n_.advertise<Msg_PoseArray>(vehVarName("particlecloud", *veh), 1));
742 #else
743  rclcpp::QoS qosLatched1(rclcpp::KeepLast(1));
744  qosLatched1.durability(
745  rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
746 
747  // pub: <VEH>/amcl_pose
748  pubsubs.pub_amcl_pose = n_->create_publisher<Msg_PoseWithCovarianceStamped>(
749  vehVarName("amcl_pose", *veh), qosLatched1);
750  // pub: <VEH>/particlecloud
751  pubsubs.pub_particlecloud =
752  n_->create_publisher<Msg_PoseArray>(vehVarName("particlecloud", *veh), 1);
753 #endif
754  }
755 
756  // TF STATIC(namespace <Ri>): /base_link -> /base_footprint
758  tx.header.frame_id = "base_link";
759  tx.child_frame_id = "base_footprint";
760  tx.header.stamp = myNow();
761  tx.transform = tf2::toMsg(tfIdentity_);
762 
763  Msg_TFMessage tfMsg;
764  tfMsg.transforms.push_back(tx);
765  pubsubs.pub_tf_static->publish(tfMsg);
766 }
767 
769 {
770  auto* controller = veh->getControllerInterface();
771 
772  // Update cmd_vel timestamp:
774 
775  const bool ctrlAcceptTwist =
776  controller->setTwistCommand({cmd->linear.x, cmd->linear.y, cmd->angular.z});
777 
778  if (!ctrlAcceptTwist)
779  {
781  1.0, "*Warning* Vehicle's controller ['%s'] refuses Twist commands!",
782  veh->getName().c_str());
783  }
784 }
785 
788 {
789  if (!mvsim_world_) return;
790 
791  const auto& vehs = mvsim_world_->getListOfVehicles();
792 
793  // skip if the node is already shutting down:
794  if (!ok()) return;
795 
796  // Get current simulation time (for messages) and publish "/clock"
797  // ----------------------------------------------------------------
798 #if PACKAGE_ROS_VERSION == 1
799  // sim_time_.fromSec(mvsim_world_->get_simul_time());
800  // clockMsg_.clock = sim_time_;
801  // pub_clock_->publish(clockMsg_);
802 #else
803  // sim_time_ = myNow();
804  // MRPT_TODO("Publish /clock for ROS2 too?");
805 #endif
806 
807  // Publish all TFs for each vehicle:
808  // ---------------------------------------------------------------------
809  if (tim_publish_tf_.Tac() > period_ms_publish_tf_ * 1e-3)
810  {
811  tim_publish_tf_.Tic();
812 
813  size_t i = 0;
814  ASSERT_EQUAL_(pubsub_vehicles_.size(), vehs.size());
815 
816  for (auto it = vehs.begin(); it != vehs.end(); ++it, ++i)
817  {
818  const auto& veh = it->second;
819  auto& pubs = pubsub_vehicles_[i];
820 
821  // 1) Ground-truth pose and velocity
822  // --------------------------------------------
823  const mrpt::math::TPose3D& gh_veh_pose = veh->getPose();
824  // [vx,vy,w] in global frame
825  const auto& gh_veh_vel = veh->getTwist();
826 
827  {
828  Msg_Odometry gtOdoMsg;
829  gtOdoMsg.pose.pose = mrpt2ros::toROS_Pose(gh_veh_pose);
830 
831  gtOdoMsg.twist.twist.linear.x = gh_veh_vel.vx;
832  gtOdoMsg.twist.twist.linear.y = gh_veh_vel.vy;
833  gtOdoMsg.twist.twist.linear.z = 0;
834  gtOdoMsg.twist.twist.angular.z = gh_veh_vel.omega;
835 
836  gtOdoMsg.header.stamp = myNow();
837  gtOdoMsg.header.frame_id = "odom";
838  gtOdoMsg.child_frame_id = "base_link";
839 
840  pubs.pub_ground_truth->publish(gtOdoMsg);
841 
843  {
845  Msg_PoseArray particleCloud;
846 
847  // topic: <Ri>/particlecloud
848  {
849  particleCloud.header.stamp = myNow();
850  particleCloud.header.frame_id = "map";
851  particleCloud.poses.resize(1);
852  particleCloud.poses[0] = gtOdoMsg.pose.pose;
853  pubs.pub_particlecloud->publish(particleCloud);
854  }
855 
856  // topic: <Ri>/amcl_pose
857  {
858  currentPos.header = gtOdoMsg.header;
859  currentPos.pose.pose = gtOdoMsg.pose.pose;
860  pubs.pub_amcl_pose->publish(currentPos);
861  }
862 
863  // TF(namespace <Ri>): /map -> /odom
864  {
866  tx.header.frame_id = "map";
867  tx.child_frame_id = "odom";
868  tx.header.stamp = myNow();
869  tx.transform = tf2::toMsg(tf2::Transform::getIdentity());
870 
871  Msg_TFMessage tfMsg;
872  tfMsg.transforms.push_back(tx);
873  pubs.pub_tf->publish(tfMsg);
874  }
875  }
876  }
877 
878  // 2) Chassis markers (for rviz visualization)
879  // --------------------------------------------
880  // pub: <VEH>/chassis_markers
881  {
882  // visualization_msgs::MarkerArray
883  auto& msg_shapes = pubs.chassis_shape_msg;
884  ASSERT_EQUAL_(msg_shapes.markers.size(), (1 + veh->getNumWheels()));
885 
886  // [0] Chassis shape: static no need to update.
887  // [1:N] Wheel shapes: may move
888  for (size_t j = 0; j < veh->getNumWheels(); j++)
889  {
890  // visualization_msgs::Marker
891  auto& wheel_shape_msg = msg_shapes.markers[1 + j];
892  const auto& w = veh->getWheelInfo(j);
893 
894  // Set local pose of the wheel wrt the vehicle:
895  wheel_shape_msg.pose = mrpt2ros::toROS_Pose(w.pose());
896 
897  } // end for each wheel
898 
899  // Publish Initial pose
900  pubs.pub_chassis_markers->publish(msg_shapes);
901  }
902 
903  // 3) odometry transform
904  // --------------------------------------------
905  {
906  const mrpt::math::TPose3D odo_pose = gh_veh_pose;
907 
908  // TF(namespace <Ri>): /odom -> /base_link
910  {
912  tx.header.frame_id = "odom";
913  tx.child_frame_id = "base_link";
914  tx.header.stamp = myNow();
915  tx.transform = tf2::toMsg(mrpt2ros::toROS_tfTransform(odo_pose));
916 
917  Msg_TFMessage tfMsg;
918  tfMsg.transforms.push_back(tx);
919  pubs.pub_tf->publish(tfMsg);
920  }
921 
922  // Apart from TF, publish to the "odom" topic as well
923  {
924  Msg_Odometry odoMsg;
925  odoMsg.pose.pose = mrpt2ros::toROS_Pose(odo_pose);
926 
927  // first, we'll populate the header for the odometry msg
928  odoMsg.header.stamp = myNow();
929  odoMsg.header.frame_id = "odom";
930  odoMsg.child_frame_id = "base_link";
931 
932  // publish:
933  pubs.pub_odom->publish(odoMsg);
934  }
935  }
936 
937  // 4) Collision status
938  // --------------------------------------------
939  const bool col = veh->hadCollision();
940  veh->resetCollisionFlag();
941  {
942  Msg_Bool colMsg;
943  colMsg.data = col;
944 
945  // publish:
946  pubs.pub_collision->publish(colMsg);
947  }
948 
949  } // end for each vehicle
950 
951  } // end publish tf
952 
953 } // end spinNotifyROS()
954 
956  const mvsim::Simulable& sim, const mrpt::obs::CObservation::Ptr& obs)
957 {
958  mrpt::system::CTimeLoggerEntry tle(profiler_, "onNewObservation");
959 
960  // skip if the node is already shutting down:
961  if (!ok()) return;
962 
963  ASSERT_(obs);
964  ASSERT_(!obs->sensorLabel.empty());
965 
966  const auto& vehPtr = dynamic_cast<const mvsim::VehicleBase*>(&sim);
967  if (!vehPtr) return; // for example, if obs from invisible aux block.
968  const auto& veh = *vehPtr;
969 
970  // -----------------------------
971  // Observation: 2d laser scans
972  // -----------------------------
973  if (const auto* o2DLidar = dynamic_cast<const mrpt::obs::CObservation2DRangeScan*>(obs.get());
974  o2DLidar)
975  {
976  internalOn(veh, *o2DLidar);
977  }
978  else if (const auto* oImage = dynamic_cast<const mrpt::obs::CObservationImage*>(obs.get());
979  oImage)
980  {
981  internalOn(veh, *oImage);
982  }
983  else if (const auto* oRGBD = dynamic_cast<const mrpt::obs::CObservation3DRangeScan*>(obs.get());
984  oRGBD)
985  {
986  internalOn(veh, *oRGBD);
987  }
988  else if (const auto* oPC = dynamic_cast<const mrpt::obs::CObservationPointCloud*>(obs.get());
989  oPC)
990  {
991  internalOn(veh, *oPC);
992  }
993  else if (const auto* oIMU = dynamic_cast<const mrpt::obs::CObservationIMU*>(obs.get()); oIMU)
994  {
995  internalOn(veh, *oIMU);
996  }
997  else if (const auto* oGPS = dynamic_cast<const mrpt::obs::CObservationGPS*>(obs.get()); oGPS)
998  {
999  internalOn(veh, *oGPS);
1000  }
1001  else
1002  {
1003  // Don't know how to emit this observation to ROS!
1005  1.0, "Do not know how to publish this observation to ROS: '"
1006  << obs->sensorLabel << "', class: " << obs->GetRuntimeClass()->className);
1007  }
1008 
1009 } // end of onNewObservation()
1010 
1013 std::string MVSimNode::vehVarName(const std::string& sVarName, const mvsim::VehicleBase& veh) const
1014 {
1015  if (mvsim_world_->getListOfVehicles().size() == 1 && !force_publish_vehicle_namespace_)
1016  {
1017  return sVarName;
1018  }
1019  else
1020  {
1021  return veh.getName() + std::string("/") + sVarName;
1022  }
1023 }
1024 
1026  const mvsim::VehicleBase& veh, const mrpt::obs::CObservation2DRangeScan& obs)
1027 {
1028  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1029  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1030 
1031  // Create the publisher the first time an observation arrives:
1032  const bool is_1st_pub = pubs.pub_sensors.find(obs.sensorLabel) == pubs.pub_sensors.end();
1033  auto& pub = pubs.pub_sensors[obs.sensorLabel];
1034 
1035  if (is_1st_pub)
1036  {
1037 #if PACKAGE_ROS_VERSION == 1
1038  pub = mvsim_node::make_shared<ros::Publisher>(
1039  n_.advertise<Msg_LaserScan>(vehVarName(obs.sensorLabel, veh), publisher_history_len_));
1040 #else
1041  pub = mvsim_node::make_shared<PublisherWrapper<Msg_LaserScan>>(
1042  n_, vehVarName(obs.sensorLabel, veh), publisher_history_len_);
1043 #endif
1044  }
1045  lck.unlock();
1046 
1047  // Send TF:
1048  mrpt::poses::CPose3D sensorPose = obs.sensorPose;
1049  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1050 
1051  Msg_TransformStamped tfStmp;
1052  tfStmp.transform = tf2::toMsg(transform);
1053  tfStmp.header.frame_id = "base_link";
1054  tfStmp.child_frame_id = obs.sensorLabel;
1055  tfStmp.header.stamp = myNow();
1056 
1057  Msg_TFMessage tfMsg;
1058  tfMsg.transforms.push_back(tfStmp);
1059  pubs.pub_tf->publish(tfMsg);
1060 
1061  // Send observation:
1062  {
1063  // Convert observation MRPT -> ROS
1064  Msg_Pose msg_pose_laser;
1065  Msg_LaserScan msg_laser;
1066  // Force usage of simulation time:
1067  msg_laser.header.stamp = myNow();
1068  msg_laser.header.frame_id = obs.sensorLabel;
1069  mrpt2ros::toROS(obs, msg_laser, msg_pose_laser);
1070  pub->publish(mvsim_node::make_shared<Msg_LaserScan>(msg_laser));
1071  }
1072 }
1073 
1074 void MVSimNode::internalOn(const mvsim::VehicleBase& veh, const mrpt::obs::CObservationIMU& obs)
1075 {
1076  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1077  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1078 
1079  // Create the publisher the first time an observation arrives:
1080  const bool is_1st_pub = pubs.pub_sensors.find(obs.sensorLabel) == pubs.pub_sensors.end();
1081  auto& pub = pubs.pub_sensors[obs.sensorLabel];
1082 
1083  if (is_1st_pub)
1084  {
1085 #if PACKAGE_ROS_VERSION == 1
1086  pub = mvsim_node::make_shared<ros::Publisher>(
1087  n_.advertise<Msg_Imu>(vehVarName(obs.sensorLabel, veh), publisher_history_len_));
1088 #else
1089  pub = mvsim_node::make_shared<PublisherWrapper<Msg_Imu>>(
1090  n_, vehVarName(obs.sensorLabel, veh), publisher_history_len_);
1091 #endif
1092  }
1093  lck.unlock();
1094 
1095  // Send TF:
1096  mrpt::poses::CPose3D sensorPose = obs.sensorPose;
1097  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1098 
1099  Msg_TransformStamped tfStmp;
1100  tfStmp.transform = tf2::toMsg(transform);
1101  tfStmp.header.frame_id = "base_link";
1102  tfStmp.child_frame_id = obs.sensorLabel;
1103  tfStmp.header.stamp = myNow();
1104 
1105  Msg_TFMessage tfMsg;
1106  tfMsg.transforms.push_back(tfStmp);
1107  pubs.pub_tf->publish(tfMsg);
1108 
1109  // Send observation:
1110  {
1111  // Convert observation MRPT -> ROS
1112  Msg_Imu msg_imu;
1113  Msg_Header msg_header;
1114  // Force usage of simulation time:
1115  msg_header.stamp = myNow();
1116  msg_header.frame_id = obs.sensorLabel;
1117  mrpt2ros::toROS(obs, msg_header, msg_imu);
1118  pub->publish(mvsim_node::make_shared<Msg_Imu>(msg_imu));
1119  }
1120 }
1121 
1122 void MVSimNode::internalOn(const mvsim::VehicleBase& veh, const mrpt::obs::CObservationGPS& obs)
1123 {
1124  if (!obs.has_GGA_datum())
1125  {
1126  ROS12_WARN_THROTTLE(5.0, "Ignoring GPS observation without GGA field (!)");
1127  return;
1128  }
1129 
1130  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1131  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1132 
1133  // Create the publisher the first time an observation arrives:
1134  const bool is_1st_pub = pubs.pub_sensors.find(obs.sensorLabel) == pubs.pub_sensors.end();
1135  auto& pub = pubs.pub_sensors[obs.sensorLabel];
1136 
1137  if (is_1st_pub)
1138  {
1139 #if PACKAGE_ROS_VERSION == 1
1140  pub = mvsim_node::make_shared<ros::Publisher>(
1141  n_.advertise<Msg_GPS>(vehVarName(obs.sensorLabel, veh), publisher_history_len_));
1142 #else
1143  pub = mvsim_node::make_shared<PublisherWrapper<Msg_GPS>>(
1144  n_, vehVarName(obs.sensorLabel, veh), publisher_history_len_);
1145 #endif
1146  }
1147  lck.unlock();
1148 
1149  // Send TF:
1150  mrpt::poses::CPose3D sensorPose = obs.sensorPose;
1151  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1152 
1153  Msg_TransformStamped tfStmp;
1154  tfStmp.transform = tf2::toMsg(transform);
1155  tfStmp.header.frame_id = "base_link";
1156  tfStmp.child_frame_id = obs.sensorLabel;
1157  tfStmp.header.stamp = myNow();
1158 
1159  Msg_TFMessage tfMsg;
1160  tfMsg.transforms.push_back(tfStmp);
1161  pubs.pub_tf->publish(tfMsg);
1162 
1163  // Send observation:
1164  {
1165  // Convert observation MRPT -> ROS
1166  auto msg = mvsim_node::make_shared<Msg_GPS>();
1167  msg->header.stamp = myNow();
1168  msg->header.frame_id = obs.sensorLabel;
1169 
1170  const auto& o = obs.getMsgByClass<mrpt::obs::gnss::Message_NMEA_GGA>();
1171 
1172  msg->latitude = o.fields.latitude_degrees;
1173  msg->longitude = o.fields.longitude_degrees;
1174  msg->altitude = o.fields.altitude_meters;
1175 
1176  if (auto& c = obs.covariance_enu; c.has_value())
1177  {
1178 #if PACKAGE_ROS_VERSION == 1
1179  msg->position_covariance_type = sensor_msgs::NavSatFix::COVARIANCE_TYPE_DIAGONAL_KNOWN;
1180 #else
1181  msg->position_covariance_type =
1182  sensor_msgs::msg::NavSatFix::COVARIANCE_TYPE_DIAGONAL_KNOWN;
1183 #endif
1184 
1185  msg->position_covariance.fill(0.0);
1186  msg->position_covariance[0] = (*c)(0, 0);
1187  msg->position_covariance[4] = (*c)(1, 1);
1188  msg->position_covariance[8] = (*c)(2, 2);
1189  }
1190 
1191  pub->publish(msg);
1192  }
1193 }
1194 
1195 namespace
1196 {
1200 Msg_CameraInfo camInfoToRos(const mrpt::img::TCamera& c)
1201 {
1202  Msg_CameraInfo ci;
1203  ci.height = c.nrows;
1204  ci.width = c.ncols;
1205 
1206 #if PACKAGE_ROS_VERSION == 1
1207  auto& dist = ci.D;
1208  auto& K = ci.K;
1209  auto& P = ci.P;
1210 #else
1211  auto& dist = ci.d;
1212  auto& K = ci.k;
1213  auto& P = ci.p;
1214 #endif
1215 
1216  switch (c.distortion)
1217  {
1218  case mrpt::img::DistortionModel::kannala_brandt:
1219  ci.distortion_model = "kannala_brandt";
1220  dist.resize(4);
1221  dist[0] = c.k1();
1222  dist[1] = c.k2();
1223  dist[2] = c.k3();
1224  dist[3] = c.k4();
1225  break;
1226 
1227  case mrpt::img::DistortionModel::plumb_bob:
1228  ci.distortion_model = "plumb_bob";
1229  dist.resize(5);
1230  for (size_t i = 0; i < dist.size(); i++) dist[i] = c.dist[i];
1231  break;
1232 
1233  case mrpt::img::DistortionModel::none:
1234  ci.distortion_model = "plumb_bob";
1235  dist.resize(5);
1236  for (size_t i = 0; i < dist.size(); i++) dist[i] = 0;
1237  break;
1238 
1239  default:
1240  THROW_EXCEPTION("Unexpected distortion model!");
1241  }
1242 
1243  K.fill(0);
1244  K[0] = c.fx();
1245  K[4] = c.fy();
1246  K[2] = c.cx();
1247  K[5] = c.cy();
1248  K[8] = 1.0;
1249 
1250  P.fill(0);
1251  P[0] = 1;
1252  P[5] = 1;
1253  P[10] = 1;
1254 
1255  return ci;
1256 }
1257 } // namespace
1258 
1259 void MVSimNode::internalOn(const mvsim::VehicleBase& veh, const mrpt::obs::CObservationImage& obs)
1260 {
1261  using namespace std::string_literals;
1262 
1263  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1264  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1265 
1266  const std::string img_topic = obs.sensorLabel + "/image_raw"s;
1267  const std::string camInfo_topic = obs.sensorLabel + "/camera_info"s;
1268 
1269  // Create the publisher the first time an observation arrives:
1270  const bool is_1st_pub = pubs.pub_sensors.find(img_topic) == pubs.pub_sensors.end();
1271  auto& pubImg = pubs.pub_sensors[img_topic];
1272  auto& pubCamInfo = pubs.pub_sensors[camInfo_topic];
1273 
1274  if (is_1st_pub)
1275  {
1276 #if PACKAGE_ROS_VERSION == 1
1277  pubImg = mvsim_node::make_shared<ros::Publisher>(
1278  n_.advertise<Msg_Image>(vehVarName(img_topic, veh), publisher_history_len_));
1279  pubCamInfo = mvsim_node::make_shared<ros::Publisher>(
1280  n_.advertise<Msg_CameraInfo>(vehVarName(camInfo_topic, veh), publisher_history_len_));
1281 #else
1282  pubImg = mvsim_node::make_shared<PublisherWrapper<Msg_Image>>(
1283  n_, vehVarName(img_topic, veh), publisher_history_len_);
1284  pubCamInfo = mvsim_node::make_shared<PublisherWrapper<Msg_CameraInfo>>(
1285  n_, vehVarName(camInfo_topic, veh), publisher_history_len_);
1286 #endif
1287  }
1288  lck.unlock();
1289 
1290  // Send TF:
1291  mrpt::poses::CPose3D sensorPose;
1292  obs.getSensorPose(sensorPose);
1293  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1294 
1295  Msg_TransformStamped tfStmp;
1296  tfStmp.transform = tf2::toMsg(transform);
1297  tfStmp.header.frame_id = "base_link";
1298  tfStmp.child_frame_id = obs.sensorLabel;
1299  tfStmp.header.stamp = myNow();
1300 
1301  Msg_TFMessage tfMsg;
1302  tfMsg.transforms.push_back(tfStmp);
1303  pubs.pub_tf->publish(tfMsg);
1304 
1305  // Send observation:
1306  Msg_Header msg_header;
1307  msg_header.stamp = myNow();
1308  msg_header.frame_id = obs.sensorLabel;
1309 
1310  {
1311  // Convert observation MRPT -> ROS
1312  Msg_Image msg_img;
1313  msg_img = mrpt2ros::toROS(obs.image, msg_header);
1314  pubImg->publish(mvsim_node::make_shared<Msg_Image>(msg_img));
1315  }
1316  // Send CameraInfo
1317  {
1318  Msg_CameraInfo camInfo = camInfoToRos(obs.cameraParams);
1319  camInfo.header = msg_header;
1320  pubCamInfo->publish(mvsim_node::make_shared<Msg_CameraInfo>(camInfo));
1321  }
1322 }
1323 
1325  const mvsim::VehicleBase& veh, const mrpt::obs::CObservation3DRangeScan& obs)
1326 {
1327  using namespace std::string_literals;
1328 
1329  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1330  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1331 
1332  const auto lbPoints = obs.sensorLabel + "_points"s;
1333  const auto lbImage = obs.sensorLabel + "_image"s;
1334 
1335  // Create the publisher the first time an observation arrives:
1336  const bool is_1st_pub = pubs.pub_sensors.find(lbPoints) == pubs.pub_sensors.end();
1337 
1338  auto& pubPts = pubs.pub_sensors[lbPoints];
1339  auto& pubImg = pubs.pub_sensors[lbImage];
1340 
1341  if (is_1st_pub)
1342  {
1343 #if PACKAGE_ROS_VERSION == 1
1344  pubImg = mvsim_node::make_shared<ros::Publisher>(
1345  n_.advertise<Msg_Image>(vehVarName(lbImage, veh), publisher_history_len_));
1346  pubPts = mvsim_node::make_shared<ros::Publisher>(
1347  n_.advertise<Msg_PointCloud2>(vehVarName(lbPoints, veh), publisher_history_len_));
1348 #else
1349  pubImg = mvsim_node::make_shared<PublisherWrapper<Msg_Image>>(
1350  n_, vehVarName(lbImage, veh), publisher_history_len_);
1351  pubPts = mvsim_node::make_shared<PublisherWrapper<Msg_PointCloud2>>(
1352  n_, vehVarName(lbPoints, veh), publisher_history_len_);
1353 #endif
1354  }
1355  lck.unlock();
1356 
1357  const auto now = myNow();
1358 
1359  // IMAGE
1360  // --------
1361  if (obs.hasIntensityImage)
1362  {
1363  // Send TF:
1364  mrpt::poses::CPose3D sensorPose = obs.sensorPose + obs.relativePoseIntensityWRTDepth;
1365  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1366 
1367  Msg_TransformStamped tfStmp;
1368  tfStmp.transform = tf2::toMsg(transform);
1369  tfStmp.header.frame_id = "base_link";
1370  tfStmp.child_frame_id = lbImage;
1371  tfStmp.header.stamp = now;
1372 
1373  Msg_TFMessage tfMsg;
1374  tfMsg.transforms.push_back(tfStmp);
1375  pubs.pub_tf->publish(tfMsg);
1376 
1377  // Send observation:
1378  {
1379  // Convert observation MRPT -> ROS
1380  Msg_Image msg_img;
1381  Msg_Header msg_header;
1382  msg_header.stamp = now;
1383  msg_header.frame_id = lbImage;
1384  msg_img = mrpt2ros::toROS(obs.intensityImage, msg_header);
1385  pubImg->publish(mvsim_node::make_shared<Msg_Image>(msg_img));
1386  }
1387  }
1388 
1389  // POINTS
1390  // --------
1391  if (obs.hasRangeImage)
1392  {
1393  // Send TF:
1394  mrpt::poses::CPose3D sensorPose = obs.sensorPose;
1395  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1396 
1397  Msg_TransformStamped tfStmp;
1398  tfStmp.transform = tf2::toMsg(transform);
1399  tfStmp.header.frame_id = "base_link";
1400  tfStmp.child_frame_id = lbPoints;
1401  tfStmp.header.stamp = now;
1402 
1403  Msg_TFMessage tfMsg;
1404  tfMsg.transforms.push_back(tfStmp);
1405  pubs.pub_tf->publish(tfMsg);
1406 
1407  // Send observation:
1408  {
1409  // Convert observation MRPT -> ROS
1410  Msg_PointCloud2 msg_pts;
1411  Msg_Header msg_header;
1412  msg_header.stamp = now;
1413  msg_header.frame_id = lbPoints;
1414 
1415  mrpt::obs::T3DPointsProjectionParams pp;
1416  pp.takeIntoAccountSensorPoseOnRobot = false;
1417 
1418  mrpt::maps::CSimplePointsMap pts;
1419  const_cast<mrpt::obs::CObservation3DRangeScan&>(obs).unprojectInto(pts, pp);
1420  mrpt2ros::toROS(pts, msg_header, msg_pts);
1421  pubPts->publish(mvsim_node::make_shared<Msg_PointCloud2>(msg_pts));
1422  }
1423  }
1424 }
1425 
1427  const mvsim::VehicleBase& veh, const mrpt::obs::CObservationPointCloud& obs)
1428 {
1429  using namespace std::string_literals;
1430 
1431  auto lck = mrpt::lockHelper(pubsub_vehicles_mtx_);
1432  auto& pubs = pubsub_vehicles_[veh.getVehicleIndex()];
1433 
1434  const auto lbPoints = obs.sensorLabel + "_points"s;
1435 
1436  // Create the publisher the first time an observation arrives:
1437  const bool is_1st_pub = pubs.pub_sensors.find(lbPoints) == pubs.pub_sensors.end();
1438 
1439  auto& pubPts = pubs.pub_sensors[lbPoints];
1440 
1441  if (is_1st_pub)
1442  {
1443 #if PACKAGE_ROS_VERSION == 1
1444  pubPts = mvsim_node::make_shared<ros::Publisher>(
1445  n_.advertise<Msg_PointCloud2>(vehVarName(lbPoints, veh), publisher_history_len_));
1446 #else
1447  pubPts = mvsim_node::make_shared<PublisherWrapper<Msg_PointCloud2>>(
1448  n_, vehVarName(lbPoints, veh), publisher_history_len_);
1449 #endif
1450  }
1451  lck.unlock();
1452 
1453  const auto now = myNow();
1454 
1455  // POINTS
1456  // --------
1457 
1458  // Send TF:
1459  mrpt::poses::CPose3D sensorPose = obs.sensorPose;
1460  auto transform = mrpt2ros::toROS_tfTransform(sensorPose);
1461 
1462  Msg_TransformStamped tfStmp;
1463  tfStmp.transform = tf2::toMsg(transform);
1464  tfStmp.header.frame_id = "base_link";
1465  tfStmp.child_frame_id = lbPoints;
1466  tfStmp.header.stamp = now;
1467 
1468  Msg_TFMessage tfMsg;
1469  tfMsg.transforms.push_back(tfStmp);
1470  pubs.pub_tf->publish(tfMsg);
1471 
1472  // Send observation:
1473  {
1474  // Convert observation MRPT -> ROS
1475  Msg_PointCloud2 msg_pts;
1476  Msg_Header msg_header;
1477  msg_header.stamp = now;
1478  msg_header.frame_id = lbPoints;
1479 
1480 #if defined(HAVE_POINTS_XYZIRT)
1481  if (auto* xyzirt = dynamic_cast<const mrpt::maps::CPointsMapXYZIRT*>(obs.pointcloud.get());
1482  xyzirt)
1483  {
1484  mrpt2ros::toROS(*xyzirt, msg_header, msg_pts);
1485  }
1486  else
1487 #endif
1488  if (auto* xyzi = dynamic_cast<const mrpt::maps::CPointsMapXYZI*>(obs.pointcloud.get());
1489  xyzi)
1490  {
1491  mrpt2ros::toROS(*xyzi, msg_header, msg_pts);
1492  }
1493  else if (auto* sPts =
1494  dynamic_cast<const mrpt::maps::CSimplePointsMap*>(obs.pointcloud.get());
1495  sPts)
1496  {
1497  mrpt2ros::toROS(*sPts, msg_header, msg_pts);
1498  }
1499  else
1500  {
1501  THROW_EXCEPTION("Do not know how to handle this variant of CPointsMap");
1502  }
1503 
1504  pubPts->publish(mvsim_node::make_shared<Msg_PointCloud2>(msg_pts));
1505  }
1506 }
Msg_Marker
visualization_msgs::msg::Marker Msg_Marker
Definition: mvsim_node.cpp:99
MVSimNode::TThreadParams::obj
MVSimNode * obj
Definition: mvsim_node_core.h:295
ROS12_WARN_THROTTLE
#define ROS12_WARN_THROTTLE(...)
Definition: mvsim_node.cpp:115
MVSimNode::onNewObservation
void onNewObservation(const mvsim::Simulable &veh, const mrpt::obs::CObservation::Ptr &obs)
Definition: mvsim_node.cpp:955
MVSimNode::do_fake_localization_
bool do_fake_localization_
Definition: mvsim_node_core.h:167
mvsim::VehicleBase::getChassisShape
const mrpt::math::TPolygon2D & getChassisShape() const
Definition: VehicleBase.h:106
mvsim
Definition: Client.h:21
ROS12_INFO
#define ROS12_INFO(...)
Definition: mvsim_node.cpp:114
MVSimNode::spin
void spin()
Process pending msgs, run real-time simulation, etc.
Definition: mvsim_node.cpp:317
ROS12_WARN_STREAM_THROTTLE
#define ROS12_WARN_STREAM_THROTTLE(...)
Definition: mvsim_node.cpp:116
mvsim::Simulable::getName
const std::string & getName() const
Definition: Simulable.h:107
ROS12_ERROR
#define ROS12_ERROR(...)
Definition: mvsim_node.cpp:118
MVSimNode::~MVSimNode
~MVSimNode()
Definition: mvsim_node.cpp:281
MVSimNode::tim_teleop_refresh_
mrpt::system::CTicTac tim_teleop_refresh_
Definition: mvsim_node_core.h:318
MVSimNode::spinNotifyROS
void spinNotifyROS()
Definition: mvsim_node.cpp:787
MVSimNode::gui_key_events_
mvsim::World::TGUIKeyEvent gui_key_events_
Definition: mvsim_node_core.h:324
MVSimNode::TThreadParams
Definition: mvsim_node_core.h:291
MVSimNode::vehVarName
std::string vehVarName(const std::string &sVarName, const mvsim::VehicleBase &veh) const
Definition: mvsim_node.cpp:1013
MVSimNode::publisher_history_len_
int publisher_history_len_
Definition: mvsim_node_core.h:171
MVSimNode::WorldPubs::pub_map_metadata
rclcpp::Publisher< Msg_MapMetaData >::SharedPtr pub_map_metadata
Definition: mvsim_node_core.h:203
MVSimNode::force_publish_vehicle_namespace_
bool force_publish_vehicle_namespace_
If true, vehicle namespaces will be used even if there is only one vehicle:
Definition: mvsim_node_core.h:313
MVSimNode::TPubSubPerVehicle::sub_cmd_vel
rclcpp::Subscription< Msg_Twist >::SharedPtr sub_cmd_vel
Subscribers vehicle's "cmd_vel" topic.
Definition: mvsim_node_core.h:237
MVSimNode::thread_params_
TThreadParams thread_params_
Definition: mvsim_node_core.h:298
s
XmlRpcServer s
MVSimNode::lastCmdVelTimestamp_
std::map< mvsim::VehicleBase *, double > lastCmdVelTimestamp_
Definition: mvsim_node_core.h:320
Msg_PointCloud2
sensor_msgs::msg::PointCloud2 Msg_PointCloud2
Definition: mvsim_node.cpp:97
MAX_CMD_VEL_AGE_SECONDS
const double MAX_CMD_VEL_AGE_SECONDS
Definition: mvsim_node.cpp:121
MVSimNode::TPubSubPerVehicle::pub_particlecloud
rclcpp::Publisher< Msg_PoseArray >::SharedPtr pub_particlecloud
Definition: mvsim_node_core.h:246
mvsim::VehicleBase::getControllerInterface
virtual ControllerBaseInterface * getControllerInterface()=0
mvsim::ControllerBaseInterface::TeleopOutput::append_gui_lines
std::string append_gui_lines
Definition: ControllerBase.h:35
TimeBase< Time, Duration >::toSec
double toSec() const
MVSimNode::TPubSubPerVehicle
Definition: mvsim_node_core.h:209
MVSimNode::myNow
ros_Time myNow() const
Definition: mvsim_node.cpp:544
Msg_MapMetaData
nav_msgs::msg::MapMetaData Msg_MapMetaData
Definition: mvsim_node_core.h:93
MVSimNode::MVSimNode
MVSimNode(rclcpp::Node::SharedPtr &n)
Definition: mvsim_node.cpp:130
Msg_Image
sensor_msgs::msg::Image Msg_Image
Definition: mvsim_node.cpp:94
ros_Time
rclcpp::Time ros_Time
Definition: mvsim_node_core.h:83
MVSimNode::publishWorldElements
void publishWorldElements(mvsim::WorldElementBase &obj)
Definition: mvsim_node.cpp:478
mvsim::OccupancyGridMap
Definition: OccupancyGridMap.h:28
Msg_MarkerArray
visualization_msgs::msg::MarkerArray Msg_MarkerArray
Definition: mvsim_node_core.h:96
MVSimNode
Definition: mvsim_node_core.h:125
ros::ok
ROSCPP_DECL bool ok()
MVSimNode::tim_publish_tf_
mrpt::system::CTicTac tim_publish_tf_
Definition: mvsim_node_core.h:310
mvsim_node_core.h
MVSimNode::clock_
rclcpp::Clock::SharedPtr clock_
Definition: mvsim_node_core.h:190
MVSimNode::realtime_factor_
double realtime_factor_
(Defaul=1.0) >1: speed-up, <1: slow-down
Definition: mvsim_node_core.h:161
mvsim::World::TUpdateGUIParams::msg_lines
std::string msg_lines
Messages to show.
Definition: World.h:182
rapidxml::file
Represents data loaded from a file.
Definition: rapidxml_utils.hpp:21
MVSimNode::thread_update_GUI
static void thread_update_GUI(TThreadParams &thread_params)
Definition: mvsim_node.cpp:430
mvsim::VehicleBase::getVehicleIndex
size_t getVehicleIndex() const
Definition: VehicleBase.h:111
MVSimNode::ros_publisher_workers_
mrpt::WorkerThreadsPool ros_publisher_workers_
Definition: mvsim_node_core.h:157
Msg_GPS
sensor_msgs::msg::NavSatFix Msg_GPS
Definition: mvsim_node.cpp:93
mvsim::Wheel::diameter
double diameter
Definition: Wheel.h:46
Msg_TransformStamped
geometry_msgs::msg::TransformStamped Msg_TransformStamped
Definition: mvsim_node.cpp:91
MVSimNode::headless_
bool headless_
Definition: mvsim_node_core.h:163
Msg_LaserScan
sensor_msgs::msg::LaserScan Msg_LaserScan
Definition: mvsim_node.cpp:96
tf2::Transform::getIdentity
static const Transform & getIdentity()
Msg_Twist
geometry_msgs::msg::Twist Msg_Twist
Definition: mvsim_node_core.h:89
MVSimNode::terminateSimulation
void terminateSimulation()
Definition: mvsim_node.cpp:283
rapidxml::file::data
Ch * data()
Definition: rapidxml_utils.hpp:65
Msg_TFMessage
tf2_msgs::msg::TFMessage Msg_TFMessage
Definition: mvsim_node_core.h:95
Msg_Polygon
geometry_msgs::msg::Polygon Msg_Polygon
Definition: mvsim_node_core.h:86
MVSimNode::TPubSubPerVehicle::pub_odom
rclcpp::Publisher< Msg_Odometry >::SharedPtr pub_odom
Publisher of "odom" topic.
Definition: mvsim_node_core.h:240
MVSimNode::thGUI_
std::thread thGUI_
Definition: mvsim_node_core.h:327
MVSimNode::TThreadParams::closing
std::atomic_bool closing
Definition: mvsim_node_core.h:296
mvsim::ControllerBaseInterface::TeleopOutput
Definition: ControllerBase.h:33
mvsim::World::TUpdateGUIParams
Definition: World.h:179
Msg_Odometry
nav_msgs::msg::Odometry Msg_Odometry
Definition: mvsim_node_core.h:92
MVSimNode::TPubSubPerVehicle::pub_collision
rclcpp::Publisher< Msg_Bool >::SharedPtr pub_collision
"<VEH>/collision"
Definition: mvsim_node_core.h:256
mvsim::World::TUpdateGUIParams::keyevent
TGUIKeyEvent keyevent
Keystrokes in the window are returned here.
Definition: World.h:181
Msg_CameraInfo
sensor_msgs::msg::CameraInfo Msg_CameraInfo
Definition: mvsim_node_core.h:97
MVSimNode::myNowSec
double myNowSec() const
Definition: mvsim_node.cpp:553
rapidxml_utils.hpp
MVSimNode::TPubSubPerVehicle::pub_ground_truth
rclcpp::Publisher< Msg_Odometry >::SharedPtr pub_ground_truth
"base_pose_ground_truth" topic
Definition: mvsim_node_core.h:242
MVSimNode::realtime_tictac_
mrpt::system::CTicTac realtime_tictac_
Definition: mvsim_node_core.h:299
MVSimNode::TPubSubPerVehicle::pub_amcl_pose
rclcpp::Publisher< Msg_PoseWithCovarianceStamped >::SharedPtr pub_amcl_pose
"fake_localization" pubs:
Definition: mvsim_node_core.h:245
MVSimNode::WorldPubs::pub_map_ros
rclcpp::Publisher< Msg_OccupancyGrid >::SharedPtr pub_map_ros
used for simul_map publication
Definition: mvsim_node_core.h:202
Msg_PoseArray
geometry_msgs::msg::PoseArray Msg_PoseArray
Definition: mvsim_node_core.h:87
MVSimNode::notifyROSWorldIsUpdated
void notifyROSWorldIsUpdated()
Definition: mvsim_node.cpp:502
MVSimNode::period_ms_publish_tf_
double period_ms_publish_tf_
Definition: mvsim_node_core.h:308
MVSimNode::publish_tf_odom2baselink_
bool publish_tf_odom2baselink_
Definition: mvsim_node_core.h:169
MVSimNode::onROSMsgCmdVel
void onROSMsgCmdVel(Msg_Twist_CSPtr cmd, mvsim::VehicleBase *veh)
Definition: mvsim_node.cpp:768
mvsim::VehicleBase::getWheelInfo
const Wheel & getWheelInfo(const size_t idx) const
Definition: VehicleBase.h:83
MVSimNode::worldPubs_
WorldPubs worldPubs_
Definition: mvsim_node_core.h:207
mvsim::VehicleBase::getNumWheels
size_t getNumWheels() const
Definition: VehicleBase.h:82
Msg_Bool
std_msgs::msg::Bool Msg_Bool
Definition: mvsim_node_core.h:94
mvsim::VehicleBase
Definition: VehicleBase.h:44
Msg_Twist_CSPtr
geometry_msgs::msg::Twist::ConstSharedPtr Msg_Twist_CSPtr
Definition: mvsim_node_core.h:90
MVSimNode::publishVehicles
void publishVehicles(mvsim::VehicleBase &veh)
Definition: mvsim_node.cpp:471
MVSimNode::teleop_idx_veh_
size_t teleop_idx_veh_
for teleoperation from the GUI (selects the focused" vehicle)
Definition: mvsim_node_core.h:323
MVSimNode::tfIdentity_
const tf2::Transform tfIdentity_
Unit transform (const, once)
Definition: mvsim_node_core.h:289
mvsim::Simulable
Definition: Simulable.h:39
MVSimNode::TPubSubPerVehicle::pub_tf_static
rclcpp::Publisher< Msg_TFMessage >::SharedPtr pub_tf_static
Definition: mvsim_node_core.h:260
OccupancyGridMap.h
MVSimNode::initPubSubs
void initPubSubs(TPubSubPerVehicle &out_pubsubs, mvsim::VehicleBase *veh)
Definition: mvsim_node.cpp:564
mvsim::World::TGUIKeyEvent
Definition: World.h:168
Msg_OccupancyGrid
nav_msgs::msg::OccupancyGrid Msg_OccupancyGrid
Definition: mvsim_node_core.h:91
mvsim::ControllerBaseInterface::TeleopInput
Definition: ControllerBase.h:25
MVSimNode::world_init_ok_
bool world_init_ok_
will be true after a success call to loadWorldModel()
Definition: mvsim_node_core.h:304
tf2_geometry_msgs.h
tf2::toMsg
B toMsg(const A &a)
mvsim::ControllerBaseInterface::TeleopInput::js
std::optional< TJoyStickEvent > js
Definition: ControllerBase.h:28
mvsim::ControllerBaseInterface::TeleopInput::keycode
int keycode
Definition: ControllerBase.h:27
MVSimNode::launch_mvsim_server
void launch_mvsim_server()
Definition: mvsim_node.cpp:246
MVSimNode::internalOn
void internalOn(const mvsim::VehicleBase &veh, const mrpt::obs::CObservation2DRangeScan &obs)
Definition: mvsim_node.cpp:1025
Msg_PoseWithCovarianceStamped
geometry_msgs::msg::PoseWithCovarianceStamped Msg_PoseWithCovarianceStamped
Definition: mvsim_node_core.h:88
Msg_Pose
geometry_msgs::msg::Pose Msg_Pose
Definition: mvsim_node.cpp:90
MVSimNode::period_ms_teleop_refresh_
double period_ms_teleop_refresh_
Definition: mvsim_node_core.h:317
MVSimNode::pubsub_vehicles_mtx_
std::mutex pubsub_vehicles_mtx_
Definition: mvsim_node_core.h:269
cmd
string cmd
MVSimNode::TPubSubPerVehicle::pub_chassis_markers
rclcpp::Publisher< Msg_MarkerArray >::SharedPtr pub_chassis_markers
"<VEH>/chassis_markers"
Definition: mvsim_node_core.h:252
Msg_Imu
sensor_msgs::msg::Imu Msg_Imu
Definition: mvsim_node.cpp:95
MVSimNode::msg2gui_
std::string msg2gui_
Definition: mvsim_node_core.h:325
Msg_Header
std_msgs::msg::Header Msg_Header
Definition: mvsim_node.cpp:88
MVSimNode::gui_refresh_period_ms_
int gui_refresh_period_ms_
Definition: mvsim_node_core.h:162
MVSimNode::TPubSubPerVehicle::pub_chassis_shape
rclcpp::Publisher< Msg_Polygon >::SharedPtr pub_chassis_shape
"<VEH>/chassis_shape"
Definition: mvsim_node_core.h:254
config
config
MVSimNode::mvsim_world_
mvsim_node::shared_ptr< mvsim::World > mvsim_world_
Definition: mvsim_node_core.h:155
mvsim::World::TGUIKeyEvent::keycode
int keycode
0=no Key. Otherwise, ASCII code.
Definition: World.h:170
mvsim::WorldElementBase
Definition: WorldElementBase.h:27
t
geometry_msgs::TransformStamped t
MVSimNode::profiler_
mrpt::system::CTimeLogger profiler_
Definition: mvsim_node_core.h:344
MVSimNode::TPubSubPerVehicle::pub_tf
rclcpp::Publisher< Msg_TFMessage >::SharedPtr pub_tf
"<VEH>/tf", "<VEH>/tf_static"
Definition: mvsim_node_core.h:259
MVSimNode::TPubSubPerVehicle::chassis_shape_msg
Msg_MarkerArray chassis_shape_msg
Definition: mvsim_node_core.h:262
ros::NodeHandle
MVSimNode::pubsub_vehicles_
std::vector< TPubSubPerVehicle > pubsub_vehicles_
Definition: mvsim_node_core.h:268
ros::Time::now
static Time now()
MVSimNode::t_old_
double t_old_
Definition: mvsim_node_core.h:301
ImGui::Image
IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2 &size, const ImVec2 &uv0=ImVec2(0, 0), const ImVec2 &uv1=ImVec2(1, 1), const ImVec4 &tint_col=ImVec4(1, 1, 1, 1), const ImVec4 &border_col=ImVec4(0, 0, 0, 0))
Definition: imgui_widgets.cpp:812
MVSimNode::loadWorldModel
void loadWorldModel(const std::string &world_xml_file)
Definition: mvsim_node.cpp:260
MVSimNode::n_
rclcpp::Node::SharedPtr n_
Definition: mvsim_node_core.h:182


mvsim
Author(s):
autogenerated on Wed May 28 2025 02:13:08