25 namespace carto = ::cartographer;
27 using carto::transform::Rigid3d;
31 const std::string& CheckNoLeadingSlash(
const std::string& frame_id) {
32 if (frame_id.size() > 0) {
33 CHECK_NE(frame_id[0],
'/') <<
"The frame_id " << frame_id
34 <<
" should not start with a /. See 1.7 in " 35 "http://wiki.ros.org/tf2/Migration.";
43 const int num_subdivisions_per_laser_scan,
44 const std::string& tracking_frame,
45 const double lookup_transform_timeout_sec,
tf2_ros::Buffer*
const tf_buffer,
46 carto::mapping::TrajectoryBuilderInterface*
const trajectory_builder)
47 : num_subdivisions_per_laser_scan_(num_subdivisions_per_laser_scan),
48 tf_bridge_(tracking_frame, lookup_transform_timeout_sec, tf_buffer),
49 trajectory_builder_(trajectory_builder) {}
51 std::unique_ptr<carto::sensor::OdometryData> SensorBridge::ToOdometryData(
52 const nav_msgs::Odometry::ConstPtr& msg) {
53 const carto::common::Time
time =
FromRos(msg->header.stamp);
54 const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
55 time, CheckNoLeadingSlash(msg->child_frame_id));
56 if (sensor_to_tracking ==
nullptr) {
59 return carto::common::make_unique<carto::sensor::OdometryData>(
60 carto::sensor::OdometryData{
61 time,
ToRigid3d(msg->pose.pose) * sensor_to_tracking->inverse()});
64 void SensorBridge::HandleOdometryMessage(
65 const std::string& sensor_id,
const nav_msgs::Odometry::ConstPtr& msg) {
66 std::unique_ptr<carto::sensor::OdometryData> odometry_data =
68 if (odometry_data !=
nullptr) {
69 trajectory_builder_->AddSensorData(
71 carto::sensor::OdometryData{odometry_data->time, odometry_data->pose});
75 void SensorBridge::HandleNavSatFixMessage(
76 const std::string& sensor_id,
const sensor_msgs::NavSatFix::ConstPtr& msg) {
77 const carto::common::Time
time =
FromRos(msg->header.stamp);
78 if (msg->status.status == sensor_msgs::NavSatStatus::STATUS_NO_FIX) {
79 trajectory_builder_->AddSensorData(
80 sensor_id, carto::sensor::FixedFramePoseData{
81 time, carto::common::optional<Rigid3d>()});
85 if (!ecef_to_local_frame_.has_value()) {
86 ecef_to_local_frame_ =
88 LOG(INFO) <<
"Using NavSatFix. Setting ecef_to_local_frame with lat = " 89 << msg->latitude <<
", long = " << msg->longitude <<
".";
92 trajectory_builder_->AddSensorData(
94 carto::sensor::FixedFramePoseData{
96 ecef_to_local_frame_.value() *
101 void SensorBridge::HandleLandmarkMessage(
102 const std::string& sensor_id,
103 const cartographer_ros_msgs::LandmarkList::ConstPtr& msg) {
104 trajectory_builder_->AddSensorData(sensor_id,
ToLandmarkData(*msg));
107 std::unique_ptr<carto::sensor::ImuData> SensorBridge::ToImuData(
108 const sensor_msgs::Imu::ConstPtr& msg) {
109 CHECK_NE(msg->linear_acceleration_covariance[0], -1)
110 <<
"Your IMU data claims to not contain linear acceleration measurements " 111 "by setting linear_acceleration_covariance[0] to -1. Cartographer " 112 "requires this data to work. See " 113 "http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html.";
114 CHECK_NE(msg->angular_velocity_covariance[0], -1)
115 <<
"Your IMU data claims to not contain angular velocity measurements " 116 "by setting angular_velocity_covariance[0] to -1. Cartographer " 117 "requires this data to work. See " 118 "http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html.";
120 const carto::common::Time
time =
FromRos(msg->header.stamp);
121 const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
122 time, CheckNoLeadingSlash(msg->header.frame_id));
123 if (sensor_to_tracking ==
nullptr) {
126 CHECK(sensor_to_tracking->translation().norm() < 1e-5)
127 <<
"The IMU frame must be colocated with the tracking frame. " 128 "Transforming linear acceleration into the tracking frame will " 129 "otherwise be imprecise.";
130 return carto::common::make_unique<carto::sensor::ImuData>(
131 carto::sensor::ImuData{
133 sensor_to_tracking->rotation() *
ToEigen(msg->linear_acceleration),
134 sensor_to_tracking->rotation() *
ToEigen(msg->angular_velocity)});
137 void SensorBridge::HandleImuMessage(
const std::string& sensor_id,
138 const sensor_msgs::Imu::ConstPtr& msg) {
139 std::unique_ptr<carto::sensor::ImuData> imu_data = ToImuData(msg);
140 if (imu_data !=
nullptr) {
141 trajectory_builder_->AddSensorData(
143 carto::sensor::ImuData{imu_data->time, imu_data->linear_acceleration,
144 imu_data->angular_velocity});
148 void SensorBridge::HandleLaserScanMessage(
149 const std::string& sensor_id,
const sensor_msgs::LaserScan::ConstPtr& msg) {
150 carto::sensor::PointCloudWithIntensities point_cloud;
151 carto::common::Time
time;
153 HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
156 void SensorBridge::HandleMultiEchoLaserScanMessage(
157 const std::string& sensor_id,
158 const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) {
159 carto::sensor::PointCloudWithIntensities point_cloud;
160 carto::common::Time
time;
162 HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
165 void SensorBridge::HandlePointCloud2Message(
166 const std::string& sensor_id,
167 const sensor_msgs::PointCloud2::ConstPtr& msg) {
168 pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
170 carto::sensor::TimedPointCloud point_cloud;
171 for (
const auto& point : pcl_point_cloud) {
172 point_cloud.emplace_back(point.x, point.y, point.z, 0.f);
174 HandleRangefinder(sensor_id,
FromRos(msg->header.stamp), msg->header.frame_id,
178 const TfBridge& SensorBridge::tf_bridge()
const {
return tf_bridge_; }
180 void SensorBridge::HandleLaserScan(
181 const std::string& sensor_id,
const carto::common::Time time,
182 const std::string& frame_id,
183 const carto::sensor::PointCloudWithIntensities& points) {
184 if (points.points.empty()) {
187 CHECK_LE(points.points.back()[3], 0);
189 for (
int i = 0; i != num_subdivisions_per_laser_scan_; ++i) {
191 points.points.size() * i / num_subdivisions_per_laser_scan_;
193 points.points.size() * (i + 1) / num_subdivisions_per_laser_scan_;
194 carto::sensor::TimedPointCloud subdivision(
195 points.points.begin() + start_index, points.points.begin() + end_index);
196 if (start_index == end_index) {
199 const double time_to_subdivision_end = subdivision.back()[3];
202 const carto::common::Time subdivision_time =
203 time + carto::common::FromSeconds(time_to_subdivision_end);
204 auto it = sensor_to_previous_subdivision_time_.find(sensor_id);
205 if (it != sensor_to_previous_subdivision_time_.end() &&
206 it->second >= subdivision_time) {
207 LOG(WARNING) <<
"Ignored subdivision of a LaserScan message from sensor " 208 << sensor_id <<
" because previous subdivision time " 209 << it->second <<
" is not before current subdivision time " 213 sensor_to_previous_subdivision_time_[sensor_id] = subdivision_time;
214 for (Eigen::Vector4f& point : subdivision) {
215 point[3] -= time_to_subdivision_end;
217 CHECK_EQ(subdivision.back()[3], 0);
218 HandleRangefinder(sensor_id, subdivision_time, frame_id, subdivision);
222 void SensorBridge::HandleRangefinder(
223 const std::string& sensor_id,
const carto::common::Time time,
224 const std::string& frame_id,
const carto::sensor::TimedPointCloud& ranges) {
225 const auto sensor_to_tracking =
226 tf_bridge_.LookupToTracking(time, CheckNoLeadingSlash(frame_id));
227 if (sensor_to_tracking !=
nullptr) {
228 trajectory_builder_->AddSensorData(
229 sensor_id, carto::sensor::TimedPointCloudData{
230 time, sensor_to_tracking->translation().cast<
float>(),
231 carto::sensor::TransformTimedPointCloud(
232 ranges, sensor_to_tracking->cast<
float>())});
Rigid3d ToRigid3d(const geometry_msgs::TransformStamped &transform)
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
SensorBridge(int num_subdivisions_per_laser_scan, const std::string &tracking_frame, double lookup_transform_timeout_sec, tf2_ros::Buffer *tf_buffer, ::cartographer::mapping::TrajectoryBuilderInterface *trajectory_builder)
Eigen::Vector3d ToEigen(const geometry_msgs::Vector3 &vector3)
LandmarkData ToLandmarkData(const LandmarkList &landmark_list)
cartographer::transform::Rigid3d ComputeLocalFrameFromLatLong(const double latitude, const double longitude)
Eigen::Vector3d LatLongAltToEcef(const double latitude, const double longitude, const double altitude)
::cartographer::common::Time FromRos(const ::ros::Time &time)
std::tuple<::cartographer::sensor::PointCloudWithIntensities, ::cartographer::common::Time > ToPointCloudWithIntensities(const sensor_msgs::LaserScan &msg)