Skip to content

Commit 8f6d345

Browse files
authored
Backport #192 to kilted
2 parents 54564e8 + a54aaf2 commit 8f6d345

File tree

5 files changed

+148
-0
lines changed

5 files changed

+148
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from launch import LaunchDescription
2+
from launch.substitutions import PathJoinSubstitution
3+
from launch_ros.actions import Node
4+
from ament_index_python.packages import get_package_share_directory
5+
6+
7+
def generate_launch_description():
8+
return LaunchDescription([
9+
Node(
10+
package="laser_filters",
11+
executable="scan_to_scan_filter_chain",
12+
parameters=[
13+
PathJoinSubstitution([
14+
get_package_share_directory("laser_filters"),
15+
"examples", "bin_filter_example.yaml",
16+
])],
17+
)
18+
])

examples/bin_filter_example.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
scan_to_scan_filter_chain:
2+
ros__parameters:
3+
filter1:
4+
name: angle
5+
type: laser_filters/LaserScanBinningFilter
6+
params:
7+
num_bins: 360
8+
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*********************************************************************
2+
* Software License Agreement (BSD License)
3+
*
4+
* Copyright (c) 2024, Anthony Goeckner <anthony.goeckner@gmail.com>
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
*
11+
* * Redistributions of source code must retain the above copyright
12+
* notice, this list of conditions and the following disclaimer.
13+
* * Redistributions in binary form must reproduce the above
14+
* copyright notice, this list of conditions and the following
15+
* disclaimer in the documentation and/or other materials provided
16+
* with the distribution.
17+
* * Neither the name of the Willow Garage nor the names of its
18+
* contributors may be used to endorse or promote products derived
19+
* from this software without specific prior written permission.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24+
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25+
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30+
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
* POSSIBILITY OF SUCH DAMAGE.
33+
*********************************************************************/
34+
35+
#ifndef BINNING_FILTER_H
36+
#define BINNING_FILTER_H
37+
/**
38+
\author Anthony Goeckner
39+
@b LaserScanBinningFilter splits input scans into bins, ensuring that every output scan has the exact same number of readings as the number of bins.
40+
41+
**/
42+
43+
#include "filters/filter_base.hpp"
44+
45+
#include <sensor_msgs/msg/laser_scan.hpp>
46+
47+
namespace laser_filters
48+
{
49+
50+
class LaserScanBinningFilter : public filters::FilterBase<sensor_msgs::msg::LaserScan>
51+
{
52+
public:
53+
54+
uint num_bins_;
55+
56+
bool configure()
57+
{
58+
num_bins_ = 360;
59+
getParam("num_bins", num_bins_);
60+
61+
return true;
62+
}
63+
64+
virtual ~LaserScanBinningFilter()
65+
{
66+
}
67+
68+
bool update(const sensor_msgs::msg::LaserScan& input_scan, sensor_msgs::msg::LaserScan& filtered_scan)
69+
{
70+
// Copy data first.
71+
filtered_scan = input_scan;
72+
73+
// Set up filtered_scan.
74+
filtered_scan.ranges.assign(num_bins_, std::numeric_limits<float>::quiet_NaN());
75+
filtered_scan.intensities.assign(num_bins_, std::numeric_limits<float>::quiet_NaN());
76+
filtered_scan.angle_increment = 2.0 * M_PI / static_cast<float>(num_bins_);
77+
78+
// Convert to positive angles.
79+
float input_angle_min = fmodf(2.0 * M_PI + input_scan.angle_min, 2.0 * M_PI);
80+
81+
// Need to account for case where scan wraps around, causing the data at angle 0 to actually occur
82+
// at the end of the scan. Therefore, we need to adjust the starting point of the scan.
83+
double angle_overlap = fmodf(input_scan.angle_increment * input_scan.ranges.size(), 2.0 * M_PI);
84+
85+
// Update the scan time and angle_min to the oldest reading which will not be overlapped by a newer one.
86+
filtered_scan.scan_time = input_scan.scan_time + input_scan.time_increment * angle_overlap / input_scan.angle_increment;
87+
filtered_scan.time_increment = input_scan.time_increment * input_scan.ranges.size() / num_bins_;
88+
filtered_scan.angle_min = fmodf(input_angle_min + angle_overlap, 2.0 * M_PI);
89+
filtered_scan.angle_max = filtered_scan.angle_min + 2.0 * M_PI;
90+
91+
// Assign data to bins.
92+
unsigned int i = 0;
93+
while(i < input_scan.ranges.size())
94+
{
95+
// Calculate angle.
96+
double input_angle_relative = input_scan.angle_increment * i;
97+
double angle = fmodf(input_angle_relative - angle_overlap + 2.0 * M_PI, 2.0 * M_PI);
98+
99+
// Calculate the bin index.
100+
unsigned int bin_index = static_cast<unsigned int>(floor(angle / filtered_scan.angle_increment));
101+
102+
// Copy the range and intensity values.
103+
filtered_scan.ranges[bin_index] = input_scan.ranges[i];
104+
filtered_scan.intensities[bin_index] = input_scan.intensities[i];
105+
106+
i++;
107+
}
108+
return true;
109+
}
110+
};
111+
112+
}
113+
114+
#endif // LASER_SCAN_INTENSITY_FILTER_H

laser_filters_plugins.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
This is a filter that will generate range readings for error readings in a scan by interpolating between valid readings on either side of the error
3131
</description>
3232
</class>
33+
<class name="laser_filters/LaserScanBinningFilter" type="laser_filters::LaserScanBinningFilter"
34+
base_class_type="filters::FilterBase&lt;sensor_msgs::msg::LaserScan&gt;">
35+
<description>
36+
This is a filter that bins laser scans such that the output always contains a fixed number of measurements.
37+
</description>
38+
</class>
3339
<class name="laser_filters/LaserScanAngularBoundsFilter" type="laser_filters::LaserScanAngularBoundsFilter"
3440
base_class_type="filters::FilterBase&lt;sensor_msgs::msg::LaserScan&gt;">
3541
<description>

src/laser_scan_filters.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "laser_filters/median_filter.h"
3131
#include "laser_filters/median_spatial_filter.h"
3232
#include "laser_filters/array_filter.h"
33+
#include "laser_filters/binning_filter.h"
3334
#include "laser_filters/intensity_filter.h"
3435
#include "laser_filters/range_filter.h"
3536
#include "laser_filters/scan_mask_filter.h"
@@ -52,6 +53,7 @@
5253
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserMedianFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
5354
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserScanMedianSpatialFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
5455
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserArrayFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
56+
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserScanBinningFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
5557
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserScanIntensityFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
5658
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserScanRangeFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)
5759
PLUGINLIB_EXPORT_CLASS(laser_filters::LaserScanAngularBoundsFilter, filters::FilterBase<sensor_msgs::msg::LaserScan>)

0 commit comments

Comments
 (0)