Skip to content

Commit 5ef4bc2

Browse files
authored
fix: skip empty and short decoded lidar scans (#77)
* fix: skip empty and short decoded lidar scans Move empty and minimum-point filtering into the shared decoder path so Nebula and Seyond inputs consistently skip status-only or non-decodable packet groups without misleading conversion results. Made-with: Cursor * docs: drop README note about skipped scans Keep the README focused on user-facing behavior and remove the extra note about internal scan filtering from the PR. Made-with: Cursor
1 parent 5146838 commit 5ef4bc2

5 files changed

Lines changed: 35 additions & 29 deletions

File tree

src/bag_converter/include/bag_converter.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ namespace bag_converter
4444

4545
namespace defaults
4646
{
47-
inline constexpr size_t min_points_per_scan = 1000;
48-
4947
// Common configuration defaults
5048
inline constexpr double min_range = 0.1;
5149
inline constexpr double max_range = 250.0;

src/bag_converter/include/base_decoder.hpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,23 @@
1111

1212
#include "point_types.hpp"
1313

14+
#include <rclcpp/rclcpp.hpp>
1415
#include <rclcpp/serialization.hpp>
1516

1617
#include <sensor_msgs/msg/point_cloud2.hpp>
1718

18-
#include <pcl/point_cloud.h>
19-
#include <pcl/point_types.h>
20-
2119
#include <memory>
2220

2321
namespace bag_converter::decoder
2422
{
2523

24+
namespace defaults
25+
{
26+
27+
inline constexpr size_t min_points_per_scan = 1000;
28+
29+
} // namespace defaults
30+
2631
/**
2732
* @brief Abstract base class for PCD decoders using type erasure pattern
2833
*
@@ -49,7 +54,8 @@ class BasePCDDecoder
4954
* Derived classes deserialize the message internally and convert to PointCloud2.
5055
*
5156
* @param serialized_msg The serialized ROS message to decode
52-
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails
57+
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails, the point cloud
58+
* is empty, or the point cloud contains too few points
5359
*/
5460
virtual sensor_msgs::msg::PointCloud2::SharedPtr decode(
5561
const rclcpp::SerializedMessage & serialized_msg) = 0;
@@ -82,17 +88,33 @@ class PCDDecoder : public BasePCDDecoder
8288
/**
8389
* @brief Decode serialized message to PointCloud2 (implements PCDDecoderBase)
8490
*
85-
* Deserializes the message and delegates to the typed decode method.
91+
* Deserializes the message and delegates to the typed decode method. Returns nullptr if the
92+
* decoded PointCloud2 is empty or contains too few points.
8693
*
8794
* @param serialized_msg The serialized ROS message to decode
88-
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails
95+
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails, the point cloud
96+
* is empty, or the point cloud contains too few points
8997
*/
9098
sensor_msgs::msg::PointCloud2::SharedPtr decode(
9199
const rclcpp::SerializedMessage & serialized_msg) override
92100
{
93101
InputScanT input_msg;
94102
serializer_.deserialize_message(&serialized_msg, &input_msg);
95-
return decode_typed(input_msg);
103+
auto decoded_msg = decode_typed(input_msg);
104+
if (!decoded_msg) {
105+
return nullptr;
106+
}
107+
108+
if (decoded_msg->width == 0 || decoded_msg->data.empty()) {
109+
return nullptr;
110+
}
111+
112+
const size_t num_points = decoded_msg->width * decoded_msg->height;
113+
if (num_points < defaults::min_points_per_scan) {
114+
return nullptr;
115+
}
116+
117+
return decoded_msg;
96118
}
97119

98120
/**
@@ -102,10 +124,12 @@ class PCDDecoder : public BasePCDDecoder
102124
* Converts the input scan message of type InputScanT to a PointCloud2 message.
103125
*
104126
* @param input The input scan message to decode
105-
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails
127+
* @return Shared pointer to PointCloud2 message, or nullptr if decoding fails or contains no
128+
* points
106129
*/
107130
virtual sensor_msgs::msg::PointCloud2::SharedPtr decode_typed(const InputScanT & input) = 0;
108131

132+
protected:
109133
private:
110134
rclcpp::Serialization<InputScanT> serializer_;
111135
};

src/bag_converter/src/bag_converter.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,13 +496,6 @@ ProcessMessageResult process_lidar_message(
496496
auto pcd_msg = dec->decode(serialized_msg);
497497

498498
if (!pcd_msg) {
499-
return ProcessMessageResult::Decoded; // nothing to write, no stats change
500-
}
501-
502-
const size_t num_points = pcd_msg->width * pcd_msg->height;
503-
if (num_points < defaults::min_points_per_scan) {
504-
RCLCPP_INFO(g_logger, "Status packets detected (skipped decoding this message)");
505-
conversion_stats[topic_name].skipped_count++;
506499
return ProcessMessageResult::Skipped;
507500
}
508501

src/bag_converter/src/nebula_decoder.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include <rclcpp/rclcpp.hpp>
1919

20+
#include <algorithm>
21+
2022
namespace bag_converter::decoder::nebula
2123
{
2224

@@ -175,11 +177,6 @@ sensor_msgs::msg::PointCloud2::SharedPtr NebulaPCDDecoder<OutputPointT>::decode_
175177
const nebula_msgs::msg::NebulaPackets & input)
176178
{
177179
bag_converter::msg::SeyondScan scan = nebula_packets_to_seyond_scan(input, config_.frame_id);
178-
179-
if (scan.packets.empty()) {
180-
return nullptr;
181-
}
182-
183180
return seyond_decoder_.decode_typed(scan);
184181
}
185182

src/bag_converter/src/seyond_decoder.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,12 @@ sensor_msgs::msg::PointCloud2::SharedPtr SeyondPCDDecoder<OutputPointT>::decode_
8080
}
8181
}
8282

83-
// Check if point cloud is empty
8483
if (cloud_.points.empty()) {
85-
RCLCPP_WARN(
86-
rclcpp::get_logger("bag_converter.decoder.seyond"),
87-
"Decoded point cloud is empty (no points found in scan)");
84+
return nullptr;
8885
}
8986

90-
// Create PointCloud2 message
9187
auto pc2_msg = std::make_shared<sensor_msgs::msg::PointCloud2>();
9288
pcl::toROSMsg(cloud_, *pc2_msg);
93-
94-
// Set header timestamp from input message
9589
pc2_msg->header.stamp = input.header.stamp;
9690
pc2_msg->header.frame_id = cloud_.header.frame_id;
9791

0 commit comments

Comments
 (0)