22#include "nlohmann/json.hpp"
23#include "pcl/filters/voxel_grid.h"
24#include "yaml-cpp/yaml.h"
26#include "modules/dreamview/proto/point_cloud.pb.h"
37using Json = nlohmann::json;
44 : node_(cyber::CreateNode(
"point_cloud")),
45 websocket_(websocket),
48 simworld_updater_(simworld_updater) {
49 RegisterMessageHandlers();
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_);
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>();
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_);
78void PointCloudUpdater::RegisterMessageHandlers() {
83 response[
"type"] =
"PointCloudStatus";
84 response[
"enabled"] = enabled_;
85 websocket_->
SendData(conn, response.dump());
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_ =
"";
98 boost::shared_lock<boost::shared_mutex> reader_lock(
mutex_);
99 to_send = point_cloud_str_;
106 auto enable = json.find(
"enable");
107 if (enable != json.end() && enable->is_boolean()) {
115 response[
"type"] =
"PointCloudStatus";
116 response[
"enabled"] = enabled_;
123 "GetPointCloudChannel",
125 std::vector<std::string> channels;
126 GetChannelMsg(&channels);
128 response[
"data"][
"name"] =
"GetPointCloudChannelListSuccess";
129 for (
unsigned int i = 0; i < channels.size(); i++) {
130 response[
"data"][
"info"][
"channel"][i] = channels[i];
132 websocket_->
SendData(conn, response.dump());
135 "ChangePointCloudChannel",
137 auto channel_info = json.find(
"data");
139 if (channel_info == json.end()) {
140 AERROR <<
"Cannot retrieve point cloud channel info with unknown "
142 response[
"type"] =
"ChangePointCloudChannelFail";
143 websocket_->
SendData(conn, response.dump());
146 std::string channel =
147 channel_info->dump().substr(1, channel_info->dump().length() - 2);
148 if (ChangeChannel(channel)) {
150 response[
"type"] =
"ChangePointCloudChannelSuccess";
151 websocket_->
SendData(conn, response.dump());
153 response[
"type"] =
"ChangePointCloudChannelFail";
154 websocket_->
SendData(conn, response.dump());
159void PointCloudUpdater::GetChannelMsg(std::vector<std::string> *channels) {
161 auto channelManager =
162 apollo::cyber::service_discovery::TopologyManager::Instance()
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());
176 channels_ = {channels->begin(), channels->end()};
179bool PointCloudUpdater::ChangeChannel(
const std::string &channel) {
180 if (curr_channel_name !=
"") {
181 if (!node_->DeleteReader(curr_channel_name)) {
182 AERROR <<
"delete reader failed";
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);
192 if (point_cloud_reader_ ==
nullptr) {
195 curr_channel_name = channel;
196 bool update_res =
false;
197 update_res = callback_api_(channel);
199 AERROR <<
"update current point cloud channel fail";
206 callback_api_ = callback_api;
208 FLAGS_localization_topic,
209 [
this](
const std::shared_ptr<LocalizationEstimate> &msg) {
210 UpdateLocalizationTime(msg);
217 async_future_.wait();
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;
229 if (point_cloud->width() * point_cloud->height() !=
230 static_cast<unsigned int>(point_cloud->point_size())) {
232 pcl_ptr->height = point_cloud->point_size();
234 pcl_ptr->points.resize(point_cloud->point_size());
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();
245void PointCloudUpdater::UpdatePointCloud(
246 const std::shared_ptr<drivers::PointCloud> &point_cloud) {
250 if (point_cloud->header().has_timestamp_sec()) {
251 last_point_cloud_time_ = point_cloud->header().timestamp_sec();
253 last_point_cloud_time_ = point_cloud->measurement_time();
257 AWARN <<
"skipping outdated point cloud data";
260 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr;
262 if (enable_voxel_filter_) {
264 future_ready_ =
false;
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);
272 pcl_ptr = ConvertPCLPointCloud(point_cloud);
273 this->FilterPointCloud(pcl_ptr);
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>);
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);
295 pcl_filtered_ptr = pcl_ptr;
300 boost::shared_lock<boost::shared_mutex> reader_lock(
mutex_);
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);
313 boost::unique_lock<boost::shared_mutex> writer_lock(
mutex_);
314 point_cloud_pb.SerializeToString(&point_cloud_str_);
315 future_ready_ =
true;
319void PointCloudUpdater::UpdateLocalizationTime(
320 const std::shared_ptr<LocalizationEstimate> &localization) {
321 last_localization_time_ = localization->header().timestamp_sec();
void Start(DvCallback callback_api)
Starts to push PointCloud to frontend.
static boost::shared_mutex mutex_
static float lidar_height_
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...
double LastAdcTimestampSec()
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)
bool PathExists(const std::string &path)
Check if the path exists.