Skip to content

Commit 300ac7f

Browse files
authored
feat(multi_object_tracker): integrate odometry and transform processes (#9912)
* feat: Add odometry processor to multi-object tracker Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Refactor Odometry class for improved code organization and readability Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * feat: Refactor Odometry class for improved code organization and readability Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Transform objects to world coordinate in Odometry class refactor: Transform objects to world coordinate in Odometry class refactor: Update Odometry class to get transform from tf with source frame ID feat: Update Odometry class to get transform from tf with source frame ID fix: move necessare tr2 header Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * Revert "refactor: Transform objects to world coordinate in Odometry class" This reverts commit efca28a. Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Remove unnecessary tf2 headers from tracker models Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * fix: move transform obtainer to odometry class Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to get transform from tf with source frame ID Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Transform objects to world coordinate in Odometry class Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: remove transformObjects from shapes Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to use 'updateFromTf' instead of 'setOdometryFromTf' Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to use 'updateFromTf' instead of 'setOdometryFromTf' Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update InputManager to include Odometry in constructor Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Move odometry.cpp to lib folder Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * move object transform to input stream Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Add enable_odometry_uncertainty parameter to Odometry constructor Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to return optional Odometry from getOdometryFromTf Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to use tf_cache_ for storing and retrieving transforms Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update Odometry class to use tf_cache_ for storing and retrieving transforms Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: bring odometry covariance modeler into odometry class Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Remove redundant code for updating tf cache in Odometry::updateTfCache Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> * refactor: Update runProcess parameter name to detected_objects Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp> --------- Signed-off-by: Taekjin LEE <taekjin.lee@tier4.jp>
1 parent b7b3dd6 commit 300ac7f

17 files changed

+305
-167
lines changed

perception/autoware_multi_object_tracker/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ set(${PROJECT_NAME}_src
2828
src/processor/input_manager.cpp
2929
)
3030
set(${PROJECT_NAME}_lib
31+
lib/odometry.cpp
3132
lib/association/association.cpp
3233
lib/association/mu_successive_shortest_path/mu_ssp.cpp
3334
lib/object_model/types.cpp

perception/autoware_multi_object_tracker/include/autoware/multi_object_tracker/object_model/shapes.hpp

-4
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ namespace autoware::multi_object_tracker
3030
{
3131
namespace shapes
3232
{
33-
bool transformObjects(
34-
const types::DynamicObjectList & input_msg, const std::string & target_frame_id,
35-
const tf2_ros::Buffer & tf_buffer, types::DynamicObjectList & output_msg);
36-
3733
double get2dIoU(
3834
const types::DynamicObject & source_object, const types::DynamicObject & target_object,
3935
const double min_union_area = 0.01);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2025 TIER IV, 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+
#ifndef AUTOWARE__MULTI_OBJECT_TRACKER__ODOMETRY_HPP_
16+
#define AUTOWARE__MULTI_OBJECT_TRACKER__ODOMETRY_HPP_
17+
18+
#include "autoware/multi_object_tracker/object_model/types.hpp"
19+
20+
#include <rclcpp/rclcpp.hpp>
21+
22+
#include <geometry_msgs/msg/pose_stamped.hpp>
23+
#include <nav_msgs/msg/odometry.hpp>
24+
25+
#include <tf2_ros/buffer.h>
26+
#include <tf2_ros/transform_listener.h>
27+
28+
#include <map>
29+
#include <optional>
30+
#include <string>
31+
32+
namespace autoware::multi_object_tracker
33+
{
34+
35+
class Odometry
36+
{
37+
public:
38+
Odometry(
39+
rclcpp::Node & node, const std::string & world_frame_id,
40+
bool enable_odometry_uncertainty = false);
41+
42+
std::optional<geometry_msgs::msg::Transform> getTransform(
43+
const std::string & source_frame_id, const rclcpp::Time & time) const;
44+
std::optional<geometry_msgs::msg::Transform> getTransform(const rclcpp::Time & time) const;
45+
46+
std::optional<nav_msgs::msg::Odometry> getOdometryFromTf(const rclcpp::Time & time) const;
47+
48+
std::optional<types::DynamicObjectList> transformObjects(
49+
const types::DynamicObjectList & input_objects) const;
50+
51+
private:
52+
rclcpp::Node & node_;
53+
// frame id
54+
std::string ego_frame_id_ = "base_link"; // ego vehicle frame
55+
std::string world_frame_id_; // absolute/relative ground frame
56+
// tf
57+
tf2_ros::Buffer tf_buffer_;
58+
tf2_ros::TransformListener tf_listener_;
59+
60+
public:
61+
bool enable_odometry_uncertainty_ = false;
62+
63+
private:
64+
void updateTfCache(
65+
const rclcpp::Time & time, const geometry_msgs::msg::Transform & transform) const;
66+
67+
// cache of tf
68+
mutable std::map<rclcpp::Time, geometry_msgs::msg::Transform> tf_cache_;
69+
};
70+
71+
} // namespace autoware::multi_object_tracker
72+
73+
#endif // AUTOWARE__MULTI_OBJECT_TRACKER__ODOMETRY_HPP_

perception/autoware_multi_object_tracker/lib/object_model/shapes.cpp

-50
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
#include <boost/geometry.hpp>
2828

2929
#include <tf2/utils.h>
30-
#include <tf2_ros/transform_listener.h>
3130

3231
#include <algorithm>
3332
#include <string>
@@ -37,55 +36,6 @@ namespace autoware::multi_object_tracker
3736
{
3837
namespace shapes
3938
{
40-
inline boost::optional<geometry_msgs::msg::Transform> getTransform(
41-
const tf2_ros::Buffer & tf_buffer, const std::string & source_frame_id,
42-
const std::string & target_frame_id, const rclcpp::Time & time)
43-
{
44-
try {
45-
geometry_msgs::msg::TransformStamped self_transform_stamped;
46-
self_transform_stamped = tf_buffer.lookupTransform(
47-
target_frame_id, source_frame_id, time, rclcpp::Duration::from_seconds(0.5));
48-
return self_transform_stamped.transform;
49-
} catch (tf2::TransformException & ex) {
50-
RCLCPP_WARN_STREAM(rclcpp::get_logger("multi_object_tracker"), ex.what());
51-
return boost::none;
52-
}
53-
}
54-
55-
bool transformObjects(
56-
const types::DynamicObjectList & input_msg, const std::string & target_frame_id,
57-
const tf2_ros::Buffer & tf_buffer, types::DynamicObjectList & output_msg)
58-
{
59-
output_msg = input_msg;
60-
61-
// transform to world coordinate
62-
if (input_msg.header.frame_id != target_frame_id) {
63-
output_msg.header.frame_id = target_frame_id;
64-
tf2::Transform tf_target2objects_world;
65-
tf2::Transform tf_target2objects;
66-
tf2::Transform tf_objects_world2objects;
67-
{
68-
const auto ros_target2objects_world =
69-
getTransform(tf_buffer, input_msg.header.frame_id, target_frame_id, input_msg.header.stamp);
70-
if (!ros_target2objects_world) {
71-
return false;
72-
}
73-
tf2::fromMsg(*ros_target2objects_world, tf_target2objects_world);
74-
}
75-
for (auto & object : output_msg.objects) {
76-
auto & pose_with_cov = object.kinematics.pose_with_covariance;
77-
tf2::fromMsg(pose_with_cov.pose, tf_objects_world2objects);
78-
tf_target2objects = tf_target2objects_world * tf_objects_world2objects;
79-
// transform pose, frame difference and object pose
80-
tf2::toMsg(tf_target2objects, pose_with_cov.pose);
81-
// transform covariance, only the frame difference
82-
pose_with_cov.covariance =
83-
tf2::transformCovariance(pose_with_cov.covariance, tf_target2objects_world);
84-
}
85-
}
86-
return true;
87-
}
88-
8939
inline double getSumArea(const std::vector<autoware::universe_utils::Polygon2d> & polygons)
9040
{
9141
return std::accumulate(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2025 TIER IV, 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+
#include "autoware/multi_object_tracker/odometry.hpp"
16+
17+
#include "autoware/multi_object_tracker/uncertainty/uncertainty_processor.hpp"
18+
19+
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
20+
21+
#include <tf2_ros/create_timer_ros.h>
22+
23+
#include <memory>
24+
#include <string>
25+
26+
namespace autoware::multi_object_tracker
27+
{
28+
29+
Odometry::Odometry(
30+
rclcpp::Node & node, const std::string & world_frame_id, bool enable_odometry_uncertainty)
31+
: node_(node),
32+
world_frame_id_(world_frame_id),
33+
tf_buffer_(node_.get_clock()),
34+
tf_listener_(tf_buffer_),
35+
enable_odometry_uncertainty_(enable_odometry_uncertainty)
36+
{
37+
// Create tf timer
38+
auto cti = std::make_shared<tf2_ros::CreateTimerROS>(
39+
node_.get_node_base_interface(), node_.get_node_timers_interface());
40+
tf_buffer_.setCreateTimerInterface(cti);
41+
}
42+
43+
void Odometry::updateTfCache(
44+
const rclcpp::Time & time, const geometry_msgs::msg::Transform & tf) const
45+
{
46+
// update the tf buffer
47+
tf_cache_.emplace(time, tf);
48+
49+
// remove too old tf
50+
const auto max_tf_age = rclcpp::Duration::from_seconds(1.0);
51+
for (auto it = tf_cache_.begin(); it != tf_cache_.end();) {
52+
if (time - it->first > max_tf_age) {
53+
it = tf_cache_.erase(it);
54+
} else {
55+
++it;
56+
}
57+
}
58+
}
59+
60+
std::optional<geometry_msgs::msg::Transform> Odometry::getTransform(const rclcpp::Time & time) const
61+
{
62+
// check buffer and return if the transform is found
63+
for (const auto & tf : tf_cache_) {
64+
if (tf.first == time) {
65+
return std::optional<geometry_msgs::msg::Transform>(tf.second);
66+
}
67+
}
68+
// if not found, get the transform from tf
69+
return getTransform(ego_frame_id_, time);
70+
}
71+
72+
std::optional<geometry_msgs::msg::Transform> Odometry::getTransform(
73+
const std::string & source_frame_id, const rclcpp::Time & time) const
74+
{
75+
try {
76+
// Check if the frames are ready
77+
std::string errstr; // This argument prevents error msg from being displayed in the terminal.
78+
if (!tf_buffer_.canTransform(
79+
world_frame_id_, source_frame_id, tf2::TimePointZero, tf2::Duration::zero(), &errstr)) {
80+
return std::nullopt;
81+
}
82+
83+
// Lookup the transform
84+
geometry_msgs::msg::TransformStamped self_transform_stamped;
85+
self_transform_stamped = tf_buffer_.lookupTransform(
86+
world_frame_id_, source_frame_id, time, rclcpp::Duration::from_seconds(0.5));
87+
88+
// update the cache
89+
if (source_frame_id == ego_frame_id_) {
90+
updateTfCache(time, self_transform_stamped.transform);
91+
}
92+
93+
return std::optional<geometry_msgs::msg::Transform>(self_transform_stamped.transform);
94+
} catch (tf2::TransformException & ex) {
95+
RCLCPP_WARN_STREAM(rclcpp::get_logger("multi_object_tracker"), ex.what());
96+
return std::nullopt;
97+
}
98+
}
99+
100+
std::optional<nav_msgs::msg::Odometry> Odometry::getOdometryFromTf(const rclcpp::Time & time) const
101+
{
102+
const auto self_transform = getTransform(time);
103+
if (!self_transform) {
104+
return std::nullopt;
105+
}
106+
const auto current_transform = self_transform.value();
107+
108+
nav_msgs::msg::Odometry odometry;
109+
{
110+
odometry.header.stamp = time + rclcpp::Duration::from_seconds(0.00001);
111+
112+
// set the odometry pose
113+
auto & odom_pose = odometry.pose.pose;
114+
odom_pose.position.x = current_transform.translation.x;
115+
odom_pose.position.y = current_transform.translation.y;
116+
odom_pose.position.z = current_transform.translation.z;
117+
odom_pose.orientation = current_transform.rotation;
118+
119+
// set odometry twist
120+
auto & odom_twist = odometry.twist.twist;
121+
odom_twist.linear.x = 10.0; // m/s
122+
odom_twist.linear.y = 0.1; // m/s
123+
odom_twist.angular.z = 0.1; // rad/s
124+
125+
// model the uncertainty
126+
auto & odom_pose_cov = odometry.pose.covariance;
127+
odom_pose_cov[0] = 0.1; // x-x
128+
odom_pose_cov[7] = 0.1; // y-y
129+
odom_pose_cov[35] = 0.0001; // yaw-yaw
130+
131+
auto & odom_twist_cov = odometry.twist.covariance;
132+
odom_twist_cov[0] = 2.0; // x-x [m^2/s^2]
133+
odom_twist_cov[7] = 0.2; // y-y [m^2/s^2]
134+
odom_twist_cov[35] = 0.001; // yaw-yaw [rad^2/s^2]
135+
}
136+
137+
return std::optional<nav_msgs::msg::Odometry>(odometry);
138+
}
139+
140+
std::optional<types::DynamicObjectList> Odometry::transformObjects(
141+
const types::DynamicObjectList & input_objects) const
142+
{
143+
types::DynamicObjectList output_objects = input_objects;
144+
145+
// transform to world coordinate
146+
if (input_objects.header.frame_id != world_frame_id_) {
147+
output_objects.header.frame_id = world_frame_id_;
148+
tf2::Transform tf_target2objects_world;
149+
tf2::Transform tf_target2objects;
150+
tf2::Transform tf_objects_world2objects;
151+
{
152+
const auto ros_target2objects_world =
153+
getTransform(input_objects.header.frame_id, input_objects.header.stamp);
154+
if (!ros_target2objects_world) {
155+
return std::nullopt;
156+
}
157+
tf2::fromMsg(*ros_target2objects_world, tf_target2objects_world);
158+
}
159+
for (auto & object : output_objects.objects) {
160+
auto & pose_with_cov = object.kinematics.pose_with_covariance;
161+
tf2::fromMsg(pose_with_cov.pose, tf_objects_world2objects);
162+
tf_target2objects = tf_target2objects_world * tf_objects_world2objects;
163+
// transform pose, frame difference and object pose
164+
tf2::toMsg(tf_target2objects, pose_with_cov.pose);
165+
// transform covariance, only the frame difference
166+
pose_with_cov.covariance =
167+
tf2::transformCovariance(pose_with_cov.covariance, tf_target2objects_world);
168+
}
169+
}
170+
// Add the odometry uncertainty to the object uncertainty
171+
if (enable_odometry_uncertainty_) {
172+
// Create a modeled odometry message
173+
const auto odometry = getOdometryFromTf(input_objects.header.stamp);
174+
// Add the odometry uncertainty to the object uncertainty
175+
uncertainty::addOdometryUncertainty(odometry.value(), output_objects);
176+
}
177+
178+
return std::optional<types::DynamicObjectList>(output_objects);
179+
}
180+
181+
} // namespace autoware::multi_object_tracker

perception/autoware_multi_object_tracker/lib/tracker/model/bicycle_tracker.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
#include <autoware/universe_utils/ros/msg_covariance.hpp>
3131

3232
#include <bits/stdc++.h>
33-
#include <tf2/LinearMath/Matrix3x3.h>
34-
#include <tf2/LinearMath/Quaternion.h>
3533
#include <tf2/utils.h>
3634

3735
#ifdef ROS_DISTRO_GALACTIC

perception/autoware_multi_object_tracker/lib/tracker/model/pass_through_tracker.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
#include <autoware/universe_utils/ros/msg_covariance.hpp>
2525

2626
#include <bits/stdc++.h>
27-
#include <tf2/LinearMath/Matrix3x3.h>
28-
#include <tf2/LinearMath/Quaternion.h>
2927
#include <tf2/utils.h>
3028

3129
#ifdef ROS_DISTRO_GALACTIC

perception/autoware_multi_object_tracker/lib/tracker/model/pedestrian_tracker.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
#include <autoware/universe_utils/ros/msg_covariance.hpp>
2929

3030
#include <bits/stdc++.h>
31-
#include <tf2/LinearMath/Matrix3x3.h>
32-
#include <tf2/LinearMath/Quaternion.h>
3331
#include <tf2/utils.h>
3432

3533
#ifdef ROS_DISTRO_GALACTIC

perception/autoware_multi_object_tracker/lib/tracker/model/unknown_tracker.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
#include <autoware/universe_utils/ros/msg_covariance.hpp>
2525

2626
#include <bits/stdc++.h>
27-
#include <tf2/LinearMath/Matrix3x3.h>
28-
#include <tf2/LinearMath/Quaternion.h>
2927
#include <tf2/utils.h>
3028

3129
#ifdef ROS_DISTRO_GALACTIC

perception/autoware_multi_object_tracker/lib/tracker/model/vehicle_tracker.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
#include <autoware/universe_utils/ros/msg_covariance.hpp>
3131

3232
#include <bits/stdc++.h>
33-
#include <tf2/LinearMath/Matrix3x3.h>
34-
#include <tf2/LinearMath/Quaternion.h>
3533
#include <tf2/utils.h>
3634

3735
#ifdef ROS_DISTRO_GALACTIC

perception/autoware_multi_object_tracker/lib/tracker/motion_model/bicycle_motion_model.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <autoware/universe_utils/math/unit_conversion.hpp>
2828
#include <autoware/universe_utils/ros/msg_covariance.hpp>
2929

30+
#include <tf2/LinearMath/Quaternion.h>
31+
3032
#include <algorithm>
3133

3234
namespace autoware::multi_object_tracker

0 commit comments

Comments
 (0)