// // The MIT License (MIT) // // Copyright (c) 2022 Livox. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // #include "pub_handler.h" #include #include #include #include namespace livox_ros { std::atomic PubHandler::is_timestamp_sync_; PubHandler &pub_handler() { static PubHandler handler; return handler; } void PubHandler::Init() { } void PubHandler::Uninit() { if (lidar_listen_id_ > 0) { LivoxLidarRemovePointCloudObserver(lidar_listen_id_); lidar_listen_id_ = 0; } RequestExit(); if (point_process_thread_ && point_process_thread_->joinable()) { point_process_thread_->join(); point_process_thread_ = nullptr; } else { /* */ } } void PubHandler::RequestExit() { is_quit_.store(true); } void PubHandler::SetPointCloudConfig(const double publish_freq) { publish_interval_ = (kNsPerSecond / (publish_freq * 10)) * 10; publish_interval_tolerance_ = publish_interval_ - kNsTolerantFrameTimeDeviation; publish_interval_ms_ = publish_interval_ / kRatioOfMsToNs; if (!point_process_thread_) { point_process_thread_ = std::make_shared(&PubHandler::RawDataProcess, this); } return; } void PubHandler::SetImuDataCallback(ImuDataCallback cb, void* client_data) { imu_client_data_ = client_data; imu_callback_ = cb; } void PubHandler::AddLidarsExtParam(LidarExtParameter& lidar_param) { std::unique_lock lock(packet_mutex_); uint32_t id = 0; GetLidarId(lidar_param.lidar_type, lidar_param.handle, id); lidar_extrinsics_[id] = lidar_param; } void PubHandler::ClearAllLidarsExtrinsicParams() { std::unique_lock lock(packet_mutex_); lidar_extrinsics_.clear(); } void PubHandler::SetPointCloudsCallback(PointCloudsCallback cb, void* client_data) { pub_client_data_ = client_data; points_callback_ = cb; lidar_listen_id_ = LivoxLidarAddPointCloudObserver(OnLivoxLidarPointCloudCallback, this); } void PubHandler::OnLivoxLidarPointCloudCallback(uint32_t handle, const uint8_t dev_type, LivoxLidarEthernetPacket *data, void *client_data) { PubHandler* self = (PubHandler*)client_data; if (!self) { return; } if (data->time_type != kTimestampTypeNoSync) { is_timestamp_sync_.store(true); } else { is_timestamp_sync_.store(false); } if (data->data_type == kLivoxLidarImuData) { if (self->imu_callback_) { RawImuPoint* imu = (RawImuPoint*) data->data; ImuData imu_data; imu_data.lidar_type = static_cast(LidarProtoType::kLivoxLidarType); imu_data.handle = handle; imu_data.time_stamp = GetEthPacketTimestamp(data->time_type, data->timestamp, sizeof(data->timestamp)); imu_data.gyro_x = imu->gyro_x; imu_data.gyro_y = imu->gyro_y; imu_data.gyro_z = imu->gyro_z; imu_data.acc_x = imu->acc_x; imu_data.acc_y = imu->acc_y; imu_data.acc_z = imu->acc_z; self->imu_callback_(&imu_data, self->imu_client_data_); } return; } RawPacket packet = {}; packet.handle = handle; packet.lidar_type = LidarProtoType::kLivoxLidarType; packet.extrinsic_enable = false; if (dev_type == LivoxLidarDeviceType::kLivoxLidarTypeIndustrialHAP) { packet.line_num = kLineNumberHAP; } else if (dev_type == LivoxLidarDeviceType::kLivoxLidarTypeMid360) { packet.line_num = kLineNumberMid360; } else { packet.line_num = kLineNumberDefault; } packet.data_type = data->data_type; packet.point_num = data->dot_num; packet.point_interval = data->time_interval * 100 / data->dot_num; //ns packet.time_stamp = GetEthPacketTimestamp(data->time_type, data->timestamp, sizeof(data->timestamp)); uint32_t length = data->length - sizeof(LivoxLidarEthernetPacket) + 1; packet.raw_data.insert(packet.raw_data.end(), data->data, data->data + length); { std::unique_lock lock(self->packet_mutex_); self->raw_packet_queue_.push_back(packet); } self->packet_condition_.notify_one(); return; } void PubHandler::PublishPointCloud() { //publish point if (points_callback_) { points_callback_(&frame_, pub_client_data_); } return; } void PubHandler::CheckTimer(uint32_t id) { if (PubHandler::is_timestamp_sync_.load()) { // Enable time synchronization auto& process_handler = lidar_process_handlers_[id]; uint64_t recent_time_ms = process_handler->GetRecentTimeStamp() / kRatioOfMsToNs; if ((recent_time_ms % publish_interval_ms_ != 0) || recent_time_ms == 0) { return; } uint64_t diff = process_handler->GetRecentTimeStamp() - process_handler->GetLidarBaseTime(); if (diff < publish_interval_tolerance_) { return; } frame_.base_time[frame_.lidar_num] = process_handler->GetLidarBaseTime(); points_[id].clear(); process_handler->GetLidarPointClouds(points_[id]); if (points_[id].empty()) { return; } PointPacket& lidar_point = frame_.lidar_point[frame_.lidar_num]; lidar_point.lidar_type = LidarProtoType::kLivoxLidarType; // TODO: lidar_point.handle = id; lidar_point.points_num = points_[id].size(); lidar_point.points = points_[id].data(); frame_.lidar_num++; if (frame_.lidar_num != 0) { PublishPointCloud(); frame_.lidar_num = 0; } } else { // Disable time synchronization auto now_time = std::chrono::high_resolution_clock::now(); //First Set static bool first = true; if (first) { last_pub_time_ = now_time; first = false; return; } if (now_time - last_pub_time_ < std::chrono::nanoseconds(publish_interval_)) { return; } last_pub_time_ += std::chrono::nanoseconds(publish_interval_); for (auto &process_handler : lidar_process_handlers_) { frame_.base_time[frame_.lidar_num] = process_handler.second->GetLidarBaseTime(); uint32_t handle = process_handler.first; points_[handle].clear(); process_handler.second->GetLidarPointClouds(points_[handle]); if (points_[handle].empty()) { continue; } PointPacket& lidar_point = frame_.lidar_point[frame_.lidar_num]; lidar_point.lidar_type = LidarProtoType::kLivoxLidarType; // TODO: lidar_point.handle = handle; lidar_point.points_num = points_[handle].size(); lidar_point.points = points_[handle].data(); frame_.lidar_num++; } PublishPointCloud(); frame_.lidar_num = 0; } return; } void PubHandler::RawDataProcess() { RawPacket raw_data; while (!is_quit_.load()) { { std::unique_lock lock(packet_mutex_); if (raw_packet_queue_.empty()) { packet_condition_.wait_for(lock, std::chrono::milliseconds(500)); if (raw_packet_queue_.empty()) { continue; } } raw_data = raw_packet_queue_.front(); raw_packet_queue_.pop_front(); } uint32_t id = 0; GetLidarId(raw_data.lidar_type, raw_data.handle, id); if (lidar_process_handlers_.find(id) == lidar_process_handlers_.end()) { lidar_process_handlers_[id].reset(new LidarPubHandler()); } auto &process_handler = lidar_process_handlers_[id]; if (lidar_extrinsics_.find(id) != lidar_extrinsics_.end()) { lidar_process_handlers_[id]->SetLidarsExtParam(lidar_extrinsics_[id]); } process_handler->PointCloudProcess(raw_data); CheckTimer(id); } } bool PubHandler::GetLidarId(LidarProtoType lidar_type, uint32_t handle, uint32_t& id) { if (lidar_type == kLivoxLidarType) { id = handle; return true; } return false; } uint64_t PubHandler::GetEthPacketTimestamp(uint8_t timestamp_type, uint8_t* time_stamp, uint8_t size) { LdsStamp time; memcpy(time.stamp_bytes, time_stamp, size); if (timestamp_type == kTimestampTypeGptpOrPtp || timestamp_type == kTimestampTypeGps) { return time.stamp; } return std::chrono::high_resolution_clock::now().time_since_epoch().count(); } /*******************************/ /* LidarPubHandler Definitions*/ LidarPubHandler::LidarPubHandler() : is_set_extrinsic_params_(false) {} uint64_t LidarPubHandler::GetLidarBaseTime() { if (points_clouds_.empty()) { return 0; } return points_clouds_.at(0).offset_time; } void LidarPubHandler::GetLidarPointClouds(std::vector& points_clouds) { std::lock_guard lock(mutex_); points_clouds.swap(points_clouds_); } uint64_t LidarPubHandler::GetRecentTimeStamp() { if (points_clouds_.empty()) { return 0; } return points_clouds_.back().offset_time; } uint32_t LidarPubHandler::GetLidarPointCloudsSize() { std::lock_guard lock(mutex_); return points_clouds_.size(); } //convert to standard format and extrinsic compensate void LidarPubHandler::PointCloudProcess(RawPacket & pkt) { if (pkt.lidar_type == LidarProtoType::kLivoxLidarType) { LivoxLidarPointCloudProcess(pkt); } else { static bool flag = false; if (!flag) { std::cout << "error, unsupported protocol type: " << static_cast(pkt.lidar_type) << std::endl; flag = true; } } } void LidarPubHandler::LivoxLidarPointCloudProcess(RawPacket & pkt) { switch (pkt.data_type) { case kLivoxLidarCartesianCoordinateHighData: ProcessCartesianHighPoint(pkt); break; case kLivoxLidarCartesianCoordinateLowData: ProcessCartesianLowPoint(pkt); break; case kLivoxLidarSphericalCoordinateData: ProcessSphericalPoint(pkt); break; default: std::cout << "unknown data type: " << static_cast(pkt.data_type) << " !!" << std::endl; break; } } void LidarPubHandler::SetLidarsExtParam(LidarExtParameter lidar_param) { if (is_set_extrinsic_params_) { return; } extrinsic_.trans[0] = lidar_param.param.x; extrinsic_.trans[1] = lidar_param.param.y; extrinsic_.trans[2] = lidar_param.param.z; double cos_roll = cos(static_cast(lidar_param.param.roll * PI / 180.0)); double cos_pitch = cos(static_cast(lidar_param.param.pitch * PI / 180.0)); double cos_yaw = cos(static_cast(lidar_param.param.yaw * PI / 180.0)); double sin_roll = sin(static_cast(lidar_param.param.roll * PI / 180.0)); double sin_pitch = sin(static_cast(lidar_param.param.pitch * PI / 180.0)); double sin_yaw = sin(static_cast(lidar_param.param.yaw * PI / 180.0)); extrinsic_.rotation[0][0] = cos_pitch * cos_yaw; extrinsic_.rotation[0][1] = sin_roll * sin_pitch * cos_yaw - cos_roll * sin_yaw; extrinsic_.rotation[0][2] = cos_roll * sin_pitch * cos_yaw + sin_roll * sin_yaw; extrinsic_.rotation[1][0] = cos_pitch * sin_yaw; extrinsic_.rotation[1][1] = sin_roll * sin_pitch * sin_yaw + cos_roll * cos_yaw; extrinsic_.rotation[1][2] = cos_roll * sin_pitch * sin_yaw - sin_roll * cos_yaw; extrinsic_.rotation[2][0] = -sin_pitch; extrinsic_.rotation[2][1] = sin_roll * cos_pitch; extrinsic_.rotation[2][2] = cos_roll * cos_pitch; is_set_extrinsic_params_ = true; } void LidarPubHandler::ProcessCartesianHighPoint(RawPacket & pkt) { LivoxLidarCartesianHighRawPoint* raw = (LivoxLidarCartesianHighRawPoint*)pkt.raw_data.data(); PointXyzlt point = {}; for (uint32_t i = 0; i < pkt.point_num; i++) { if (pkt.extrinsic_enable) { point.x = raw[i].x / 1000.0; point.y = raw[i].y / 1000.0; point.z = raw[i].z / 1000.0; } else { point.x = (raw[i].x * extrinsic_.rotation[0][0] + raw[i].y * extrinsic_.rotation[0][1] + raw[i].z * extrinsic_.rotation[0][2] + extrinsic_.trans[0]) / 1000.0; point.y = (raw[i].x* extrinsic_.rotation[1][0] + raw[i].y * extrinsic_.rotation[1][1] + raw[i].z * extrinsic_.rotation[1][2] + extrinsic_.trans[1]) / 1000.0; point.z = (raw[i].x * extrinsic_.rotation[2][0] + raw[i].y * extrinsic_.rotation[2][1] + raw[i].z * extrinsic_.rotation[2][2] + extrinsic_.trans[2]) / 1000.0; } point.intensity = raw[i].reflectivity; point.line = i % pkt.line_num; point.tag = raw[i].tag; point.offset_time = pkt.time_stamp + i * pkt.point_interval; std::lock_guard lock(mutex_); points_clouds_.push_back(point); } } void LidarPubHandler::ProcessCartesianLowPoint(RawPacket & pkt) { LivoxLidarCartesianLowRawPoint* raw = (LivoxLidarCartesianLowRawPoint*)pkt.raw_data.data(); PointXyzlt point = {}; for (uint32_t i = 0; i < pkt.point_num; i++) { if (pkt.extrinsic_enable) { point.x = raw[i].x / 100.0; point.y = raw[i].y / 100.0; point.z = raw[i].z / 100.0; } else { point.x = (raw[i].x * extrinsic_.rotation[0][0] + raw[i].y * extrinsic_.rotation[0][1] + raw[i].z * extrinsic_.rotation[0][2] + extrinsic_.trans[0]) / 100.0; point.y = (raw[i].x* extrinsic_.rotation[1][0] + raw[i].y * extrinsic_.rotation[1][1] + raw[i].z * extrinsic_.rotation[1][2] + extrinsic_.trans[1]) / 100.0; point.z = (raw[i].x * extrinsic_.rotation[2][0] + raw[i].y * extrinsic_.rotation[2][1] + raw[i].z * extrinsic_.rotation[2][2] + extrinsic_.trans[2]) / 100.0; } point.intensity = raw[i].reflectivity; point.line = i % pkt.line_num; point.tag = raw[i].tag; point.offset_time = pkt.time_stamp + i * pkt.point_interval; std::lock_guard lock(mutex_); points_clouds_.push_back(point); } } void LidarPubHandler::ProcessSphericalPoint(RawPacket& pkt) { LivoxLidarSpherPoint* raw = (LivoxLidarSpherPoint*)pkt.raw_data.data(); PointXyzlt point = {}; for (uint32_t i = 0; i < pkt.point_num; i++) { double radius = raw[i].depth / 1000.0; double theta = raw[i].theta / 100.0 / 180 * PI; double phi = raw[i].phi / 100.0 / 180 * PI; double src_x = radius * sin(theta) * cos(phi); double src_y = radius * sin(theta) * sin(phi); double src_z = radius * cos(theta); if (pkt.extrinsic_enable) { point.x = src_x; point.y = src_y; point.z = src_z; } else { point.x = src_x * extrinsic_.rotation[0][0] + src_y * extrinsic_.rotation[0][1] + src_z * extrinsic_.rotation[0][2] + (extrinsic_.trans[0] / 1000.0); point.y = src_x * extrinsic_.rotation[1][0] + src_y * extrinsic_.rotation[1][1] + src_z * extrinsic_.rotation[1][2] + (extrinsic_.trans[1] / 1000.0); point.z = src_x * extrinsic_.rotation[2][0] + src_y * extrinsic_.rotation[2][1] + src_z * extrinsic_.rotation[2][2] + (extrinsic_.trans[2] / 1000.0); } point.intensity = raw[i].reflectivity; point.line = i % pkt.line_num; point.tag = raw[i].tag; point.offset_time = pkt.time_stamp + i * pkt.point_interval; std::lock_guard lock(mutex_); points_clouds_.push_back(point); } } } // namespace livox_ros