Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9812d10
basic package setup
jorgenfj Oct 19, 2025
e4e0639
removed 'lib' from gitignore
jorgenfj Oct 19, 2025
acaba30
generic pose sub setup
jorgenfj Oct 22, 2025
72e8211
removed unused member in favor of variant
jorgenfj Oct 22, 2025
8a117c8
config setup
jorgenfj Oct 26, 2025
5d7b9b7
track manager impl
jorgenfj Oct 26, 2025
4ceeeb9
ValidPoseMsg concept
jorgenfj Dec 10, 2025
1c9c7f9
Variant alias
jorgenfj Dec 15, 2025
b9f1891
refactor eluer to quat
jorgenfj Dec 22, 2025
ced39d2
Merge branch 'main' into ipda-pose-filtering
jorgenfj Dec 22, 2025
8cc0e02
added confirm_tracks function
jorgenfj Dec 22, 2025
9be9e2a
landmark support
jorgenfj Dec 24, 2025
7acc72c
added debug pose publisher
jorgenfj Dec 25, 2025
5396c1e
renamed pkg: ipda_pose -> pose
jorgenfj Dec 25, 2025
10bf57c
tests and doxygen comments
jorgenfj Dec 30, 2025
882f985
changed alpha calculation. Added README
jorgenfj Dec 30, 2025
b73fec7
pdaf orientation error-state
jorgenfj Jan 1, 2026
aeac20c
added landmark array publisher
jorgenfj Jan 6, 2026
65e31a6
added landmarkarray pub
jorgenfj Jan 6, 2026
610210a
added enu-ned rotation
jorgenfj Jan 6, 2026
9d65c95
Merge branch 'main' into ipda-pose-filtering
jorgenfj Jan 6, 2026
5fee31b
pr fixes
jorgenfj Jan 9, 2026
4072a26
removed unused stuff. Praise clangd
jorgenfj Jan 9, 2026
6877e66
landmark filtering refactor
jorgenfj Feb 14, 2026
32915d1
Merge branch 'main' into ipda-pose-filtering
jorgenfj Feb 15, 2026
b9afd4c
Merge branch 'main' into ipda-pose-filtering
jorgenfj Feb 18, 2026
e39ff3f
Merge branch 'main' into ipda-pose-filtering
jorgenfj Feb 18, 2026
bb8ab35
update for new landmark_msg type def
jorgenfj Feb 19, 2026
ce07185
fixed tests, update default noise levels
jorgenfj Feb 19, 2026
15ef772
Merge branch 'main' into ipda-pose-filtering
jorgenfj Feb 21, 2026
9028d66
Merge branch 'main' into ipda-pose-filtering
jorgenfj Mar 1, 2026
633dc01
sleep for stable op mode in sim tests
jorgenfj Mar 1, 2026
fa728e6
fix bug in get_tracks_by_type
jorgenfj Mar 7, 2026
2e49c7b
Merge branch 'main' into ipda-pose-filtering
jorgenfj Mar 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ install/
log/
build/
bin/
lib/
msg_gen/
srv_gen/
msg/*Action.msg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Eigen::Quaterniond PoseActionServerNode::average_quaternions(
const std::vector<Eigen::Quaterniond>& quaternions) {
Eigen::Matrix4d M = Eigen::Matrix4d::Zero();
std::ranges::for_each(quaternions, [&](const auto& q) {
M += q.coeffs() * q.coeffs().transpose();
M += q.normalized().coeffs() * q.normalized().coeffs().transpose();
});

Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> eigensolver(M);
Expand Down
111 changes: 111 additions & 0 deletions filtering/pose_filtering/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
cmake_minimum_required(VERSION 3.8)
project(pose_filtering)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(message_filters REQUIRED)
find_package(vortex_utils REQUIRED)
find_package(vortex_utils_ros REQUIRED)
find_package(vortex_utils_ros_tf REQUIRED)
find_package(vortex_filtering REQUIRED)
find_package(vortex_msgs REQUIRED)

include_directories(include)

set(CORE_LIB_NAME "${PROJECT_NAME}")

add_library(${CORE_LIB_NAME} SHARED
src/lib/pose_track_manager.cpp
)

target_include_directories(${CORE_LIB_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

ament_target_dependencies(${CORE_LIB_NAME}
vortex_filtering
vortex_utils
vortex_utils_ros
vortex_utils_ros_tf
vortex_msgs
)

set(COMPONENT_LIB_NAME "${PROJECT_NAME}_component")

add_library(${COMPONENT_LIB_NAME} SHARED
src/ros/${PROJECT_NAME}_ros.cpp
src/ros/${PROJECT_NAME}_ros_debug.cpp
src/ros/${PROJECT_NAME}_ros_conversions.cpp
)

ament_target_dependencies(${COMPONENT_LIB_NAME}
rclcpp
rclcpp_components
geometry_msgs
tf2
tf2_ros
tf2_geometry_msgs
message_filters
vortex_filtering
vortex_utils
vortex_utils_ros
vortex_utils_ros_tf
)

target_link_libraries(${COMPONENT_LIB_NAME} ${PROJECT_NAME})

rclcpp_components_register_node(
${COMPONENT_LIB_NAME}
PLUGIN "PoseFilteringNode"
EXECUTABLE ${PROJECT_NAME}_node
)

install(
TARGETS
${CORE_LIB_NAME}
${COMPONENT_LIB_NAME}
EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

install(
DIRECTORY include/
DESTINATION include
)

install(DIRECTORY
launch
config
DESTINATION share/${PROJECT_NAME}/
)

ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(
vortex_filtering
vortex_utils
)
ament_export_include_directories(include)
ament_export_libraries(${CORE_LIB_NAME})

if(BUILD_TESTING)
add_subdirectory(test)
endif()

ament_package()
119 changes: 119 additions & 0 deletions filtering/pose_filtering/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# pose_filtering

ROS 2 package providing pose-based multi-target tracking using an IPDA
filter for position together with independent orientation logic.

This package transforms incoming pose-like messages into a common
target frame, associates measurements to tracks using spatial and angular
gating, maintains an IPDA filter per track for position estimation and
existence management, and performs a separate orientation update by
averaging measurement quaternions and applying spherical linear
interpolation (slerp) to smoothly update the track orientation.

## Features

- Multi-target tracking using an IPDA (Integrated Probabilistic Data
Association) filter for 3D position state estimation.
- Separate orientation handling: angular gating, quaternion averaging and
slerp-based smoothing so orientation updates remain numerically stable
and physically meaningful.
- TF2-aware subscriptions (message_filters + tf2 message filter) so
incoming messages are automatically dropped until transform to the
configured target frame is available.
- Supports several ROS message inputs (see below) and publishes a
`geometry_msgs/PoseArray` containing the current tracked poses.

## Supported input messages

The node accepts the following message types as input measurements:

- `geometry_msgs/msg/PoseStamped`
- `geometry_msgs/msg/PoseArray`
- `geometry_msgs/msg/PoseWithCovarianceStamped`
- `vortex_msgs/msg/LandmarkArray`

The code uses a templated message type (PoseMsgT) and checks at
compile-time which types are supported. Incoming measurements are
transformed to the configured `target_frame` before being processed.

## Outputs

- `geometry_msgs/msg/PoseArray` — the current set of tracked poses in the
configured target frame. Each Pose contains the track position and a
quaternion orientation representing the track's current orientation.
- (DEBUG) `vortex_msgs/msg/PoseEulerStamped` topics with
measurement-level and track-state-level pose messages when debug mode
is enabled. Only publishes the first element of incoming measurements and stored tracks.


## How the filtering works

Position (IPDA):

- Each track runs a 3D IPDA filter instance (a Gaussian state: mean +
covariance) where the motion model is a small constant-dynamics model
and the sensor model maps state to direct position measurements.
- IPDA takes care of measurement association probabilities, gating and
existence management (confirmation / deletion thresholds). This
provides robust data association for cluttered measurements and
handles tracks appearing/disappearing over time.

Orientation (SO(3) PDAF):

Orientation is handled by a dedicated PDAF-style filter operating on
the SO(3) manifold. Each track
maintains:

- a quaternion mean q (the track orientation), and
- a small Gaussian error state in the 3D tangent space (so(3)) around
that mean.


The log and exp maps are mathematically defined for all unit quaternions
but the Kalman/PDAF linearization on the tangent space assumes small
errors. The implementation gates measurements and uses configurable
covariances to keep tangent residuals small; if large-angle residuals
occur they will either be gated out or yield small β (limited effect).

## Library export and usage

The core tracking logic (`PoseTrackManager`) is built and exported as a
shared library by this package so other packages can reuse the track
management functionality without pulling in the ROS node.

CMake / ament usage

In a consuming CMake-based package (ament_cmake) you can link against
the library like this:

```cmake
find_package(pose_filtering REQUIRED)

add_executable(my_node src/my_node.cpp)

# Link to the exported library target (the package exports the target
# with the same name as the package: `pose_filtering`)
target_link_libraries(my_node PRIVATE pose_filtering)

```

The package installs its public headers under `include/` so simply
including the header below in your code will work after linking:

```cpp
#include <pose_filtering/lib/pose_track_manager.hpp>

int main() {
vortex::filtering::TrackManagerConfig cfg;
// configure cfg.ipda, cfg.dyn_mod, cfg.sensor_mod, cfg.existence, etc.

vortex::filtering::PoseTrackManager manager(cfg);

std::vector<vortex::utils::types::Pose> measurements;
double dt = 0.1; // seconds
manager.step(measurements, dt);

const auto& tracks = manager.get_tracks();
// iterate tracks for id, pose, existence probability, etc.
}
```
37 changes: 37 additions & 0 deletions filtering/pose_filtering/config/pose_filtering.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**:
ros__parameters:
pose_sub_topic: "/aruco_detector/board"
pose_array_pub_topic: "/filtered_pose_array"
landmark_pub_topic: "/filtered_landmarks"
Comment on lines +3 to +5
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason these topics are not part of the topic list in orca.yaml, like the rest of the packages in vortex-auv?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package is not supposed to be used in production. Only for tuning/testing and debugging. The plan is to have the landmark server import the /lib part of this package. So this package will mainly be used to tune for various different obejcts etc.

target_frame: "odom"
timer_rate_ms: 200
enu_ned_rotation: true

config:
existence:
confirmation_threshold: 0.6
deletion_threshold: 0.2
initial_existence_probability: 0.4

gate:
min_pos_error: 0.0 # in meters
max_pos_error: 0.5 # in meters
min_ori_error: 0.0 # in radians
max_ori_error: 0.5 # in radians

dyn_mod_std_dev: 0.2
sens_mod_std_dev: 0.2

init_pos_std_dev: 0.1
init_ori_std_dev: 0.05

mahalanobis_gate_threshold: 2.5 # in std deviations
prob_of_detection: 0.5
clutter_intensity: 0.01
prob_of_survival: 0.95
estimate_clutter: false

debug:
enable: true
topic_name_meas: "/pose_debug_meas"
topic_name_state: "/pose_debug_state"
Loading