29 #include "cartographer/mapping/proto/submap_visualization.pb.h" 38 #include "glog/logging.h" 39 #include "nav_msgs/Odometry.h" 41 #include "sensor_msgs/PointCloud2.h" 43 #include "visualization_msgs/MarkerArray.h" 47 namespace carto = ::cartographer;
49 using carto::transform::Rigid3d;
53 constexpr
int kInfiniteSubscriberQueueSize = 0;
54 constexpr
int kLatestOnlyPublisherQueueSize = 1;
57 bool FromRosMessage(
const cartographer_ros_msgs::TrajectoryOptions& msg,
58 TrajectoryOptions* options) {
59 options->tracking_frame = msg.tracking_frame;
60 options->published_frame = msg.published_frame;
61 options->odom_frame = msg.odom_frame;
62 options->provide_odom_frame = msg.provide_odom_frame;
63 options->use_odometry = msg.use_odometry;
64 options->use_laser_scan = msg.use_laser_scan;
65 options->use_multi_echo_laser_scan = msg.use_multi_echo_laser_scan;
66 options->num_point_clouds = msg.num_point_clouds;
67 if (!options->trajectory_builder_options.ParseFromString(
68 msg.trajectory_builder_options_proto)) {
69 LOG(ERROR) <<
"Failed to parse protobuf";
75 void ShutdownSubscriber(std::unordered_map<int, ::ros::Subscriber>& subscribers,
77 if (subscribers.count(trajectory_id) == 0) {
80 subscribers[trajectory_id].shutdown();
81 LOG(INFO) <<
"Shutdown the subscriber of [" 82 << subscribers[trajectory_id].getTopic() <<
"]";
83 CHECK_EQ(subscribers.erase(trajectory_id), 1);
86 bool IsTopicNameUnique(
88 const std::unordered_map<int, ::ros::Subscriber>& subscribers) {
89 for (
auto& entry : subscribers) {
90 if (entry.second.getTopic() == topic) {
91 LOG(ERROR) <<
"Topic name [" << topic <<
"] is already used.";
101 : node_options_(node_options),
102 map_builder_bridge_(node_options_, tf_buffer) {
103 carto::common::MutexLocker lock(&
mutex_);
145 carto::common::MutexLocker lock(&
mutex_);
158 ::cartographer_ros_msgs::SubmapQuery::Request& request,
159 ::cartographer_ros_msgs::SubmapQuery::Response& response) {
160 carto::common::MutexLocker lock(&
mutex_);
161 return map_builder_bridge_.HandleSubmapQuery(request, response);
165 carto::common::MutexLocker lock(&
mutex_);
170 carto::common::MutexLocker lock(&
mutex_);
171 for (
const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {
172 const auto& trajectory_state = entry.second;
174 geometry_msgs::TransformStamped stamped_transform;
175 stamped_transform.header.stamp =
ToRos(trajectory_state.pose_estimate.time);
177 const auto& tracking_to_local = trajectory_state.pose_estimate.pose;
178 const Rigid3d tracking_to_map =
179 trajectory_state.local_to_map * tracking_to_local;
183 if (trajectory_state.pose_estimate.time !=
186 carto::common::ToUniversal(trajectory_state.pose_estimate.time),
187 trajectory_state.trajectory_options.tracking_frame,
188 carto::sensor::TransformPointCloud(
189 trajectory_state.pose_estimate.point_cloud,
190 tracking_to_local.inverse().cast<
float>())));
198 if (trajectory_state.published_to_tracking !=
nullptr) {
199 if (trajectory_state.trajectory_options.provide_odom_frame) {
200 std::vector<geometry_msgs::TransformStamped> stamped_transforms;
203 stamped_transform.child_frame_id =
204 trajectory_state.trajectory_options.odom_frame;
205 stamped_transform.transform =
207 stamped_transforms.push_back(stamped_transform);
209 stamped_transform.header.frame_id =
210 trajectory_state.trajectory_options.odom_frame;
211 stamped_transform.child_frame_id =
212 trajectory_state.trajectory_options.published_frame;
214 tracking_to_local * (*trajectory_state.published_to_tracking));
215 stamped_transforms.push_back(stamped_transform);
220 stamped_transform.child_frame_id =
221 trajectory_state.trajectory_options.published_frame;
223 tracking_to_map * (*trajectory_state.published_to_tracking));
231 const ::ros::WallTimerEvent& unused_timer_event) {
232 carto::common::MutexLocker lock(&
mutex_);
235 map_builder_bridge_.GetTrajectoryNodesList());
241 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
243 carto::common::MutexLocker lock(&
mutex_);
251 const auto occupancy_grid = map_builder_bridge_.BuildOccupancyGrid();
252 if (occupancy_grid !=
nullptr) {
259 const cartographer_ros_msgs::SensorTopics& topics) {
260 std::unordered_set<string> expected_sensor_ids;
263 expected_sensor_ids.insert(topics.laser_scan_topic);
266 expected_sensor_ids.insert(topics.multi_echo_laser_scan_topic);
270 string topic = topics.point_cloud2_topic;
272 topic +=
"_" + std::to_string(i + 1);
274 expected_sensor_ids.insert(topic);
279 expected_sensor_ids.insert(topics.imu_topic);
282 expected_sensor_ids.insert(topics.odometry_topic);
284 return map_builder_bridge_.AddTrajectory(expected_sensor_ids, options);
288 const cartographer_ros_msgs::SensorTopics& topics,
289 const int trajectory_id) {
291 const string topic = topics.laser_scan_topic;
294 topic, kInfiniteSubscriberQueueSize,
295 boost::function<void(const sensor_msgs::LaserScan::ConstPtr&)>(
296 [
this, trajectory_id,
297 topic](
const sensor_msgs::LaserScan::ConstPtr& msg) {
298 map_builder_bridge_.sensor_bridge(trajectory_id)
299 ->HandleLaserScanMessage(topic, msg);
304 const string topic = topics.multi_echo_laser_scan_topic;
307 topic, kInfiniteSubscriberQueueSize,
308 boost::function<void(
309 const sensor_msgs::MultiEchoLaserScan::ConstPtr&)>(
310 [
this, trajectory_id,
311 topic](
const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) {
312 map_builder_bridge_.sensor_bridge(trajectory_id)
313 ->HandleMultiEchoLaserScanMessage(topic, msg);
317 std::vector<::ros::Subscriber> grouped_point_cloud_subscribers;
320 string topic = topics.point_cloud2_topic;
322 topic +=
"_" + std::to_string(i + 1);
325 topic, kInfiniteSubscriberQueueSize,
326 boost::function<
void(
const sensor_msgs::PointCloud2::ConstPtr&)>(
327 [
this, trajectory_id,
328 topic](
const sensor_msgs::PointCloud2::ConstPtr& msg) {
329 map_builder_bridge_.sensor_bridge(trajectory_id)
330 ->HandlePointCloud2Message(topic, msg);
338 string topic = topics.imu_topic;
340 topic, kInfiniteSubscriberQueueSize,
341 boost::function<void(const sensor_msgs::Imu::ConstPtr&)>(
342 [
this, trajectory_id,
343 topic](
const sensor_msgs::Imu::ConstPtr& msg) {
344 map_builder_bridge_.sensor_bridge(trajectory_id)
345 ->HandleImuMessage(topic, msg);
350 string topic = topics.odometry_topic;
353 topic, kInfiniteSubscriberQueueSize,
354 boost::function<void(const nav_msgs::Odometry::ConstPtr&)>(
355 [
this, trajectory_id,
356 topic](
const nav_msgs::Odometry::ConstPtr& msg) {
357 map_builder_bridge_.sensor_bridge(trajectory_id)
358 ->HandleOdometryMessage(topic, msg);
362 is_active_trajectory_[trajectory_id] =
true;
383 const ::cartographer_ros_msgs::SensorTopics& topics,
388 if (!IsTopicNameUnique(topics.multi_echo_laser_scan_topic,
399 string topic = topics.point_cloud2_topic;
401 for (
auto& subscriber : subscribers.second) {
403 topic +=
"_" + std::to_string(count + 1);
406 if (subscriber.getTopic() == topic) {
407 LOG(ERROR) <<
"Topic name [" << topic <<
"] is already used";
416 ::cartographer_ros_msgs::StartTrajectory::Request& request,
417 ::cartographer_ros_msgs::StartTrajectory::Response& response) {
418 carto::common::MutexLocker lock(&
mutex_);
420 if (!FromRosMessage(request.options, &options) ||
422 LOG(ERROR) <<
"Invalid trajectory options.";
426 LOG(ERROR) <<
"Invalid topics.";
430 const int trajectory_id =
AddTrajectory(options, request.topics);
432 response.trajectory_id = trajectory_id;
434 is_active_trajectory_[trajectory_id] =
true;
439 carto::common::MutexLocker lock(&
mutex_);
440 cartographer_ros_msgs::SensorTopics topics;
449 is_active_trajectory_[trajectory_id] =
true;
453 ::cartographer_ros_msgs::FinishTrajectory::Request& request,
454 ::cartographer_ros_msgs::FinishTrajectory::Response& response) {
455 carto::common::MutexLocker lock(&
mutex_);
456 const int trajectory_id = request.trajectory_id;
457 if (is_active_trajectory_.count(trajectory_id) == 0) {
458 LOG(INFO) <<
"Trajectory_id " << trajectory_id <<
" is not created yet.";
461 if (!is_active_trajectory_[trajectory_id]) {
462 LOG(INFO) <<
"Trajectory_id " << trajectory_id
463 <<
" has already been finished.";
474 LOG(INFO) <<
"Shutdown the subscriber of [" << entry.getTopic() <<
"]";
479 map_builder_bridge_.FinishTrajectory(trajectory_id);
480 is_active_trajectory_[trajectory_id] =
false;
485 ::cartographer_ros_msgs::WriteAssets::Request& request,
486 ::cartographer_ros_msgs::WriteAssets::Response& response) {
487 carto::common::MutexLocker lock(&
mutex_);
488 map_builder_bridge_.SerializeState(request.stem);
489 map_builder_bridge_.WriteAssets(request.stem);
494 carto::common::MutexLocker lock(&
mutex_);
495 for (
const auto& entry : is_active_trajectory_) {
496 const int trajectory_id = entry.first;
498 map_builder_bridge_.FinishTrajectory(trajectory_id);
::ros::Publisher trajectory_nodes_list_publisher_
::cartographer::mapping::proto::MapBuilderOptions map_builder_options
void publish(const boost::shared_ptr< M > &message) const
std::vector<::ros::ServiceServer > service_servers_
void FinishAllTrajectories()
void PublishTrajectoryNodesList(const ::ros::WallTimerEvent &timer_event)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
bool ValidateTopicName(const ::cartographer_ros_msgs::SensorTopics &topics, const TrajectoryOptions &options)
bool ValidateTrajectoryOptions(const TrajectoryOptions &options)
MapBuilderBridge * map_builder_bridge()
void PublishSubmapList(const ::ros::WallTimerEvent &timer_event)
constexpr char kFinishTrajectoryServiceName[]
geometry_msgs::Transform ToGeometryMsgTransform(const Rigid3d &rigid3d)
std::thread occupancy_grid_thread_
constexpr char kImuTopic[]
cartographer::common::Mutex mutex_
const NodeOptions node_options_
constexpr char kTrajectoryNodesListTopic[]
constexpr char kLaserScanTopic[]
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
tf2_ros::TransformBroadcaster tf_broadcaster_
bool HandleStartTrajectory(cartographer_ros_msgs::StartTrajectory::Request &request, cartographer_ros_msgs::StartTrajectory::Response &response)
::ros::Publisher scan_matched_point_cloud_publisher_
void SpinOccupancyGridThreadForever()
::ros::Publisher submap_list_publisher_
bool use_multi_echo_laser_scan
std::unordered_map< int,::ros::Subscriber > odom_subscribers_
::ros::NodeHandle node_handle_
std::vector<::ros::WallTimer > wall_timers_
int AddTrajectory(const TrajectoryOptions &options, const cartographer_ros_msgs::SensorTopics &topics)
constexpr char kScanMatchedPointCloudTopic[]
WallTimer createWallTimer(WallDuration period, void(T::*callback)(const WallTimerEvent &), T *obj, bool oneshot=false, bool autostart=true) const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::unordered_map< int,::ros::Subscriber > imu_subscribers_
constexpr char kStartTrajectoryServiceName[]
bool HandleWriteAssets(cartographer_ros_msgs::WriteAssets::Request &request, cartographer_ros_msgs::WriteAssets::Response &response)
std::unordered_map< int,::ros::Subscriber > laser_scan_subscribers_
constexpr char kOdometryTopic[]
::cartographer::mapping::proto::TrajectoryBuilderOptions trajectory_builder_options
cartographer::common::Time last_scan_matched_point_cloud_time_
sensor_msgs::PointCloud2 ToPointCloud2Message(const int64 timestamp, const string &frame_id, const ::cartographer::sensor::PointCloud &point_cloud)
double trajectory_publish_period_sec
double pose_publish_period_sec
::ros::NodeHandle * node_handle()
constexpr char kOccupancyGridTopic[]
bool HandleFinishTrajectory(cartographer_ros_msgs::FinishTrajectory::Request &request, cartographer_ros_msgs::FinishTrajectory::Response &response)
uint32_t getNumSubscribers() const
void StartTrajectoryWithDefaultTopics(const TrajectoryOptions &options)
constexpr char kWriteAssetsServiceName[]
constexpr char kMultiEchoLaserScanTopic[]
std::unordered_map< int, std::vector<::ros::Subscriber > > point_cloud_subscribers_
::ros::Publisher occupancy_grid_publisher_
constexpr char kSubmapQueryServiceName[]
Node(const NodeOptions &node_options, tf2_ros::Buffer *tf_buffer)
constexpr char kPointCloud2Topic[]
constexpr char kSubmapListTopic[]
std::unordered_map< int,::ros::Subscriber > multi_echo_laser_scan_subscribers_
bool HandleSubmapQuery(cartographer_ros_msgs::SubmapQuery::Request &request, cartographer_ros_msgs::SubmapQuery::Response &response)
void LaunchSubscribers(const TrajectoryOptions &options, const cartographer_ros_msgs::SensorTopics &topics, int trajectory_id)
::ros::Time ToRos(::cartographer::common::Time time)
void PublishTrajectoryStates(const ::ros::WallTimerEvent &timer_event)
double submap_publish_period_sec