Skip to content

Add filtering capability to ft_broadcaster #1814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fe36000
Add support for publishing additional topics to the FTS Broadcaster
destogl Jun 9, 2021
ef7ad6a
Added filtering capabilities
guihomework Mar 13, 2023
a47926a
Added more tests with filters
guihomework Mar 14, 2023
a413217
Removed helper that only shows declared params
guihomework Mar 19, 2023
3652ab1
Fixed filter handling and unitialized frame_id
guihomework Mar 19, 2023
8163413
Added test for additional_frames_to_publish
guihomework Mar 19, 2023
a58bd31
Merge branch 'master' into add-filtering-capability-to-fts-broadcaste…
christophfroehlich Apr 12, 2025
31e79e2
Fix test parameters
christophfroehlich Apr 12, 2025
41853ab
Merge remote-tracking branch 'origin/master' into add-filtering-capab…
christophfroehlich Apr 19, 2025
c9b52f1
compile filters from source on humble
christophfroehlich Apr 21, 2025
206da96
Merge branch 'master' into add-filtering-capability-to-fts-broadcaste…
bmagyar May 3, 2025
0759030
Use filters from binary
christophfroehlich May 3, 2025
aa12321
Update ros2_controllers-not-released.humble.repos
christophfroehlich May 3, 2025
3d15fa5
Fix pre-commit
christophfroehlich May 3, 2025
b431110
Merge remote-tracking branch 'origin/master' into add-filtering-capab…
christophfroehlich May 21, 2025
a78dab0
Fix the tests
christophfroehlich May 21, 2025
714400d
Merge branch 'master' into add-filtering-capability-to-fts-broadcaste…
christophfroehlich Jul 1, 2025
834e1ad
Apply suggestions from code review
christophfroehlich Jul 1, 2025
98fac42
Apply suggestions from code review
christophfroehlich Jul 1, 2025
3a6978c
Use correct hpp file
christophfroehlich Jul 1, 2025
204e5d3
Merge branch 'master' into add-filtering-capability-to-fts-broadcaste…
christophfroehlich Jul 3, 2025
63dcce2
Fix format
christophfroehlich Jul 3, 2025
cad2b29
Update description of limit() function in speed_limiter (#1793)
Amronos Jul 3, 2025
c6b8ca7
Improve paths filtering of docs CI job (#1796)
christophfroehlich Jul 3, 2025
da21fd4
fix rqt_joint_trajectory_controller for robots with namespace (#1792)
oscar-lima Jul 5, 2025
8c1fa73
Mecanum Drive: Populate the pose covariance matrix (#1772)
hilary-luo Jul 7, 2025
284c4a1
Explicit cast `rcutils_duration_value_t` (#1808)
christophfroehlich Jul 8, 2025
9619195
Applying review suggestions
OscarMrZ Jul 10, 2025
b1de60b
Simplified testing
OscarMrZ Jul 11, 2025
4062e15
Improving logic to check if there's a filter chain
OscarMrZ Jul 11, 2025
ad4dcfa
Fixing with pre-commit
OscarMrZ Jul 14, 2025
59eb2ea
Merge branch 'ros-controls:master' into ft_broadcaster/add-filtering-…
OscarMrZ Jul 15, 2025
45647df
Merge branch 'ros-controls:master' into ft_broadcaster/add-filtering-…
OscarMrZ Jul 16, 2025
61e9b55
Removing unnecesarily added deps
OscarMrZ Jul 15, 2025
3afca35
Improving tests with dummy filter
OscarMrZ Jul 16, 2025
7306d25
Removing unnecessary temporary objects created while calling emplace_…
OscarMrZ Jul 16, 2025
94106c2
Merge branch 'master' into ft_broadcaster/add-filtering-capability
christophfroehlich Jul 22, 2025
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
28 changes: 28 additions & 0 deletions force_torque_sensor_broadcaster/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export_windows_symbols()

set(THIS_PACKAGE_INCLUDE_DEPENDS
controller_interface
filters
generate_parameter_library
geometry_msgs
hardware_interface
Expand Down Expand Up @@ -37,6 +38,7 @@ target_include_directories(force_torque_sensor_broadcaster PUBLIC
target_link_libraries(force_torque_sensor_broadcaster PUBLIC
force_torque_sensor_broadcaster_parameters
controller_interface::controller_interface
filters::filter_chain
hardware_interface::hardware_interface
pluginlib::pluginlib
rclcpp::rclcpp
Expand Down Expand Up @@ -69,7 +71,33 @@ if(BUILD_TESTING)
target_include_directories(test_force_torque_sensor_broadcaster PRIVATE include)
target_link_libraries(test_force_torque_sensor_broadcaster
force_torque_sensor_broadcaster
force_torque_sensor_broadcaster_parameters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
force_torque_sensor_broadcaster_parameters

do we have to link against the parameters here?

)

add_library(dummy_filter SHARED
test/dummy_filter.cpp
)
target_compile_features(dummy_filter PUBLIC cxx_std_17)
target_include_directories(dummy_filter PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/force_torque_sensor_broadcaster>
)
target_link_libraries(dummy_filter PUBLIC
filters::increment
${geometry_msgs_TARGETS}
pluginlib::pluginlib
rclcpp::rclcpp
)
install(
TARGETS
dummy_filter
EXPORT export_force_torque_sensor_broadcaster
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
)

pluginlib_export_plugin_description_file(filters "test/dummy_filter_plugin.xml")
endif()

install(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,23 @@
#define FORCE_TORQUE_SENSOR_BROADCASTER__FORCE_TORQUE_SENSOR_BROADCASTER_HPP_

#include <memory>
#include <string>
#include <vector>

#include "controller_interface/chainable_controller_interface.hpp"
// auto-generated by generate_parameter_library
#include "force_torque_sensor_broadcaster/force_torque_sensor_broadcaster_parameters.hpp"
#include "filters/filter_chain.hpp"
#include "geometry_msgs/msg/wrench_stamped.hpp"
#include "rclcpp_lifecycle/state.hpp"
#include "realtime_tools/realtime_publisher.hpp"
#include "semantic_components/force_torque_sensor.hpp"

// auto-generated by generate_parameter_library
#include "force_torque_sensor_broadcaster/force_torque_sensor_broadcaster_parameters.hpp"

namespace force_torque_sensor_broadcaster
{
using WrenchMsgType = geometry_msgs::msg::WrenchStamped;

class ForceTorqueSensorBroadcaster : public controller_interface::ChainableControllerInterface
{
public:
Expand Down Expand Up @@ -67,10 +72,18 @@ class ForceTorqueSensorBroadcaster : public controller_interface::ChainableContr
Params params_;

std::unique_ptr<semantic_components::ForceTorqueSensor> force_torque_sensor_;

using StatePublisher = realtime_tools::RealtimePublisher<geometry_msgs::msg::WrenchStamped>;
rclcpp::Publisher<geometry_msgs::msg::WrenchStamped>::SharedPtr sensor_state_publisher_;
std::unique_ptr<StatePublisher> realtime_publisher_;
bool has_filter_chain_ = false;

WrenchMsgType wrench_raw_;
WrenchMsgType wrench_filtered_;
std::unique_ptr<filters::FilterChain<WrenchMsgType>> filter_chain_;

using StatePublisher = rclcpp::Publisher<WrenchMsgType>::SharedPtr;
using StateRTPublisher = realtime_tools::RealtimePublisher<WrenchMsgType>;
StatePublisher sensor_raw_state_publisher_;
StatePublisher sensor_filtered_state_publisher_;
std::unique_ptr<StateRTPublisher> realtime_raw_publisher_;
std::unique_ptr<StateRTPublisher> realtime_filtered_publisher_;
};

} // namespace force_torque_sensor_broadcaster
Expand Down
1 change: 1 addition & 0 deletions force_torque_sensor_broadcaster/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

<depend>backward_ros</depend>
<depend>controller_interface</depend>
<depend>filters</depend>
<depend>geometry_msgs</depend>
<depend>hardware_interface</depend>
<depend>pluginlib</depend>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

#include "force_torque_sensor_broadcaster/force_torque_sensor_broadcaster.hpp"

#include <limits>
#include <memory>
#include <string>

#include "rclcpp/logging.hpp"

namespace force_torque_sensor_broadcaster
{
ForceTorqueSensorBroadcaster::ForceTorqueSensorBroadcaster()
Expand Down Expand Up @@ -87,12 +90,39 @@ controller_interface::CallbackReturn ForceTorqueSensorBroadcaster::on_configure(
torque_names.z));
}

try
{
filter_chain_ =
std::make_unique<filters::FilterChain<WrenchMsgType>>("geometry_msgs::msg::WrenchStamped");
}
catch (const std::exception & e)
{
fprintf(stderr, "Exception thrown during filter chain creation with message : %s \n", e.what());
return CallbackReturn::ERROR;
}

// As the sensor_filter_chain parameter is of type 'none', we cannot directly check if it is
// present. Even if the sensor_filter_chain parameter is not specified, the filter chain will be
// correctly configured with an empty list of filters (https://github.com/ros/filters/issues/89).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to proceed here?

has_filter_chain_ = filter_chain_->configure(
"sensor_filter_chain", get_node()->get_node_logging_interface(),
get_node()->get_node_parameters_interface());

RCLCPP_INFO_EXPRESSION(
get_node()->get_logger(), has_filter_chain_, "Filter chain is successfully configured!");

try
{
// register ft sensor data publisher
sensor_state_publisher_ = get_node()->create_publisher<geometry_msgs::msg::WrenchStamped>(
sensor_raw_state_publisher_ = get_node()->create_publisher<geometry_msgs::msg::WrenchStamped>(
"~/wrench", rclcpp::SystemDefaultsQoS());
realtime_publisher_ = std::make_unique<StatePublisher>(sensor_state_publisher_);
realtime_raw_publisher_ = std::make_unique<StateRTPublisher>(sensor_raw_state_publisher_);

sensor_filtered_state_publisher_ =
get_node()->create_publisher<geometry_msgs::msg::WrenchStamped>(
"~/wrench_filtered", rclcpp::SystemDefaultsQoS());
realtime_filtered_publisher_ =
std::make_unique<StateRTPublisher>(sensor_filtered_state_publisher_);
}
catch (const std::exception & e)
{
Expand All @@ -102,9 +132,19 @@ controller_interface::CallbackReturn ForceTorqueSensorBroadcaster::on_configure(
return controller_interface::CallbackReturn::ERROR;
}

realtime_publisher_->lock();
realtime_publisher_->msg_.header.frame_id = params_.frame_id;
realtime_publisher_->unlock();
wrench_raw_.header.frame_id = params_.frame_id;
wrench_filtered_.header.frame_id = params_.frame_id;

realtime_raw_publisher_->lock();
realtime_raw_publisher_->msg_.header.frame_id = params_.frame_id;
realtime_raw_publisher_->unlock();

if (has_filter_chain_)
{
realtime_filtered_publisher_->lock();
realtime_filtered_publisher_->msg_.header.frame_id = params_.frame_id;
realtime_filtered_publisher_->unlock();
}

RCLCPP_INFO(get_node()->get_logger(), "configure successful");
return controller_interface::CallbackReturn::SUCCESS;
Expand Down Expand Up @@ -146,13 +186,28 @@ controller_interface::return_type ForceTorqueSensorBroadcaster::update_and_write
{
param_listener_->try_get_params(params_);

if (realtime_publisher_ && realtime_publisher_->trylock())
wrench_raw_.header.stamp = time;
force_torque_sensor_->get_values_as_message(wrench_raw_.wrench);
this->apply_sensor_offset(params_, wrench_raw_);
this->apply_sensor_multiplier(params_, wrench_raw_);

if (realtime_raw_publisher_ && realtime_raw_publisher_->trylock())
{
realtime_raw_publisher_->msg_.header.stamp = time;
realtime_raw_publisher_->msg_.wrench = wrench_raw_.wrench;
realtime_raw_publisher_->unlockAndPublish();
}

if (has_filter_chain_)
{
realtime_publisher_->msg_.header.stamp = time;
force_torque_sensor_->get_values_as_message(realtime_publisher_->msg_.wrench);
this->apply_sensor_offset(params_, realtime_publisher_->msg_);
this->apply_sensor_multiplier(params_, realtime_publisher_->msg_);
realtime_publisher_->unlockAndPublish();
// Filter sensor data, if no filter chain config was specified, wrench_filtered_ = wrench_raw_
auto filtered = filter_chain_->update(wrench_raw_, wrench_filtered_);
if (filtered && realtime_filtered_publisher_ && realtime_filtered_publisher_->trylock())
{
realtime_filtered_publisher_->msg_.header.stamp = time;
realtime_filtered_publisher_->msg_.wrench = wrench_filtered_.wrench;
realtime_filtered_publisher_->unlockAndPublish();
}
}

return controller_interface::return_type::OK;
Expand Down Expand Up @@ -198,38 +253,32 @@ ForceTorqueSensorBroadcaster::on_export_state_interfaces()
if (!force_names[0].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, force_names[0], &realtime_publisher_->msg_.wrench.force.x));
export_prefix, force_names[0], &realtime_raw_publisher_->msg_.wrench.force.x);
}
if (!force_names[1].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, force_names[1], &realtime_publisher_->msg_.wrench.force.y));
export_prefix, force_names[1], &realtime_raw_publisher_->msg_.wrench.force.y);
}
if (!force_names[2].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, force_names[2], &realtime_publisher_->msg_.wrench.force.z));
export_prefix, force_names[2], &realtime_raw_publisher_->msg_.wrench.force.z);
}
if (!torque_names[0].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, torque_names[0], &realtime_publisher_->msg_.wrench.torque.x));
export_prefix, torque_names[0], &realtime_raw_publisher_->msg_.wrench.torque.x);
}
if (!torque_names[1].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, torque_names[1], &realtime_publisher_->msg_.wrench.torque.y));
export_prefix, torque_names[1], &realtime_raw_publisher_->msg_.wrench.torque.y);
}
if (!torque_names[2].empty())
{
exported_state_interfaces.emplace_back(
hardware_interface::StateInterface(
export_prefix, torque_names[2], &realtime_publisher_->msg_.wrench.torque.z));
export_prefix, torque_names[2], &realtime_raw_publisher_->msg_.wrench.torque.z);
}
return exported_state_interfaces;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,10 @@ force_torque_sensor_broadcaster:
default_value: 1.0,
description: "The multiplier of torque value around 'z' axis.",
}
sensor_filter_chain: {
type: none,
description: "Map of parameters that defines a filter chain, containing filterN as key. The fields for each filter are:
type: The filter plugin to be loaded
name: Actual name semantically describing the filter, e.g., low_pass_filter
params: And underlying map of parameters needed for a specific filter, refer to the specific filter documentation."
}
56 changes: 56 additions & 0 deletions force_torque_sensor_broadcaster/test/dummy_filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

// Copyright (c) 2025, PAL Robotics
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
* Authors: Oscar Martinez
*/

#include <rclcpp/logger.hpp>
#include <rclcpp/logging.hpp>

#include "geometry_msgs/msg/wrench_stamped.hpp"

#include "filters/increment.hpp"

namespace filters
{

template <>
bool IncrementFilter<geometry_msgs::msg::WrenchStamped>::update(
const geometry_msgs::msg::WrenchStamped & data_in, geometry_msgs::msg::WrenchStamped & data_out)
{
if (!this->configured_)
{
throw std::runtime_error("Filter is not configured");
}

// Just increment every value
data_out.wrench.force.x = data_in.wrench.force.x + 1;
data_out.wrench.force.y = data_in.wrench.force.y + 1;
data_out.wrench.force.z = data_in.wrench.force.z + 1;
data_out.wrench.torque.x = data_in.wrench.torque.x + 1;
data_out.wrench.torque.y = data_in.wrench.torque.y + 1;
data_out.wrench.torque.z = data_in.wrench.torque.z + 1;

return true;
}

} // namespace filters

#include "pluginlib/class_list_macros.hpp"

PLUGINLIB_EXPORT_CLASS(
filters::IncrementFilter<geometry_msgs::msg::WrenchStamped>,
filters::FilterBase<geometry_msgs::msg::WrenchStamped>)
9 changes: 9 additions & 0 deletions force_torque_sensor_broadcaster/test/dummy_filter_plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<class_libraries>
<library path="dummy_filter">
<class name="filters/IncrementFilterWrench" type="filters::IncrementFilter&lt;geometry_msgs::msg::WrenchStamped&gt;" base_class_type="filters::FilterBase&lt;geometry_msgs::msg::WrenchStamped&gt;">
<description>
This is a increment filter which works on a wrench message.
</description>
</class>
</library>
</class_libraries>
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
test_force_torque_sensor_broadcaster:
ros__parameters:

frame_id: "fts_sensor_frame"
test_force_torque_sensor_broadcaster_with_chain:
ros__parameters:
frame_id: "fts_sensor_frame"
sensor_name: "fts_sensor"
sensor_filter_chain:
filter1:
name: dummy
type: filters/IncrementFilterWrench
Loading
Loading