vive_filter.cpp


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
}