Apollo 11.0
自动驾驶开放平台
msg_exporter.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2019 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
18
19#include <fstream>
20#include <memory>
21#include <vector>
22
23#include <opencv2/opencv.hpp>
24
25#include "absl/strings/str_split.h"
26#include "pcl/io/pcd_io.h"
27
28#include "cyber/common/file.h"
29#include "cyber/common/log.h"
30#include "modules/common_msgs/transform_msgs/transform.pb.h"
31
32namespace apollo {
33namespace perception {
34namespace msg {
35
36MsgExporter::MsgExporter(std::shared_ptr<apollo::cyber::Node> node,
37 const std::vector<std::string> channels,
38 const std::vector<std::string> child_frame_ids) {
39 _cyber_node = node;
40 _channels = channels;
41 _child_frame_ids = child_frame_ids;
42 // _localization_method = "perception_localization_100hz";
43 _localization_method = "novatel";
44 auto create_folder = [](const std::string& folder) {
47 }
49 };
50 // bind channels
51 for (std::size_t i = 0; i < _channels.size(); ++i) {
52 const auto& channel = _channels[i];
53 const auto& child_frame_id = _child_frame_ids[i];
54 std::string folder = TransformChannelToFolder(channel);
55 if (IsLidar(channel)) {
56 _cyber_node->CreateReader<PcMsg>(
57 channel,
58 std::bind(&MsgExporter::PointCloudMessageHandler, this,
59 std::placeholders::_1, channel, child_frame_id, folder));
60 create_folder(folder);
61 std::cout << "Bind lidar channel: " << channel << std::endl;
62 } else if (IsCamera(channel)) {
63 _cyber_node->CreateReader<ImgMsg>(
64 channel,
65 std::bind(&MsgExporter::ImageMessageHandler, this,
66 std::placeholders::_1, channel, child_frame_id, folder));
67 create_folder(folder);
68 std::cout << "Bind camera channel: " << channel << std::endl;
69 } else {
70 std::cout << "Unknown channel type: " << channel << std::endl;
71 }
72 }
73}
74
75void MsgExporter::ImageMessageHandler(
76 const std::shared_ptr<const ImgMsg>& img_msg, const std::string& channel,
77 const std::string& child_frame_id, const std::string& folder) {
78 double timestamp = img_msg->measurement_time();
79 // std::cout << "Receive image message from channel: " << channel <<
80 // " at time: " << FORMAT_TIMESTAMP(timestamp) << std::endl;
81 const unsigned char* data =
82 reinterpret_cast<const unsigned char*>(img_msg->data().data());
83 const unsigned char* range_data = nullptr;
84 // const unsigned char* range_data = is_stereo_camera(channel) ?
85 // reinterpret_cast<const unsigned char*>(img_msg->disparity_data().data())
86 // : nullptr;
87 std::size_t width = img_msg->width();
88 std::size_t height = img_msg->height();
89 Eigen::Matrix4d pose;
90 if (QuerySensorToWorldPose(timestamp, child_frame_id, &pose)) {
91 SaveImage(data, range_data, width, height, timestamp, folder);
92 SavePose(pose, timestamp, folder);
93 } else {
94 std::cout << "Failed to query camera pose, child_frame_id "
95 << child_frame_id << std::endl;
96 }
97}
98
99void MsgExporter::PointCloudMessageHandler(
100 const std::shared_ptr<const PcMsg>& cloud_msg, const std::string& channel,
101 const std::string& child_frame_id, const std::string& folder) {
102 double timestamp = cloud_msg->measurement_time();
103 // std::cout << "Receive point cloud message from channel: " << channel <<
104 // " at time: " << timestamp << std::endl;
105 pcl::PointCloud<PCLPointXYZIT> cloud;
106 if (cloud_msg->point_size() > 0) {
107 cloud.resize(cloud_msg->point_size());
108 for (int i = 0; i < cloud_msg->point_size(); ++i) {
109 const apollo::drivers::PointXYZIT& pt = cloud_msg->point(i);
110 if (std::isnan(pt.x()) || std::isnan(pt.y()) || std::isnan(pt.z())) {
111 continue;
112 }
113 if (std::fabs(pt.x()) > 1e3 || std::fabs(pt.y()) > 1e3 ||
114 std::fabs(pt.z()) > 1e3) {
115 continue;
116 }
117 cloud[i].x = pt.x();
118 cloud[i].y = pt.y();
119 cloud[i].z = pt.z();
120 cloud[i].intensity = static_cast<unsigned char>(pt.intensity());
121 cloud[i].timestamp = static_cast<double>(pt.timestamp()) * 1e-9;
122 }
123 }
124 Eigen::Matrix4d pose;
125 if (QuerySensorToWorldPose(timestamp, child_frame_id, &pose)) {
126 SavePointCloud(cloud, timestamp, folder);
127 SavePose(pose, timestamp, folder);
128 } else {
129 std::cout << "Failed to query lidar pose, child_frame_id " << child_frame_id
130 << " but still export pointcloud." << std::endl;
131 SavePointCloud(cloud, timestamp, folder);
132 }
133}
134
135bool MsgExporter::SavePointCloud(
136 const pcl::PointCloud<PCLPointXYZIT>& point_cloud, double timestamp,
137 const std::string& folder) {
138 static char path[500];
139 snprintf(path, sizeof(path), "%s/%.6f.pcd", folder.c_str(), timestamp);
140 pcl::PCDWriter writer;
141 writer.writeBinaryCompressed(path, point_cloud);
142 return true;
143}
144
145bool MsgExporter::SaveImage(const unsigned char* color_image,
146 const unsigned char* range_image, std::size_t width,
147 std::size_t height, double timestamp,
148 const std::string& folder) {
149 cv::Mat image(static_cast<int>(height), static_cast<int>(width), CV_8UC3,
150 const_cast<unsigned char*>(color_image));
151 cv::Mat out(static_cast<int>(height), static_cast<int>(width), CV_8UC3);
152 for (size_t y = 0; y < height; y++) {
153 for (size_t x = 0; x < width; x++) {
154 out.at<cv::Vec3b>(y, x)[0] = image.at<cv::Vec3b>(y, x)[2]; // R -> B
155 out.at<cv::Vec3b>(y, x)[2] = image.at<cv::Vec3b>(y, x)[0]; // B -> R
156 out.at<cv::Vec3b>(y, x)[1] = image.at<cv::Vec3b>(y, x)[1]; // G -> G
157 }
158 }
159 static char path[500];
160 snprintf(path, sizeof(path), "%s/%.6f.jpg", folder.c_str(), timestamp);
161 cv::imwrite(path, out);
162 if (range_image != nullptr) {
163 snprintf(path, sizeof(path), "%s/%.6f.data", folder.c_str(), timestamp);
164 std::ofstream fout(path, std::ofstream::binary);
165 if (fout.is_open()) {
166 fout.write(reinterpret_cast<const char*>(range_image), width * height);
167 fout.close();
168 }
169 // cv::Mat range_image_mat(height, width, CV_8UC1, const_cast<unsigned
170 // char*>(range_image));
171 // snprintf(path, 500, "%s/%.6f_range_snapshot.jpg", folder.c_str(),
172 // timestamp);
173 // cv::imwrite(path, range_image_mat);
174 }
175 return true;
176}
177
178bool MsgExporter::QuerySensorToWorldPose(double timestamp,
179 const std::string& child_frame_id,
180 Eigen::Matrix4d* pose) {
181 Eigen::Matrix4d sensor2novatel_extrinsics = Eigen::Matrix4d::Identity();
182 Eigen::Matrix4d novatel2world_pose;
183 // query sensor to novatel extrinsics
184 if (child_frame_id != "null" &&
185 !QueryPose(timestamp, "novatel", child_frame_id,
186 &sensor2novatel_extrinsics)) {
187 return false;
188 }
189
190 // query novatel to world pose
191 if (!QueryPose(timestamp, "world", _localization_method,
192 &novatel2world_pose)) {
193 return false;
194 }
195 *pose = novatel2world_pose * sensor2novatel_extrinsics;
196 return true;
197}
198
199bool MsgExporter::QueryPose(double timestamp, const std::string& frame_id,
200 const std::string& child_frame_id,
201 Eigen::Matrix4d* pose) {
202 cyber::Time query_time(timestamp);
203 std::string err_string;
204 if (!tf2_buffer_->canTransform(frame_id, child_frame_id, query_time, 0.05f,
205 &err_string)) {
206 AERROR << "Can not find transform. " //<< FORMAT_TIMESTAMP(timestamp)
207 << " frame_id: " << frame_id << " child_frame_id: " << child_frame_id
208 << " Error info: " << err_string;
209 return false;
210 }
211 apollo::transform::TransformStamped stamped_transform;
212 try {
213 stamped_transform =
214 tf2_buffer_->lookupTransform(frame_id, child_frame_id, query_time);
215 Eigen::Translation3d translation(
216 stamped_transform.transform().translation().x(),
217 stamped_transform.transform().translation().y(),
218 stamped_transform.transform().translation().z());
219 Eigen::Quaterniond rotation(stamped_transform.transform().rotation().qw(),
220 stamped_transform.transform().rotation().qx(),
221 stamped_transform.transform().rotation().qy(),
222 stamped_transform.transform().rotation().qz());
223 *pose = (translation * rotation).matrix();
224 } catch (tf2::TransformException& ex) {
225 AERROR << ex.what();
226 return false;
227 }
228 return true;
229}
230
231bool MsgExporter::SavePose(const Eigen::Matrix4d& pose, double timestamp,
232 const std::string& folder) {
233 Eigen::Affine3d affine(pose);
234 Eigen::Quaterniond quat = (Eigen::Quaterniond)affine.linear();
235 static char path[500];
236 snprintf(path, sizeof(path), "%s/%.6f.pose", folder.c_str(), timestamp);
237 std::ofstream fout(path);
238 if (fout.is_open()) {
239 fout << 0 << " " << std::setprecision(14) << timestamp << " "
240 << affine.translation().transpose() << " " << quat.x() << " "
241 << quat.y() << " " << quat.z() << " " << quat.w() << std::endl;
242 fout.close();
243 }
244 return true;
245}
246
247bool MsgExporter::IsStereoCamera(const std::string& channel) {
248 const std::vector<std::string> strs = absl::StrSplit(channel, '/');
249 return strs.size() > 2 && strs[2] == "smartereye";
250}
251
252bool MsgExporter::IsCamera(const std::string& channel) {
253 const std::vector<std::string> strs = absl::StrSplit(channel, '/');
254 return strs.size() > 1 && strs.back() == "image";
255}
256
257bool MsgExporter::IsLidar(const std::string& channel) {
258 const std::vector<std::string> strs = absl::StrSplit(channel, '/');
259 return strs.size() > 0 && strs.back() == "PointCloud2";
260}
261
262std::string MsgExporter::TransformChannelToFolder(const std::string& channel) {
263 const std::vector<std::string> strs = absl::StrSplit(channel, '/');
264 std::string target = "";
265 for (auto& str : strs) {
266 target += str + "_";
267 }
268 target += "data";
269 return target;
270}
271
272} // namespace msg
273} // namespace perception
274} // namespace apollo
MsgExporter(std::shared_ptr< apollo::cyber::Node > node, const std::vector< std::string > channels, const std::vector< std::string > child_frame_ids)
virtual bool canTransform(const std::string &target_frame, const std::string &source_frame, const cyber::Time &target_time, const float timeout_second=0.01f, std::string *errstr=nullptr) const
Test if a transform is possible
Definition buffer.cc:195
virtual TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const cyber::Time &time, const float timeout_second=0.01f) const
Get the transform between two frames by frame ID.
Definition buffer.cc:168
#define AERROR
Definition log.h:44
bool DeleteFile(const string &filename)
Definition file.cc:495
bool CreateDir(const string &dir)
Definition file.cc:538
bool DirectoryExists(const std::string &directory_path)
Check if the directory specified by directory_path exists and is indeed a directory.
Definition file.cc:207
std::string frame_id
Point cloud frame id
uint32_t height
Height of point cloud
uint32_t width
Width of point cloud
class register implement
Definition arena_queue.h:37
optional apollo::common::Quaternion rotation
optional apollo::common::Point3D translation