Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,20 @@ source install/setup.bash

### Quick Start

Launch the self filter node with your robot configuration:
Launch the self filter node with your robot configuration. The robot description can be provided either as a parameter or via a ROS 2 topic (e.g. published by `robot_state_publisher`).

**Option 1: From topic (recommended)**

If `robot_state_publisher` is running, the filter will automatically pick up the robot description from the `/robot_description` topic:

```bash
ros2 launch robot_self_filter self_filter.launch.py \
filter_config:=/path/to/filter_config.yaml \
in_pointcloud_topic:=/lidar/points \
out_pointcloud_topic:=/lidar/points_filtered
```

**Option 2: From parameter**

```bash
ros2 launch robot_self_filter self_filter.launch.py \
Expand All @@ -54,18 +67,21 @@ ros2 launch robot_self_filter self_filter.launch.py \
out_pointcloud_topic:=/lidar/points_filtered
```

When `robot_description` is provided as a non-empty parameter, it takes priority over the topic.

### Launch Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `robot_description` | string | - | Robot URDF/XACRO description |
| `robot_description` | string | - | Robot URDF/XACRO description (if empty, uses topic instead) |
| `robot_description_topic` | string | `/robot_description` | Topic to subscribe to for robot description |
| `filter_config` | string | - | Path to YAML configuration file |
| `in_pointcloud_topic` | string | `/cloud_in` | Input point cloud topic |
| `out_pointcloud_topic` | string | `/cloud_out` | Filtered point cloud topic |
| `lidar_sensor_type` | int | `2` | Sensor type (0: XYZ, 1: XYZRGB, 2: Ouster, 3: Hesai, 4: Robosense, 5: Pandar) |
| `lidar_sensor_type` | int | `2` | Sensor type (0: XYZ, 1: XYZRGB, 2: Ouster, 3: Hesai, 4: Robosense, 5: Pandar, 6: XYZI) |
| `zero_for_removed_points` | bool | `true` | Set filtered points to zero instead of removing |
| `sensor_frame` | string | `Lidar` | TF frame of the sensor |
| `use_sim_time` | bool | `true` | Use simulation time |
| `description_name` | string | `/robot_description` | Robot description parameter namespace |

Comment thread
jeldriks marked this conversation as resolved.
## Configuration

Expand Down Expand Up @@ -131,6 +147,7 @@ The filter automatically determines shape types from the robot's URDF collision
### Subscribed Topics

- `<in_pointcloud_topic>` (sensor_msgs/PointCloud2): Raw point cloud from sensor
- `<robot_description_topic>` (std_msgs/String): Robot URDF description (only when `robot_description` parameter is empty)
- `/tf` (tf2_msgs/TFMessage): Transform data
- `/tf_static` (tf2_msgs/TFMessage): Static transforms
- `/joint_states` (sensor_msgs/JointState): Robot joint positions
Expand Down Expand Up @@ -160,6 +177,7 @@ The package supports multiple sensor types through the `lidar_sensor_type` param
| 3 | Hesai | Custom Hesai point type |
| 4 | Robosense | Custom Robosense point type |
| 5 | Pandar | Custom Pandar point type |
| 6 | Generic XYZI | `pcl::PointXYZI` |

## Examples

Expand Down
22 changes: 15 additions & 7 deletions launch/self_filter.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
from launch_ros.parameter_descriptions import ParameterValue

def generate_launch_description():
description_name_arg = DeclareLaunchArgument(
'description_name',
default_value='/robot_description'
)
zero_for_removed_points_arg = DeclareLaunchArgument(
'zero_for_removed_points',
default_value='true'
Expand All @@ -28,11 +24,21 @@ def generate_launch_description():
default_value='/cloud_out'
)
robot_description_arg = DeclareLaunchArgument(
'robot_description'
'robot_description',
default_value=''
)
robot_description_topic_arg = DeclareLaunchArgument(
'robot_description_topic',
default_value='/robot_description'
)
Comment thread
jeldriks marked this conversation as resolved.
filter_config_arg = DeclareLaunchArgument(
'filter_config'
)
sensor_frame_arg = DeclareLaunchArgument(
'sensor_frame',
default_value='Lidar',
description='TF frame of the sensor'
)
# Declare use_sim_time argument
use_sim_time_arg = DeclareLaunchArgument(
'use_sim_time',
Expand All @@ -56,25 +62,27 @@ def generate_launch_description():
LaunchConfiguration('robot_description'),
value_type=str
),
'robot_description_topic': LaunchConfiguration('robot_description_topic'),
'zero_for_removed_points': LaunchConfiguration('zero_for_removed_points'),
'sensor_frame': LaunchConfiguration('sensor_frame'),
'use_sim_time': LaunchConfiguration('use_sim_time') # Use the launch argument
}
],
remappings=[
('/robot_description', LaunchConfiguration('description_name')),
('/cloud_in', LaunchConfiguration('in_pointcloud_topic')),
('/cloud_out', LaunchConfiguration('out_pointcloud_topic')),
Comment thread
jeldriks marked this conversation as resolved.
Outdated
],
)

return LaunchDescription([
description_name_arg,
zero_for_removed_points_arg,
lidar_sensor_type_arg,
in_pointcloud_topic_arg,
out_pointcloud_topic_arg,
robot_description_arg,
robot_description_topic_arg,
filter_config_arg,
sensor_frame_arg,
use_sim_time_arg, # Add to launch description
log_config,
self_filter_node
Expand Down
49 changes: 48 additions & 1 deletion src/self_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <std_msgs/msg/string.hpp>
#include <tf2_ros/buffer.h>
#include <tf2_ros/create_timer_ros.h>
#include <tf2_ros/transform_listener.h>
Expand All @@ -31,6 +32,7 @@ namespace robot_self_filter
HesaiSensor = 3,
RobosenseSensor = 4,
PandarSensor = 5,
XYZISensor = 6,
};

class SelfFilterNode : public rclcpp::Node
Expand All @@ -54,6 +56,7 @@ namespace robot_self_filter
this->declare_parameter<int>("max_queue_size", 10);
this->declare_parameter<int>("lidar_sensor_type", 0);
this->declare_parameter<std::string>("robot_description", "");
this->declare_parameter<std::string>("robot_description_topic", "/robot_description");
this->declare_parameter<std::string>("in_pointcloud_topic", "/cloud_in");

sensor_frame_ = this->get_parameter("sensor_frame").as_string();
Expand Down Expand Up @@ -90,6 +93,38 @@ namespace robot_self_filter
{
std::string robot_description_xml = this->get_parameter("robot_description").as_string();

if (!robot_description_xml.empty())
{
RCLCPP_INFO(this->get_logger(), "Using robot description from parameter");
setupFilter();
return;
}

std::string topic = this->get_parameter("robot_description_topic").as_string();
RCLCPP_INFO(this->get_logger(), "No robot_description parameter provided, subscribing to topic: %s", topic.c_str());

robot_desc_sub_ = this->create_subscription<std_msgs::msg::String>(
topic,
rclcpp::QoS(1).transient_local(),
std::bind(&SelfFilterNode::robotDescriptionCallback, this, std::placeholders::_1));
Comment thread
jeldriks marked this conversation as resolved.
Outdated
}

private:
void robotDescriptionCallback(const std_msgs::msg::String::SharedPtr msg)
{
if (msg->data.empty())
{
RCLCPP_WARN(this->get_logger(), "Received empty robot description from topic, ignoring");
return;
}
RCLCPP_INFO(this->get_logger(), "Received robot description from topic");
this->set_parameter(rclcpp::Parameter("robot_description", msg->data));
robot_desc_sub_.reset();
setupFilter();
}

void setupFilter()
{
switch (sensor_type_)
{
case SensorType::XYZSensor:
Expand All @@ -110,6 +145,9 @@ namespace robot_self_filter
case SensorType::PandarSensor:
self_filter_ = std::make_shared<filters::SelfFilter<PointPandar>>(this->shared_from_this());
break;
case SensorType::XYZISensor:
self_filter_ = std::make_shared<filters::SelfFilter<pcl::PointXYZI>>(this->shared_from_this());
break;
default:
self_filter_ = std::make_shared<filters::SelfFilter<pcl::PointXYZ>>(this->shared_from_this());
break;
Expand All @@ -125,7 +163,6 @@ namespace robot_self_filter
std::bind(&SelfFilterNode::cloudCallback, this, std::placeholders::_1));
}
Comment thread
jeldriks marked this conversation as resolved.

private:
void cloudCallback(const sensor_msgs::msg::PointCloud2::ConstSharedPtr &cloud)
{
RCLCPP_INFO(this->get_logger(), "Received cloud message with timestamp %.6f",
Expand Down Expand Up @@ -161,6 +198,15 @@ namespace robot_self_filter
publishShapesFromMask(mask, cloud->header.frame_id);
break;
}
case SensorType::XYZISensor:
{
auto sf_xyzi = std::dynamic_pointer_cast<filters::SelfFilter<pcl::PointXYZI>>(self_filter_);
if (!sf_xyzi)
return;
auto mask = sf_xyzi->getSelfMaskPtr();
publishShapesFromMask(mask, cloud->header.frame_id);
break;
}
default:
RCLCPP_ERROR(this->get_logger(), "Sensor type not handled for shape publishing");
return;
Expand Down Expand Up @@ -293,6 +339,7 @@ namespace robot_self_filter
std::shared_ptr<filters::SelfFilterInterface> self_filter_;

rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr sub_;
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr robot_desc_sub_;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pointCloudPublisher_;
rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr marker_pub_;

Expand Down
Loading