SMACC2
Loading...
Searching...
No Matches
cp_object_tracker_tf.hpp
Go to the documentation of this file.
1// Copyright 2025 Robosoft Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16#include <tf2_ros/buffer.h>
17#include <tf2_ros/transform_broadcaster.h>
18#include <tf2_ros/transform_listener.h>
19#include <smacc2/component.hpp>
21#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
22
24
25namespace cl_foundation_pose
26{
27
28using namespace smacc2::client_core_components;
29
31{
32private:
33 std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
34 std::shared_ptr<tf2_ros::TransformListener> tfListener_;
35 std::shared_ptr<tf2_ros::TransformBroadcaster> tfBroadcaster_;
36
37 std::string global_frame_id_;
38 bool enabled_ = false;
39
40 // Declare a data structure to store the detected objects.
41 std::map<std::string, DetectedObject> detectedObjects;
42 std::mutex m_mutex_;
43
44public:
45 CpObjectTrackerTf(std::string global_frame_id = "map") : global_frame_id_(global_frame_id) {}
46
47 void setEnabled(bool enabled)
48 {
50 enabled_ = enabled;
51 }
52
53 bool isEnabled() { return enabled_; }
54
56 {
57 std::lock_guard<std::mutex> lock(m_mutex_);
58
59 RCLCPP_INFO(
60 getLogger(), "CpObjectTrackerTf::resetPoseEstimation() tracked objects: %ld",
61 detectedObjects.size());
62 for (auto & detectedObject : detectedObjects)
63 {
64 auto & detectedObjectInfo = detectedObject.second;
65 RCLCPP_INFO(
66 getLogger(), "CpObjectTrackerTf::resetPoseEstimation() tracking object: %s",
67 detectedObject.first.c_str());
68
69 detectedObjectInfo.filtered_pose.reset();
70 detectedObjectInfo.historicalPoses_.clear();
71 }
72 detectedObjects.clear();
73 }
74
75 void update() override
76 {
77 RCLCPP_INFO(
78 getLogger(), "CpObjectTrackerTf::update() tracked objects: %ld", detectedObjects.size());
79 std::lock_guard<std::mutex> lock(m_mutex_);
80
81 if (!enabled_) return;
82
83 RCLCPP_INFO(
84 getLogger(), "CpObjectTrackerTf::update() heartbeat, tracked objects: %ld",
85 detectedObjects.size());
86
87 // refresh tracked object poses
88 for (auto & detectedObject : detectedObjects)
89 {
90 RCLCPP_INFO(
91 getLogger(), "CpObjectTrackerTf::update() tracking object: %s",
92 detectedObject.first.c_str());
93
94 auto globalObjectPose = this->updateAndGetGlobalPose(detectedObject.first, global_frame_id_);
95 if (globalObjectPose)
96 {
97 detectedObject.second.filtered_pose = *globalObjectPose;
98 }
99 }
100 }
101
103 {
104 tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->getNode()->get_clock());
105 tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
106 tfBroadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this->getNode());
107 }
108
109 std::optional<geometry_msgs::msg::PoseStamped> updateAndGetGlobalPose(
110 const std::string & child_frame_id, const std::string & frame_id)
111 {
112 RCLCPP_INFO(
113 getLogger(), "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s')", child_frame_id.c_str(),
114 frame_id.c_str());
115 // check in database if the object is already tracked
116 auto object = detectedObjects.find(child_frame_id);
117 DetectedObject * detectedObject = nullptr;
118 if (object != detectedObjects.end()) // already tracked
119 {
120 detectedObject = &object->second;
121 RCLCPP_INFO(
122 getLogger(), "[CpObjectTrackerTf] tracking existing object: %s", child_frame_id.c_str());
123 }
124 else
125 {
126 detectedObjects[child_frame_id] = DetectedObject();
127 detectedObject = &detectedObjects[child_frame_id];
128 RCLCPP_INFO(
129 getLogger(), "[CpObjectTrackerTf] tracking new object: %s", child_frame_id.c_str());
130 }
131
132 if (tfBuffer_->canTransform(frame_id, child_frame_id, rclcpp::Time(0)))
133 {
134 geometry_msgs::msg::PoseStamped pose;
135
136 auto transformStamped = tfBuffer_->lookupTransform(frame_id, child_frame_id, rclcpp::Time(0));
137 pose.header = transformStamped.header;
138 pose.pose.position.x = transformStamped.transform.translation.x;
139 pose.pose.position.y = transformStamped.transform.translation.y;
140 pose.pose.position.z = transformStamped.transform.translation.z;
141 pose.pose.orientation = transformStamped.transform.rotation;
142
143 RCLCPP_INFO(
144 getLogger(), "[CpObjectTrackerTf] *updateAndGetGlobalPose('%s', '%s') pose: %f, %f, %f",
145 child_frame_id.c_str(), frame_id.c_str(), pose.pose.position.x, pose.pose.position.y,
146 tf2::getYaw(pose.pose.orientation));
147
148 auto & historicalPoses_ = detectedObject->historicalPoses_;
149 RCLCPP_INFO(
150 getLogger(), "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') historical poses: %ld",
151 child_frame_id.c_str(), frame_id.c_str(), historicalPoses_.size());
152
153 const size_t MAX_HISTORY = 512;
154 if (historicalPoses_.size() > MAX_HISTORY)
155 {
156 RCLCPP_INFO(
157 getLogger(),
158 "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') historical poses, popping oldest "
159 "pose",
160 child_frame_id.c_str(), frame_id.c_str());
161 historicalPoses_.erase(historicalPoses_.begin());
162 }
163 RCLCPP_INFO(
164 getLogger(),
165 "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') historical poses, pushing new pose",
166 child_frame_id.c_str(), frame_id.c_str());
167 historicalPoses_.push_back(pose);
168
169 RCLCPP_INFO(
170 getLogger(),
171 "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') -presort- historical poses: %ld",
172 child_frame_id.c_str(), frame_id.c_str(), historicalPoses_.size());
173
174 // compute median position in x
175 std::sort(
176 historicalPoses_.begin(), historicalPoses_.end(),
177 [](const geometry_msgs::msg::PoseStamped & a, const geometry_msgs::msg::PoseStamped & b)
178 { return a.pose.position.x < b.pose.position.x; });
179
180 RCLCPP_INFO(
181 getLogger(),
182 "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s')- presort- historical poses: %ld",
183 child_frame_id.c_str(), frame_id.c_str(), historicalPoses_.size());
184
185 geometry_msgs::msg::PoseStamped medianPose;
186 medianPose.pose.position.x = historicalPoses_[historicalPoses_.size() / 2].pose.position.x;
187
188 // compute median position in y
189 std::sort(
190 historicalPoses_.begin(), historicalPoses_.end(),
191 [](const geometry_msgs::msg::PoseStamped & a, const geometry_msgs::msg::PoseStamped & b)
192 { return a.pose.position.y < b.pose.position.y; });
193
194 medianPose.pose.position.y = historicalPoses_[historicalPoses_.size() / 2].pose.position.y;
195 medianPose.header = pose.header;
196
197 detectedObject->filtered_pose = medianPose;
198
199 RCLCPP_INFO(
200 getLogger(),
201 "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') filtered pose [%ld samples]: %f, "
202 "%f, %f",
203 child_frame_id.c_str(), frame_id.c_str(), detectedObject->historicalPoses_.size(),
204 detectedObject->filtered_pose->pose.position.x,
205 detectedObject->filtered_pose->pose.position.y,
206 tf2::getYaw(detectedObject->filtered_pose->pose.orientation));
208
209 return detectedObject->filtered_pose;
210 }
211
212 RCLCPP_INFO(
213 getLogger(), "[CpObjectTrackerTf] updateAndGetGlobalPose('%s', '%s') failed",
214 child_frame_id.c_str(), frame_id.c_str());
215
216 return std::nullopt;
217 }
218};
219
220} // namespace cl_foundation_pose
std::optional< geometry_msgs::msg::PoseStamped > updateAndGetGlobalPose(const std::string &child_frame_id, const std::string &frame_id)
std::shared_ptr< tf2_ros::TransformBroadcaster > tfBroadcaster_
std::shared_ptr< tf2_ros::Buffer > tfBuffer_
std::map< std::string, DetectedObject > detectedObjects
std::shared_ptr< tf2_ros::TransformListener > tfListener_
CpObjectTrackerTf(std::string global_frame_id="map")
rclcpp::Logger getLogger() const
rclcpp::Node::SharedPtr getNode()
std::optional< geometry_msgs::msg::PoseStamped > filtered_pose
std::vector< geometry_msgs::msg::PoseStamped > historicalPoses_