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"
38using Json = nlohmann::json;
43 : node_(cyber::CreateNode(
"point_cloud")),
44 websocket_(websocket) {
46 FLAGS_localization_topic,
47 [
this](
const std::shared_ptr<LocalizationEstimate> &msg) {
48 UpdateLocalizationTime(msg);
54PointCloudChannelUpdater* PointCloudUpdater::GetPointCloudChannelUpdater(
55 const std::string &channel_name) {
56 std::lock_guard<std::mutex> lck(channel_updater_map_mutex_);
57 if (channel_updaters_.find(channel_name) == channel_updaters_.end()) {
58 channel_updaters_[channel_name] =
59 new PointCloudChannelUpdater(channel_name);
60 channel_updaters_[channel_name]->point_cloud_reader_ =
62 channel_name, [channel_name,
this](
63 const std::shared_ptr<drivers::PointCloud> &msg) {
64 UpdatePointCloud(msg, channel_name);
67 return channel_updaters_[channel_name];
71 const std::string &channel_name,
72 nlohmann::json *subscribe_param) {
73 if (channel_name.empty()) {
74 AERROR <<
"Failed to subscribe channel for channel is empty!";
77 if (std::find(channels_.begin(), channels_.end(), channel_name) ==
79 AERROR <<
"Failed to subscribe point cloud updater, for channel: "
80 << channel_name <<
" is invalid.";
83 if (time_interval_ms > 0) {
85 GetPointCloudChannelUpdater(channel_name);
88 [channel_name,
this]() { this->
OnTimer(channel_name); },
false));
99 if (channel_name.empty()) {
100 AERROR <<
"Failed to unsubscribe channel for channel is empty!";
124 boost::unique_lock<boost::shared_mutex> writer_lock(
mutex_);
128 boost::shared_lock<boost::shared_mutex> reader_lock(
mutex_);
132 StreamData stream_data;
133 std::string stream_data_string;
134 stream_data.set_action(
"stream");
135 stream_data.set_data_name(
"pointcloud");
136 stream_data.set_channel_name(channel_name);
137 std::vector<uint8_t> byte_data(to_send.begin(), to_send.end());
138 stream_data.set_data(&(byte_data[0]), byte_data.size());
139 stream_data.set_type(
"pointcloud");
140 stream_data.SerializeToString(&stream_data_string);
144void PointCloudUpdater::GetChannelMsg(std::vector<std::string> *channels) {
149pcl::PointCloud<pcl::PointXYZ>::Ptr PointCloudUpdater::ConvertPCLPointCloud(
150 const std::shared_ptr<drivers::PointCloud> &point_cloud,
151 const std::string &channel_name) {
152 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr(
153 new pcl::PointCloud<pcl::PointXYZ>);
154 pcl_ptr->width = point_cloud->width();
155 pcl_ptr->height = point_cloud->height();
156 pcl_ptr->is_dense =
false;
158 if (point_cloud->width() * point_cloud->height() !=
159 static_cast<unsigned int>(point_cloud->point_size())) {
161 pcl_ptr->height = point_cloud->point_size();
163 pcl_ptr->points.resize(point_cloud->point_size());
165 for (
size_t i = 0; i < pcl_ptr->points.size(); ++i) {
166 const auto &point = point_cloud->point(
static_cast<int>(i));
167 pcl_ptr->points[i].x = point.x();
168 pcl_ptr->points[i].y = point.y();
169 pcl_ptr->points[i].z = point.z();
172 TransformPointCloud(pcl_ptr, point_cloud->header().frame_id());
177void PointCloudUpdater::UpdatePointCloud(
178 const std::shared_ptr<drivers::PointCloud> &point_cloud,
179 const std::string& channel_name) {
180 PointCloudChannelUpdater *updater = GetPointCloudChannelUpdater(channel_name);
181 if (point_cloud->header().has_timestamp_sec()) {
182 updater->last_point_cloud_time_ = point_cloud->header().timestamp_sec();
184 updater->last_point_cloud_time_ = point_cloud->measurement_time();
186 if (std::fabs(last_localization_time_ - updater->last_point_cloud_time_) >
188 AWARN <<
"skipping outdated point cloud data";
191 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr;
193 if (enable_voxel_filter_) {
194 if (updater->future_ready_) {
195 updater->future_ready_ =
false;
197 pcl_ptr = ConvertPCLPointCloud(point_cloud, channel_name);
198 std::future<void>
f = cyber::Async(&PointCloudUpdater::FilterPointCloud,
199 this, pcl_ptr, channel_name);
200 updater->async_future_ = std::move(
f);
203 pcl_ptr = ConvertPCLPointCloud(point_cloud, channel_name);
204 this->FilterPointCloud(pcl_ptr, channel_name);
208void PointCloudUpdater::FilterPointCloud(
209 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_ptr,
210 const std::string &channel_name) {
211 PointCloudChannelUpdater *updater = GetPointCloudChannelUpdater(channel_name);
212 pcl::PointCloud<pcl::PointXYZ>::Ptr pcl_filtered_ptr(
213 new pcl::PointCloud<pcl::PointXYZ>);
220 if (enable_voxel_filter_) {
221 pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;
222 voxel_grid.setInputCloud(pcl_ptr);
223 voxel_grid.setLeafSize(
static_cast<float>(FLAGS_voxel_filter_size),
224 static_cast<float>(FLAGS_voxel_filter_size),
225 static_cast<float>(FLAGS_voxel_filter_height));
226 voxel_grid.filter(*pcl_filtered_ptr);
228 pcl_filtered_ptr = pcl_ptr;
232 for (
size_t idx = 0; idx < pcl_filtered_ptr->size(); ++idx) {
233 pcl::PointXYZ &pt = pcl_filtered_ptr->points[idx];
234 if (!std::isnan(pt.x) && !std::isnan(pt.y) && !std::isnan(pt.z)) {
235 point_cloud_pb.add_num(pt.x);
236 point_cloud_pb.add_num(pt.y);
237 point_cloud_pb.add_num(pt.z);
241 boost::unique_lock<boost::shared_mutex> writer_lock(
mutex_);
242 point_cloud_pb.SerializeToString(&updater->point_cloud_str_);
243 updater->future_ready_ =
true;
247void PointCloudUpdater::UpdateLocalizationTime(
248 const std::shared_ptr<LocalizationEstimate> &localization) {
249 last_localization_time_ = localization->header().timestamp_sec();
252void PointCloudUpdater::TransformPointCloud(
253 pcl::PointCloud<pcl::PointXYZ>::Ptr &point_cloud,
254 const std::string &frame_id) {
256 AERROR <<
"Failed to get frame id";
262 stamped_transform = tf2_buffer->
lookupTransform(
"localization", frame_id,
264 }
catch (tf2::TransformException &ex) {
270 auto &matrix = stamped_transform.
transform();
273 Eigen::Vector3f translation(
274 matrix.translation().x(), matrix.translation().y(),
275 matrix.translation().z());
278 Eigen::Quaternionf rotation(
279 matrix.rotation().qw(), matrix.rotation().qx(), matrix.rotation().qy(),
280 matrix.rotation().qz());
281 rotation.normalize();
284 Eigen::Affine3f transform = Eigen::Affine3f::Identity();
285 transform.translation() << translation;
286 transform.rotate(rotation);
289 pcl::transformPointCloud(*point_cloud, *point_cloud, transform);
292 Eigen::Matrix4f counter_transform = Eigen::Matrix4f::Identity();
293 counter_transform(0, 0) = 0;
294 counter_transform(0, 1) = 1;
295 counter_transform(1, 0) = -1;
296 counter_transform(1, 1) = 0;
299 pcl::transformPointCloud(*point_cloud, *point_cloud, counter_transform);
Cyber has builtin time type Time.
Used to perform oneshot or periodic timing tasks
void StopStream(const std::string &channel_name="") override
Stop data flow.
static boost::shared_mutex mutex_
void StartStream(const double &time_interval_ms, const std::string &channel_name="", nlohmann::json *subscribe_param=nullptr) override
Start data flow.
PointCloudUpdater(WebSocketHandler *websocket, SimulationWorldUpdater *sim_world_updater)
Constructor with the websocket handler.
void OnTimer(const std::string &channel_name="")
void PublishMessage(const std::string &channel_name="") override
Publish Message to dreamview frontend.
void GetChannelMsgWithFilter(std::vector< std::string > *channels, const std::string &filter_message_type, const std::string &filter_channel, bool reverse_filter_channel=false)
GetChannelMsgWithFilter:by msg type and channel filter field to filter channels for DataUpdater
The WebSocketHandler, built on top of CivetWebSocketHandler, is a websocket handler that handles diff...
bool BroadcastBinaryData(const std::string &data, bool skippable=false)
Sends the provided binary data to all the connected clients.
std::string frame_id
Point cloud frame id
std::atomic< bool > future_ready_
double last_point_cloud_time_
std::shared_ptr< cyber::Reader< drivers::PointCloud > > point_cloud_reader_
std::unique_ptr< cyber::Timer > timer_
std::string point_cloud_str_