1 | #define _CRT_SECURE_NO_WARNINGS
|
2 | //#define NDEBUG
|
3 |
|
4 | #include <deque>
|
5 | #include <vector>
|
6 | #include <cmath>
|
7 | #include <memory>
|
8 | #include <string>
|
9 | #include <iostream>
|
10 | #include <chrono>
|
11 | #include <functional>
|
12 | #include <assert.h>
|
13 |
|
14 | #include "rclcpp/rclcpp.hpp"
|
15 | #include "tf2_ros/transform_listener.h"
|
16 | #include "tf2_ros/buffer.h"
|
17 | #include "tf2_ros/transform_broadcaster.h"
|
18 | #include "geometry_msgs/msg/transform_stamped.hpp"
|
19 |
|
20 | using namespace std::chrono_literals;
|
21 |
|
22 |
|
23 | std::vector<double> butterworth_filter(const std::vector<double>& input, double cutoff_frequency, double sampling_rate, int order)
|
24 | {
|
25 | int N = input.size();
|
26 | std::vector<double> output(N, 0.0);
|
27 |
|
28 | // cf, init
|
29 | double nyquist = 0.5 * sampling_rate;
|
30 | double normalized_cutoff = cutoff_frequency / nyquist;
|
31 |
|
32 | std::vector<double> a(order + 1, 0.0);
|
33 | std::vector<double> b(order + 1, 0.0);
|
34 |
|
35 | double theta = M_PI * normalized_cutoff;
|
36 | double d = 1.0 / std::tan(theta);
|
37 | double d_squared = d * d;
|
38 |
|
39 | a[0] = 1.0 / (1.0 + std::sqrt(2.0) * d + d_squared);
|
40 | a[1] = 2.0 * a[0];
|
41 | a[2] = a[0];
|
42 | b[1] = 2.0 * (1.0 - d_squared) * a[0];
|
43 | b[2] = (1.0 - std::sqrt(2.0) * d + d_squared) * a[0];
|
44 |
|
45 | for (int i = 2; i < N; ++i)
|
46 | {
|
47 | #ifdef NDEBUG
|
48 | #else
|
49 | printf("\nFiltering index: %i\n",i);
|
50 | #endif
|
51 | output[i] = a[0] * input[i] + a[1] * input[i - 1] + a[2] * input[i - 2] - b[1] * output[i - 1] - b[2] * output[i - 2];
|
52 | }
|
53 |
|
54 | return output;
|
55 | }
|
56 |
|
57 | class Filter
|
58 | {
|
59 | public:
|
60 | Filter() {}
|
61 |
|
62 | void set_history_size(size_t new_size)
|
63 | {
|
64 | history_size_ = new_size;
|
65 | if (x_history_.size() > history_size_) x_history_.resize(history_size_);
|
66 | if (y_history_.size() > history_size_) y_history_.resize(history_size_);
|
67 | if (z_history_.size() > history_size_) z_history_.resize(history_size_);
|
68 | }
|
69 |
|
70 | geometry_msgs::msg::TransformStamped apply_filter(const geometry_msgs::msg::TransformStamped& transform)
|
71 | {
|
72 | x_history_.push_back(transform.transform.translation.x);
|
73 | y_history_.push_back(transform.transform.translation.y);
|
74 | z_history_.push_back(transform.transform.translation.z);
|
75 |
|
76 | if (x_history_.size() > history_size_) x_history_.pop_front();
|
77 | if (y_history_.size() > history_size_) y_history_.pop_front();
|
78 | if (z_history_.size() > history_size_) z_history_.pop_front();
|
79 |
|
80 | #ifdef NDEBUG
|
81 | #else
|
82 | printf("\nHistory Size: %zu", history_size_);
|
83 | printf("\nDeque x_history_ size: %zu", x_history_.size());
|
84 | printf("\nDeque y_history_ size: %zu", y_history_.size());
|
85 | printf("\nDeque z_history_ size: %zu", z_history_.size());
|
86 | #endif
|
87 | geometry_msgs::msg::TransformStamped filtered_transform = transform;
|
88 |
|
89 | if (x_history_.size() >= history_size_)
|
90 | {
|
91 | std::vector<double> x_filtered = butterworth_filter(
|
92 | std::vector<double>(x_history_.begin(), x_history_.end()), cutoff_frequency_, sampling_rate_, order_);
|
93 | std::vector<double> y_filtered = butterworth_filter(
|
94 | std::vector<double>(y_history_.begin(), y_history_.end()), cutoff_frequency_, sampling_rate_, order_);
|
95 | std::vector<double> z_filtered = butterworth_filter(
|
96 | std::vector<double>(z_history_.begin(), z_history_.end()), cutoff_frequency_, sampling_rate_, order_);
|
97 |
|
98 | filtered_transform.transform.translation.x = x_filtered.back();
|
99 | filtered_transform.transform.translation.y = y_filtered.back();
|
100 | filtered_transform.transform.translation.z = z_filtered.back();
|
101 | }
|
102 |
|
103 | return filtered_transform;
|
104 | }
|
105 |
|
106 | private:
|
107 | std::deque<double> x_history_;
|
108 | std::deque<double> y_history_;
|
109 | std::deque<double> z_history_;
|
110 | size_t history_size_ = 20;
|
111 | double cutoff_frequency_ = 10.0;
|
112 | double sampling_rate_ = 100.0;
|
113 | int order_ = 2;
|
114 | };
|
115 |
|
116 |
|
117 | class FrameListener : public rclcpp::Node
|
118 | {
|
119 | public:
|
120 | FrameListener() : Node("frame_listener_node"), buffer_(get_clock()), listener_(buffer_), filter_(std::make_shared<Filter>())
|
121 | {
|
122 | timer_ = this->create_wall_timer(50ms, std::bind(&FrameListener::timer_callback, this));
|
123 | publisher_ = this->create_publisher<geometry_msgs::msg::TransformStamped>("LHR_BFB7B3C7_filtered", 10);
|
124 | }
|
125 |
|
126 | private:
|
127 | void timer_callback()
|
128 | {
|
129 | geometry_msgs::msg::TransformStamped transformStamped;
|
130 | try
|
131 | {
|
132 | transformStamped = buffer_.lookupTransform("libsurvive_world", "LHR-BFB7B3C7", tf2::TimePointZero);
|
133 | RCLCPP_INFO(this->get_logger(), "Received transform (unfiltered): "
|
134 | "Translation: [%f, %f, %f], "
|
135 | "Rotation: [x: %f, y: %f, z: %f, w: %f]",
|
136 | transformStamped.transform.translation.x,
|
137 | transformStamped.transform.translation.y,
|
138 | transformStamped.transform.translation.z,
|
139 | transformStamped.transform.rotation.x,
|
140 | transformStamped.transform.rotation.y,
|
141 | transformStamped.transform.rotation.z,
|
142 | transformStamped.transform.rotation.w);
|
143 |
|
144 | geometry_msgs::msg::TransformStamped filtered_transform = filter_->apply_filter(transformStamped);
|
145 |
|
146 | publisher_->publish(filtered_transform);
|
147 |
|
148 | #ifdef NDEBUG
|
149 | #else
|
150 | RCLCPP_INFO(this->get_logger(), "Published unfiltered transform.");
|
151 | #endif
|
152 | }
|
153 | catch (tf2::TransformException& ex)
|
154 | {
|
155 | RCLCPP_WARN(this->get_logger(), "Could not transform 'LHR-BFB7B3C7' to 'libsurvive_world': %s", ex.what());
|
156 | }
|
157 | }
|
158 |
|
159 | tf2_ros::Buffer buffer_;
|
160 | tf2_ros::TransformListener listener_;
|
161 | rclcpp::Publisher<geometry_msgs::msg::TransformStamped>::SharedPtr publisher_;
|
162 | rclcpp::TimerBase::SharedPtr timer_;
|
163 | std::shared_ptr<Filter> filter_;
|
164 | };
|
165 |
|
166 |
|
167 | class FramePublisher : public rclcpp::Node
|
168 | {
|
169 | public:
|
170 | FramePublisher() : Node("frame_publisher_node"), buffer_(get_clock()), listener_(buffer_)
|
171 | {
|
172 | timer_ = this->create_wall_timer(50ms, std::bind(&FramePublisher::timer_callback, this));
|
173 | broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
|
174 | }
|
175 |
|
176 | private:
|
177 | void timer_callback()
|
178 | {
|
179 | geometry_msgs::msg::TransformStamped transformStamped;
|
180 | try
|
181 | {
|
182 | transformStamped = buffer_.lookupTransform("libsurvive_world", "LHR-BFB7B3C7", tf2::TimePointZero);
|
183 | RCLCPP_INFO(this->get_logger(), "Publishing transform (filtered): "
|
184 | "Translation: [%f, %f, %f], "
|
185 | "Rotation: [x: %f, y: %f, z: %f, w: %f]",
|
186 | transformStamped.transform.translation.x,
|
187 | transformStamped.transform.translation.y,
|
188 | transformStamped.transform.translation.z,
|
189 | transformStamped.transform.rotation.x,
|
190 | transformStamped.transform.rotation.y,
|
191 | transformStamped.transform.rotation.z,
|
192 | transformStamped.transform.rotation.w);
|
193 |
|
194 | transformStamped.child_frame_id = "LHR-BFB7B3C7_filtered";
|
195 |
|
196 | broadcaster_->sendTransform(transformStamped);
|
197 |
|
198 | #ifdef NDEBUG
|
199 | #else
|
200 | RCLCPP_INFO(this->get_logger(), "Published filtered transform.");
|
201 | #endif
|
202 |
|
203 | }
|
204 | catch (tf2::TransformException& ex)
|
205 | {
|
206 | RCLCPP_WARN(this->get_logger(), "Could not transform 'LHR-BFB7B3C7' to 'libsurvive_world': %s", ex.what());
|
207 | }
|
208 | }
|
209 |
|
210 | tf2_ros::Buffer buffer_;
|
211 | tf2_ros::TransformListener listener_;
|
212 | std::shared_ptr<tf2_ros::TransformBroadcaster> broadcaster_;
|
213 | rclcpp::TimerBase::SharedPtr timer_;
|
214 | };
|
215 |
|
216 |
|
217 | int main(int argc, char* argv[])
|
218 | {
|
219 | rclcpp::init(argc, argv);
|
220 |
|
221 | #ifdef NDEBUG
|
222 | #else
|
223 | printf("\nROS run");
|
224 | #endif
|
225 |
|
226 | auto frame_listener_node = std::make_shared<FrameListener>();
|
227 | auto frame_publisher_node = std::make_shared<FramePublisher>();
|
228 |
|
229 | rclcpp::executors::SingleThreadedExecutor executor;
|
230 | executor.add_node(frame_listener_node);
|
231 | executor.add_node(frame_publisher_node);
|
232 | executor.spin();
|
233 |
|
234 | rclcpp::shutdown();
|
235 | return 0;
|
236 | }
|