-
Notifications
You must be signed in to change notification settings - Fork 24
Pose filtering #655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jorgenfj
wants to merge
23
commits into
main
Choose a base branch
from
ipda-pose-filtering
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Pose filtering #655
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
9812d10
basic package setup
jorgenfj e4e0639
removed 'lib' from gitignore
jorgenfj acaba30
generic pose sub setup
jorgenfj 72e8211
removed unused member in favor of variant
jorgenfj 8a117c8
config setup
jorgenfj 5d7b9b7
track manager impl
jorgenfj 4ceeeb9
ValidPoseMsg concept
jorgenfj 1c9c7f9
Variant alias
jorgenfj b9f1891
refactor eluer to quat
jorgenfj ced39d2
Merge branch 'main' into ipda-pose-filtering
jorgenfj 8cc0e02
added confirm_tracks function
jorgenfj 9be9e2a
landmark support
jorgenfj 7acc72c
added debug pose publisher
jorgenfj 5396c1e
renamed pkg: ipda_pose -> pose
jorgenfj 10bf57c
tests and doxygen comments
jorgenfj 882f985
changed alpha calculation. Added README
jorgenfj b73fec7
pdaf orientation error-state
jorgenfj aeac20c
added landmark array publisher
jorgenfj 65e31a6
added landmarkarray pub
jorgenfj 610210a
added enu-ned rotation
jorgenfj 9d65c95
Merge branch 'main' into ipda-pose-filtering
jorgenfj 5fee31b
pr fixes
jorgenfj 4072a26
removed unused stuff. Praise clangd
jorgenfj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,6 @@ install/ | |
| log/ | ||
| build/ | ||
| bin/ | ||
| lib/ | ||
| msg_gen/ | ||
| srv_gen/ | ||
| msg/*Action.msg | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| 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 | ||
| src/lib/orientation_filter.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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /**: | ||
| ros__parameters: | ||
| pose_sub_topic: "/aruco_detector/board" | ||
| pose_array_pub_topic: "/filtered_pose_array" | ||
| landmark_pub_topic: "/filtered_landmarks" | ||
| target_frame: "odom" | ||
| timer_rate_ms: 100 | ||
| enu_ned_rotation: true | ||
|
|
||
| max_angle_gate_threshold: 0.4 # in radians | ||
|
|
||
| # IPDA config | ||
| position: | ||
| ipda: | ||
| prob_of_survival: 0.95 | ||
| estimate_clutter: false | ||
| prob_of_detection: 0.5 | ||
| mahalanobis_gate_threshold: 2.5 # in std deviations | ||
| min_gate_threshold: 1.0 # in meters | ||
| max_gate_threshold: 10.0 # in meters | ||
| clutter_intensity: 0.01 | ||
| initial_position_std: 0.1 | ||
|
|
||
| # Dyn model config | ||
| dyn_mod: | ||
| std_dev: 0.2 | ||
|
|
||
| # Sensor model config | ||
| sensor_mod: | ||
| std_dev: 0.2 | ||
|
|
||
| # Existence management config | ||
| existence: | ||
| confirmation_threshold: 0.6 | ||
| deletion_threshold: 0.2 | ||
| initial_existence_probability: 0.4 | ||
|
|
||
| orientation: | ||
| pdaf: | ||
| prob_of_detection: 0.95 | ||
| clutter_intensity: 0.01 | ||
| mahalanobis_gate_threshold: 2.0 # in std deviations | ||
| initial_orientation_std: 0.1 | ||
|
|
||
| dyn_mod: | ||
| std_dev: 0.05 | ||
|
|
||
| sensor_mod: | ||
| std_dev: 0.05 | ||
|
|
||
| debug: | ||
| enable: true | ||
| topic_name_meas: "/pose_debug_meas" | ||
| topic_name_state: "/pose_debug_state" | ||
64 changes: 64 additions & 0 deletions
64
filtering/pose_filtering/include/pose_filtering/lib/orientation_filter.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| #ifndef POSE_FILTERING__LIB__ORIENTATION_FILTER_HPP_ | ||
| #define POSE_FILTERING__LIB__ORIENTATION_FILTER_HPP_ | ||
|
|
||
| #include <Eigen/Dense> | ||
| #include <vector> | ||
| #include <vortex_filtering/vortex_filtering.hpp> | ||
| #include "typedefs.hpp" | ||
|
|
||
| namespace vortex::filtering { | ||
|
|
||
| /** | ||
| * @brief Orientation filter implementing prediction and PDAF update on SO(3). | ||
| * | ||
| * The filter represents the orientation mean as a quaternion and keeps | ||
| * an error-state Gaussian in the 3D tangent space. Measurement updates | ||
| * are performed by mapping quaternion residuals into the tangent space | ||
| * (log map), performing a PDAF-style weighted update, and applying the | ||
| * update via the exponential map back to the quaternion manifold. | ||
| */ | ||
| class OrientationFilter { | ||
| public: | ||
| /** | ||
| * @brief Construct an OrientationFilter | ||
| * @param config Configuration struct | ||
| */ | ||
| explicit OrientationFilter(const OrientationFilterConfig& config); | ||
|
|
||
| /** | ||
| * @brief Run a single filter step (prediction + update) for orientation. | ||
| * @param measurements Quaternion measurements associated with the | ||
| * current track | ||
| * @param dt Time step in seconds used for prediction / covariance growth | ||
| * @param state OrientationState instance that will be updated with the | ||
| * new orientation mean and error-state | ||
| */ | ||
| void step(const std::vector<Eigen::Quaterniond>& measurements, | ||
| double dt, | ||
| OrientationState& state); | ||
|
|
||
| private: | ||
| /** | ||
| * @brief Map a quaternion to the so(3) tangent vector (log map). | ||
| * @param q_in Quaternion representing the relative rotation | ||
| * @return Vector in R^3 representing the axis * angle | ||
| */ | ||
| Eigen::Vector3d so3_log_quat(const Eigen::Quaterniond& q_in); | ||
|
|
||
| /** | ||
| * @brief Map a tangent vector back to a quaternion (exp map). | ||
| * @param rvec Tangent vector (axis * angle) | ||
| * @return Quaternion corresponding to the rotation | ||
| */ | ||
| Eigen::Quaterniond so3_exp_quat(const Eigen::Vector3d& rvec); | ||
|
|
||
| DynMod dyn_mod_; | ||
|
|
||
| SensorMod sensor_mod_; | ||
|
|
||
| vortex::filter::PDAF<DynMod, SensorMod>::Config pdaf_config_; | ||
| }; | ||
|
|
||
| } // namespace vortex::filtering | ||
|
|
||
| #endif // POSE_FILTERING__LIB__ORIENTATION_FILTER_HPP_ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.