Apollo 10.0
自动驾驶开放平台
point_cloud_updater.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 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 <utility>
20#include <vector>
21
22#include "nlohmann/json.hpp"
23#include "pcl/filters/voxel_grid.h"
24#include "yaml-cpp/yaml.h"
25
26#include "modules/dreamview/proto/point_cloud.pb.h"
27
28#include "cyber/common/file.h"
29#include "cyber/common/log.h"
30#include "cyber/time/clock.h"
33namespace apollo {
34namespace dreamview {
35
37using Json = nlohmann::json;
38
39float PointCloudUpdater::lidar_height_ = kDefaultLidarHeight;
40boost::shared_mutex PointCloudUpdater::mutex_;
41
43 SimulationWorldUpdater *simworld_updater)
44 : node_(cyber::CreateNode("point_cloud")),
45 websocket_(websocket),
46 point_cloud_str_(""),
47 future_ready_(true),
48 simworld_updater_(simworld_updater) {
49 RegisterMessageHandlers();
50}
51
53
54void PointCloudUpdater::LoadLidarHeight(const std::string &file_path) {
55 if (!cyber::common::PathExists(file_path)) {
56 AWARN << "No such file: " << FLAGS_lidar_height_yaml
57 << ". Using default lidar height:" << kDefaultLidarHeight;
58 boost::unique_lock<boost::shared_mutex> writer_lock(mutex_);
59 lidar_height_ = kDefaultLidarHeight;
60 return;
61 }
62
63 YAML::Node config = YAML::LoadFile(file_path);
64 if (config["vehicle"] && config["vehicle"]["parameters"]) {
65 boost::unique_lock<boost::shared_mutex> writer_lock(mutex_);
66 lidar_height_ = config["vehicle"]["parameters"]["height"].as<float>();
67 AINFO << "Lidar height is updated to " << lidar_height_;
68 return;
69 }
70
71 AWARN << "Fail to load the lidar height yaml file: "
72 << FLAGS_lidar_height_yaml
73 << ". Using default lidar height:" << kDefaultLidarHeight;
74 boost::unique_lock<boost::shared_mutex> writer_lock(mutex_);
75 lidar_height_ = kDefaultLidarHeight;
76}
77
78void PointCloudUpdater::RegisterMessageHandlers() {
79 // Send current point_cloud status to the new client.
81 [this](WebSocketHandler::Connection *conn) {
82 Json response;
83 response["type"] = "PointCloudStatus";
84 response["enabled"] = enabled_;
85 websocket_->SendData(conn, response.dump());
86 });
87 websocket_->RegisterMessageHandler(
88 "RequestPointCloud",
89 [this](const Json &json, WebSocketHandler::Connection *conn) {
90 std::string to_send;
91 // If there is no point_cloud data for more than 2 seconds, reset.
92 if (point_cloud_str_ != "" &&
93 std::fabs(last_localization_time_ - last_point_cloud_time_) > 2.0) {
94 boost::unique_lock<boost::shared_mutex> writer_lock(mutex_);
95 point_cloud_str_ = "";
96 }
97 {
98 boost::shared_lock<boost::shared_mutex> reader_lock(mutex_);
99 to_send = point_cloud_str_;
100 }
101 websocket_->SendBinaryData(conn, to_send, true);
102 });
103 websocket_->RegisterMessageHandler(
104 "TogglePointCloud",
105 [this](const Json &json, WebSocketHandler::Connection *conn) {
106 auto enable = json.find("enable");
107 if (enable != json.end() && enable->is_boolean()) {
108 if (*enable) {
109 enabled_ = true;
110 } else {
111 enabled_ = false;
112 }
113 if (websocket_) {
114 Json response;
115 response["type"] = "PointCloudStatus";
116 response["enabled"] = enabled_;
117 // Sync the point_cloud status across all the clients.
118 websocket_->BroadcastData(response.dump());
119 }
120 }
121 });
122 websocket_->RegisterMessageHandler(
123 "GetPointCloudChannel",
124 [this](const Json &json, WebSocketHandler::Connection *conn) {
125 std::vector<std::string> channels;
126 GetChannelMsg(&channels);
127 Json response({});
128 response["data"]["name"] = "GetPointCloudChannelListSuccess";
129 for (unsigned int i = 0; i < channels.size(); i++) {
130 response["data"]["info"]["channel"][i] = channels[i];
131 }
132 websocket_->SendData(conn, response.dump());
133 });
134 websocket_->RegisterMessageHandler(
135 "ChangePointCloudChannel",
136 [this](const Json &json, WebSocketHandler::Connection *conn) {
137 auto channel_info = json.find("data");
138 Json response({});
139 if (channel_info == json.end()) {
140 AERROR << "Cannot retrieve point cloud channel info with unknown "
141 "channel.";
142 response["type"] = "ChangePointCloudChannelFail";
143 websocket_->SendData(conn, response.dump());
144 return;
145 }
146 std::string channel =
147 channel_info->dump().substr(1, channel_info->dump().length() - 2);
148 if (ChangeChannel(channel)) {
149 Json response({});
150 response["type"] = "ChangePointCloudChannelSuccess";
151 websocket_->SendData(conn, response.dump());
152 } else {
153 response["type"] = "ChangePointCloudChannelFail";
154 websocket_->SendData(conn, response.dump());
155 }
156 });
157}
158
159void PointCloudUpdater::GetChannelMsg(std::vector<std::string> *channels) {
160 enabled_ = true;
161 auto channelManager =
162 apollo::cyber::service_discovery::TopologyManager::Instance()
163 ->channel_manager();
164 std::vector<apollo::cyber::proto::RoleAttributes> role_attr_vec;
165 channelManager->GetWriters(&role_attr_vec);
166 for (auto &role_attr : role_attr_vec) {
167 std::string messageType;
168 messageType = role_attr.message_type();
169 int index = messageType.rfind("PointCloud");
170 int index_sensor = role_attr.channel_name().rfind("sensor");
171 if (index != -1 && index_sensor != -1) {
172 channels->push_back(role_attr.channel_name());
173 }
174 }
175 channels_.clear();
176 channels_ = {channels->begin(), channels->end()};
177}
178
179bool PointCloudUpdater::ChangeChannel(const std::string &channel) {
180 if (curr_channel_name != "") {
181 if (!node_->DeleteReader(curr_channel_name)) {
182 AERROR << "delete reader failed";
183 return false;
184 }
185 }
186 point_cloud_reader_.reset();
187 point_cloud_reader_ = node_->CreateReader<drivers::PointCloud>(
188 channel, [this](const std::shared_ptr<drivers::PointCloud> &msg) {
189 UpdatePointCloud(msg);
190 });
191
192 if (point_cloud_reader_ == nullptr) {
193 return false;
194 }
195 curr_channel_name = channel;
196 bool update_res = false;
197 update_res = callback_api_(channel);
198 if (!update_res) {
199 AERROR << "update current point cloud channel fail";
200 return false;
201 }
202 return true;
203}
204
206 callback_api_ = callback_api;
207 localization_reader_ = node_->CreateReader<LocalizationEstimate>(
208 FLAGS_localization_topic,
209 [this](const std::shared_ptr<LocalizationEstimate> &msg) {
210 UpdateLocalizationTime(msg);
211 });
212 LoadLidarHeight(FLAGS_lidar_height_yaml);
213}
214
216 if (enabled_) {
217 async_future_.wait();
218 }
219}
220
221pcl::PointCloud<pcl::PointXYZ>::Ptr PointCloudUpdater::ConvertPCLPointCloud(
222 const std::shared_ptr<drivers::PointCloud> &point_cloud) {
223 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr(
224 new pcl::PointCloud<pcl::PointXYZ>);
225 pcl_ptr->width = point_cloud->width();
226 pcl_ptr->height = point_cloud->height();
227 pcl_ptr->is_dense = false;
228
229 if (point_cloud->width() * point_cloud->height() !=
230 static_cast<unsigned int>(point_cloud->point_size())) {
231 pcl_ptr->width = 1;
232 pcl_ptr->height = point_cloud->point_size();
233 }
234 pcl_ptr->points.resize(point_cloud->point_size());
235
236 for (size_t i = 0; i < pcl_ptr->points.size(); ++i) {
237 const auto &point = point_cloud->point(static_cast<int>(i));
238 pcl_ptr->points[i].x = point.x();
239 pcl_ptr->points[i].y = point.y();
240 pcl_ptr->points[i].z = point.z();
241 }
242 return pcl_ptr;
243}
244
245void PointCloudUpdater::UpdatePointCloud(
246 const std::shared_ptr<drivers::PointCloud> &point_cloud) {
247 if (!enabled_) {
248 return;
249 }
250 if (point_cloud->header().has_timestamp_sec()) {
251 last_point_cloud_time_ = point_cloud->header().timestamp_sec();
252 } else {
253 last_point_cloud_time_ = point_cloud->measurement_time();
254 }
255 if (simworld_updater_->LastAdcTimestampSec() == 0.0 ||
256 simworld_updater_->LastAdcTimestampSec() - last_point_cloud_time_ > 0.1) {
257 AWARN << "skipping outdated point cloud data";
258 return;
259 }
260 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr;
261 // Check if last filter process has finished before processing new data.
262 if (enable_voxel_filter_) {
263 if (future_ready_) {
264 future_ready_ = false;
265 // transform from drivers::PointCloud to pcl::PointCloud
266 pcl_ptr = ConvertPCLPointCloud(point_cloud);
267 std::future<void> f =
268 cyber::Async(&PointCloudUpdater::FilterPointCloud, this, pcl_ptr);
269 async_future_ = std::move(f);
270 }
271 } else {
272 pcl_ptr = ConvertPCLPointCloud(point_cloud);
273 this->FilterPointCloud(pcl_ptr);
274 }
275}
276
277void PointCloudUpdater::FilterPointCloud(
278 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr) {
279 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_filtered_ptr(
280 new pcl::PointCloud<pcl::PointXYZ>);
281
282 /*
283 By default, disable voxel filter since it's taking more than 500ms
284 ideally the most efficient sampling method is to
285 use per beam random sample for organized cloud(TODO)
286 */
287 if (enable_voxel_filter_) {
288 pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;
289 voxel_grid.setInputCloud(pcl_ptr);
290 voxel_grid.setLeafSize(static_cast<float>(FLAGS_voxel_filter_size),
291 static_cast<float>(FLAGS_voxel_filter_size),
292 static_cast<float>(FLAGS_voxel_filter_height));
293 voxel_grid.filter(*pcl_filtered_ptr);
294 } else {
295 pcl_filtered_ptr = pcl_ptr;
296 }
297
298 float z_offset;
299 {
300 boost::shared_lock<boost::shared_mutex> reader_lock(mutex_);
301 z_offset = lidar_height_;
302 }
303 apollo::dreamview::PointCloud point_cloud_pb;
304 for (size_t idx = 0; idx < pcl_filtered_ptr->size(); ++idx) {
305 pcl::PointXYZ &pt = pcl_filtered_ptr->points[idx];
306 if (!std::isnan(pt.x) && !std::isnan(pt.y) && !std::isnan(pt.z)) {
307 point_cloud_pb.add_num(pt.x);
308 point_cloud_pb.add_num(pt.y);
309 point_cloud_pb.add_num(pt.z + z_offset);
310 }
311 }
312 {
313 boost::unique_lock<boost::shared_mutex> writer_lock(mutex_);
314 point_cloud_pb.SerializeToString(&point_cloud_str_);
315 future_ready_ = true;
316 }
317}
318
319void PointCloudUpdater::UpdateLocalizationTime(
320 const std::shared_ptr<LocalizationEstimate> &localization) {
321 last_localization_time_ = localization->header().timestamp_sec();
322}
323} // namespace dreamview
324} // namespace apollo
double f
void Start(DvCallback callback_api)
Starts to push PointCloud to frontend.
PointCloudUpdater(WebSocketHandler *websocket, SimulationWorldUpdater *sim_world_updater)
Constructor with the websocket handler.
std::function< bool(const std::string &string)> DvCallback
static void LoadLidarHeight(const std::string &file_path)
A wrapper around SimulationWorldService and WebSocketHandler to keep pushing SimulationWorld to front...
The WebSocketHandler, built on top of CivetWebSocketHandler, is a websocket handler that handles diff...
bool BroadcastData(const std::string &data, bool skippable=false)
Sends the provided data to all the connected clients.
void RegisterMessageHandler(std::string type, MessageHandler handler)
Add a new message handler for a message type.
bool SendData(Connection *conn, const std::string &data, bool skippable=false, int op_code=MG_WEBSOCKET_OPCODE_TEXT)
Sends the provided data to a specific connected client.
void RegisterConnectionReadyHandler(ConnectionReadyHandler handler)
Add a new handler for new connections.
bool SendBinaryData(Connection *conn, const std::string &data, bool skippable=false)
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
nlohmann::json Json
bool PathExists(const std::string &path)
Check if the path exists.
Definition file.cc:195
class register implement
Definition arena_queue.h:37