Skip to content

Commit d0f6f6d

Browse files
committed
Calculate Relative Velocity Based on Odom
1 parent b6bf980 commit d0f6f6d

File tree

7 files changed

+456
-12
lines changed

7 files changed

+456
-12
lines changed

README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,18 @@ The `min_value` and `max_value` parameter specifies the minimum and maximum valu
7676

7777
Further information about the filter configurations can be found in the ARS408 technical documentation
7878

79-
## Configuring Motion Input
79+
### Configuring Motion Input
8080

8181
This driver optionally lets you input your odometry into the sensor, which will be intepreted by the radars and used in their own tracking algorithms. There are two parameters you need to set:
8282

8383
- `odom_topic_name`: string
8484
- `radar_[index].send_motion`: bool
8585

86+
## Relative Velocity
87+
88+
Since the ARS408 tracks return their velocities under the assumption that the radar sensor frame is stationary, this driver will calculate the velocity of tracks relative to odom frame, assuming you've added an odometry topic as a parameter under `odom_topic_name`.
89+
90+
You can also set:
91+
- `transform_timeout`: Used in `lookupTransform`
92+
- `robot_base_frame`: Default is `base_link`
93+

radar_conti_ars408/src/CMakeLists.txt

+16-4
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,31 @@ install(DIRECTORY
167167

168168

169169
if(BUILD_TESTING)
170+
find_package(Catch2 2 REQUIRED)
171+
set(test_dependencies
172+
Catch2
173+
)
174+
add_executable(test_radar_transforms test/test_radar_transforms.cpp)
175+
target_link_libraries(test_radar_transforms Catch2::Catch2 ${library_name})
176+
ament_target_dependencies(test_radar_transforms
177+
${test_dependencies}
178+
${dependencies}
179+
)
180+
add_test(NAME test_radar_transforms COMMAND catch_tests)
181+
170182
# Until CI has VCAN support, don't run this test
171183
if(NOT DEFINED ENV{CI})
172184
find_package(Catch2 2 REQUIRED)
173185
set(test_dependencies
174186
Catch2
175187
)
176-
add_executable(catch_tests test/test_radar_conti_ars408_component.cpp)
177-
target_link_libraries(catch_tests Catch2::Catch2 ${library_name})
178-
ament_target_dependencies(catch_tests
188+
add_executable(test_radar_conti_ars408_component test/test_radar_conti_ars408_component.cpp)
189+
target_link_libraries(test_radar_conti_ars408_component Catch2::Catch2 ${library_name})
190+
ament_target_dependencies(test_radar_conti_ars408_component
179191
${test_dependencies}
180192
${dependencies}
181193
)
182-
add_test(NAME catch_tests COMMAND catch_tests)
194+
add_test(NAME test_radar_conti_ars408_component COMMAND catch_tests)
183195
endif()
184196
endif()
185197

radar_conti_ars408/src/config/radar.yaml

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
radar_node:
22
ros__parameters:
3-
can_channel: "can0"
3+
can_channel: "vcan0"
44
object_list_topic_name: "object_list"
55
marker_array_topic_name: "marker_array"
66
radar_tracks_topic_name: "radar_tracks"
77
obstacle_array_topic_name: "obstacle_array"
88
filter_config_topic_name: "filter_config"
99
radar_state_topic_name: "radar_state"
10-
odom_topic_name: "/vehicle/odometry"
1110

1211
radar_0:
1312
#link of radar, should be relative to base_link
1413
link_name: "front_radar_link"
15-
send_motion: true
16-
1714
radarcfg_sensor_id: 0
1815
radarcfg_sensor_id_valid: 1
1916
radarcfg_storeinnvm: 1

radar_conti_ars408/src/include/radar_conti_ars408_component.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "visibility_control.h"
1111
#include "rclcpp/rclcpp.hpp"
1212
#include "nav_msgs/msg/odometry.hpp"
13+
#include "tf2_ros/transform_listener.h"
14+
#include "tf2_ros/buffer.h"
1315

1416
#include "can_msgs/msg/frame.hpp"
1517
#include "std_msgs/msg/string.hpp"
@@ -338,6 +340,11 @@ namespace FHAC
338340
void initializeFilterConfigs();
339341
void publishFilterConfigMetadata();
340342
void odomCallback(const nav_msgs::msg::Odometry::SharedPtr msg);
343+
nav_msgs::msg::Odometry vehicle_odometry_;
344+
std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
345+
std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
346+
rclcpp::Duration transform_timeout_{0, 0};
347+
341348
void sendMotionInputSignals(const size_t &sensor_id, uint8_t direction, double speed, double yaw_rate);
342349
void publishFovMetadata();
343350

@@ -371,6 +378,7 @@ namespace FHAC
371378
std::unique_ptr<bond::Bond> bond_{nullptr};
372379

373380
std::vector<std::string> radar_link_names_;
381+
std::string robot_base_frame_;
374382
std::vector<bool> filter_config_initialized_list_;
375383

376384
// ##################################
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#ifndef TRANSFORMS_HPP
2+
#define TRANSFORMS_HPP
3+
4+
#include <nav_msgs/msg/odometry.hpp>
5+
#include <geometry_msgs/msg/transform_stamped.hpp>
6+
#include <geometry_msgs/msg/vector3.hpp>
7+
#include <tf2/LinearMath/Quaternion.h>
8+
#include <tf2/LinearMath/Matrix3x3.h>
9+
#include <tf2_ros/buffer.h>
10+
#include <rclcpp/rclcpp.hpp>
11+
#include <cmath>
12+
#include <memory>
13+
#include <string>
14+
15+
namespace radar_transforms
16+
{
17+
nav_msgs::msg::Odometry transform2DOdom(const nav_msgs::msg::Odometry &odometry, const std::shared_ptr<tf2_ros::Buffer> &tf_buffer, std::string &sensor_frame_id, std::string &base_frame_id, rclcpp::Duration &transform_timeout, const rclcpp::Time &stamp, rclcpp::Clock::SharedPtr clock)
18+
{
19+
20+
geometry_msgs::msg::TransformStamped base_link_to_sensor_transform;
21+
try
22+
{
23+
base_link_to_sensor_transform = tf_buffer->lookupTransform(sensor_frame_id, base_frame_id, stamp, transform_timeout);
24+
}
25+
catch (tf2::TimeoutException &exception)
26+
{
27+
RCLCPP_ERROR_THROTTLE(rclcpp::get_logger("radar_conti_ars408"), *clock, 1000, "Transform timed out: %s", exception.what());
28+
return nav_msgs::msg::Odometry{};
29+
}
30+
catch (tf2::TransformException &exception)
31+
{
32+
RCLCPP_ERROR_THROTTLE(rclcpp::get_logger("radar_conti_ars408"), *clock, 1000, "Transform failed: %s", exception.what());
33+
return nav_msgs::msg::Odometry{};
34+
}
35+
nav_msgs::msg::Odometry transformed_odometry_base_link;
36+
nav_msgs::msg::Odometry transformed_odometry_sensor;
37+
38+
// 1. get velocity of an offset frame, with location defined by transform
39+
// This is the sensor's velocity oriented in the direction of base_link but the magnitudes are relative to a stationary odom frame
40+
transformed_odometry_base_link.header = odometry.header;
41+
transformed_odometry_base_link.header.frame_id = base_link_to_sensor_transform.header.frame_id;
42+
transformed_odometry_base_link.child_frame_id = base_link_to_sensor_transform.child_frame_id;
43+
transformed_odometry_base_link.twist.twist.linear.x = odometry.twist.twist.linear.x;
44+
transformed_odometry_base_link.twist.twist.linear.y = odometry.twist.twist.linear.y;
45+
transformed_odometry_base_link.twist.twist.angular.z = odometry.twist.twist.angular.z;
46+
47+
tf2::Quaternion q(
48+
base_link_to_sensor_transform.transform.rotation.x,
49+
base_link_to_sensor_transform.transform.rotation.y,
50+
base_link_to_sensor_transform.transform.rotation.z,
51+
base_link_to_sensor_transform.transform.rotation.w);
52+
tf2::Matrix3x3 m(q);
53+
double roll, pitch, yaw;
54+
m.getRPY(roll, pitch, yaw);
55+
56+
transformed_odometry_sensor = transformed_odometry_base_link;
57+
transformed_odometry_sensor.twist.twist.linear.x = cos(yaw) * transformed_odometry_base_link.twist.twist.linear.x - sin(yaw) * transformed_odometry_base_link.twist.twist.linear.y;
58+
transformed_odometry_sensor.twist.twist.linear.y = sin(yaw) * transformed_odometry_base_link.twist.twist.linear.x + cos(yaw) * transformed_odometry_base_link.twist.twist.linear.y;
59+
transformed_odometry_base_link.twist.twist.angular.z = odometry.twist.twist.angular.z;
60+
61+
return transformed_odometry_sensor;
62+
}
63+
64+
geometry_msgs::msg::Vector3 correctObstacleVelocity(
65+
nav_msgs::msg::Odometry &corrected_odom,
66+
const geometry_msgs::msg::Vector3 &raw_obj_velocity,
67+
const geometry_msgs::msg::Point &raw_obj_pos)
68+
{
69+
// Calculate the angular velocity components of object velocity with respect to odom frame
70+
auto r = hypot(raw_obj_pos.x, raw_obj_pos.y);
71+
auto tangential_velocity = r * corrected_odom.twist.twist.angular.z;
72+
auto theta = atan2(raw_obj_pos.y, raw_obj_pos.x);
73+
auto tangential_velocity_x = tangential_velocity * sin(theta);
74+
auto tangential_velocity_y = -tangential_velocity * cos(theta);
75+
geometry_msgs::msg::Vector3 corrected_velocity;
76+
corrected_velocity.x = raw_obj_velocity.x + corrected_odom.twist.twist.linear.x - tangential_velocity_x;
77+
corrected_velocity.y = raw_obj_velocity.y + corrected_odom.twist.twist.linear.y - tangential_velocity_y;
78+
corrected_velocity.z = raw_obj_velocity.z; // Assuming no change in z
79+
return corrected_velocity;
80+
}
81+
}
82+
83+
#endif // TRANSFORMS_HPP

radar_conti_ars408/src/src/radar_conti_ars408_component.cpp

+24-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <utility>
99
#include <math.h>
1010
#include <fmt/core.h>
11+
#include <tf2/LinearMath/Quaternion.h>
12+
#include "../include/radar_transforms.hpp"
1113

1214
#define _USE_MATH_DEFINES
1315

@@ -52,15 +54,20 @@ namespace FHAC
5254
auto node = shared_from_this();
5355
node->declare_parameter("can_channel", rclcpp::ParameterValue(""));
5456
node->declare_parameter("odom_topic_name", rclcpp::ParameterValue(""));
57+
node->declare_parameter("robot_base_frame", rclcpp::ParameterValue("base_link"));
58+
node->declare_parameter("transform_timeout", rclcpp::ParameterValue(0.1));
5559
node->declare_parameter("object_list_topic_name", rclcpp::ParameterValue("ars408/objectlist"));
5660
node->declare_parameter("marker_array_topic_name", rclcpp::ParameterValue("ars408/marker_array"));
5761
node->declare_parameter("radar_tracks_topic_name", rclcpp::ParameterValue("ars408/radar_tracks"));
5862
node->declare_parameter("obstacle_array_topic_name", rclcpp::ParameterValue("ars408/obstacle_array"));
5963
node->declare_parameter("filter_config_topic_name", rclcpp::ParameterValue("ars408/filter_config"));
6064
node->declare_parameter("radar_state_topic_name", rclcpp::ParameterValue("ars408/radar_state"));
6165

66+
double transform_timeout_double;
6267
node->get_parameter("can_channel", can_channel_);
6368
node->get_parameter("odom_topic_name", odom_topic_name_);
69+
node->get_parameter("robot_base_frame", robot_base_frame_);
70+
node->get_parameter("transform_timeout", transform_timeout_double);
6471
node->get_parameter("object_list_topic_name", object_list_topic_name_);
6572
node->get_parameter("marker_array_topic_name", marker_array_topic_name_);
6673
node->get_parameter("radar_tracks_topic_name", radar_tracks_topic_name_);
@@ -248,6 +255,9 @@ namespace FHAC
248255
odometry_subscriber_ = create_subscription<nav_msgs::msg::Odometry>(odom_topic_name_, qos_settings, std::bind(&radar_conti_ars408::odomCallback, this, std::placeholders::_1));
249256
}
250257

258+
tf_buffer_ = std::make_unique<tf2_ros::Buffer>(node->get_clock());
259+
tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
260+
transform_timeout_ = rclcpp::Duration::from_seconds(transform_timeout_double);
251261
generateUUIDTable();
252262

253263
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
@@ -695,6 +705,12 @@ namespace FHAC
695705

696706
std::map<int, radar_conti_ars408_msgs::msg::Object>::iterator itr;
697707

708+
nav_msgs::msg::Odometry corrected_odom;
709+
if (!odom_topic_name_.empty())
710+
{
711+
corrected_odom = radar_transforms::transform2DOdom(vehicle_odometry_, tf_buffer_, radar_link_names_[sensor_id], robot_base_frame_, transform_timeout_, this->get_clock()->now(), this->get_clock());
712+
}
713+
698714
for (itr = object_map_list_[sensor_id].begin(); itr != object_map_list_[sensor_id].end(); ++itr)
699715
{
700716

@@ -729,9 +745,11 @@ namespace FHAC
729745
radar_track.position.y = itr->second.object_general.obj_distlat.data;
730746
radar_track.position.z = 0.0;
731747

732-
radar_track.velocity.x = itr->second.object_general.obj_vrellong.data;
733-
radar_track.velocity.y = itr->second.object_general.obj_vrellat.data;
734-
radar_track.velocity.z = 0.0;
748+
geometry_msgs::msg::Vector3 radar_track_vel_raw;
749+
radar_track_vel_raw.x = itr->second.object_general.obj_vrellong.data;
750+
radar_track_vel_raw.y = itr->second.object_general.obj_vrellat.data;
751+
radar_track_vel_raw.z = 0.0;
752+
radar_track.velocity = radar_transforms::correctObstacleVelocity(corrected_odom, radar_track_vel_raw, radar_track.position);
735753

736754
radar_track.acceleration.x = 0.0;
737755
radar_track.acceleration.y = 0.0;
@@ -1194,6 +1212,9 @@ namespace FHAC
11941212

11951213
void radar_conti_ars408::odomCallback(const nav_msgs::msg::Odometry::SharedPtr msg)
11961214
{
1215+
1216+
vehicle_odometry_ = *msg;
1217+
11971218
// Threshold for standstill detection
11981219
const double standstill_threshold = 0.01;
11991220

0 commit comments

Comments
 (0)