NVIDIA Holoscan SDK v3.4.0

Program Listing for File pose_tree.hpp

Return to documentation for file (include/holoscan/pose_tree/pose_tree.hpp)

Copy
Copied!
            

/* * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HOLOSCAN_POSE_TREE_POSE_TREE_HPP #define HOLOSCAN_POSE_TREE_POSE_TREE_HPP #include <map> #include <memory> #include <mutex> #include <shared_mutex> // NOLINT(build/include_order) #include <unordered_map> #include <utility> #include <vector> #include "common/fixed_vector.hpp" #include "common/unique_index_map.hpp" #include "holoscan/core/expected.hpp" #include "holoscan/core/resources/gxf/first_fit_allocator.hpp" #include "holoscan/logger/logger.hpp" #include "holoscan/pose_tree/hash_map.hpp" #include "holoscan/pose_tree/math/pose2.hpp" #include "holoscan/pose_tree/math/pose3.hpp" #include "holoscan/pose_tree/pose_tree_history.hpp" namespace std { template <class A, class B> struct hash<pair<A, B>> { size_t operator()(const pair<A, B>& p) const { return std::hash<A>{}(p.first) ^ (std::hash<B>{}(p.second) << 1); } }; } // namespace std namespace holoscan { class PoseTree { public: enum class Error { kInvalidArgument = 0, kOutOfMemory = 1, kFrameNotFound = 2, kAlreadyExists = 3, kCyclingDependency = 4, kFramesNotLinked = 5, kPoseOutOfOrder = 6, kLogicError = 7, }; static constexpr int32_t kFrameNameMaximumLength = 127; static constexpr char const* kAutoGeneratedFrameNamePrefix = "_frame_"; template <typename T> using expected_t = nvidia::Expected<T, Error>; using unexpected_t = nvidia::Unexpected<Error>; using frame_t = uint64_t; using version_t = uint64_t; using history_t = uint64_t; using uid_t = uint64_t; using CreateFrameCallback = std::function<void(frame_t frame)>; using SetEdgeCallback = std::function<void(frame_t lhs, frame_t rhs, double time, const Pose3d& lhs_T_rhs)>; expected_t<void> init(int32_t number_frames = 1024, int32_t number_edges = 16384, int32_t history_length = 1048576, int32_t default_number_edges = 16, int32_t default_history_length = 1024, int32_t edges_chunk_size = 4, int32_t history_chunk_size = 64); void deinit(); version_t get_pose_tree_version() const; expected_t<frame_t> create_frame(std::string_view name, int32_t number_edges); expected_t<frame_t> create_frame(std::string_view name); expected_t<frame_t> create_frame(int32_t number_edges); expected_t<frame_t> create_frame(); expected_t<frame_t> find_frame(std::string_view name) const; expected_t<frame_t> find_or_create_frame(std::string_view name); expected_t<frame_t> find_or_create_frame(std::string_view name, int32_t number_edges); expected_t<version_t> create_edges(frame_t lhs, frame_t rhs); expected_t<version_t> create_edges(frame_t lhs, frame_t rhs, int32_t maximum_length); expected_t<version_t> create_edges(frame_t lhs, frame_t rhs, PoseTreeEdgeHistory::AccessMethod method); expected_t<version_t> create_edges(frame_t lhs, frame_t rhs, int32_t maximum_length, PoseTreeEdgeHistory::AccessMethod method); expected_t<version_t> create_edges(std::string_view lhs, std::string_view rhs); expected_t<version_t> create_edges(std::string_view lhs, std::string_view rhs, int32_t maximum_length); expected_t<version_t> create_edges(std::string_view lhs, std::string_view rhs, PoseTreeEdgeHistory::AccessMethod method); expected_t<version_t> create_edges(std::string_view lhs, std::string_view rhs, int32_t maximum_length, PoseTreeEdgeHistory::AccessMethod method); expected_t<version_t> delete_frame(frame_t uid); expected_t<version_t> delete_frame(std::string_view name); expected_t<version_t> delete_edge(frame_t lhs, frame_t rhs); expected_t<version_t> delete_edge(std::string_view lhs, std::string_view rhs); expected_t<version_t> disconnect_frame(frame_t uid, double time); expected_t<version_t> disconnect_frame(std::string_view name, double time); expected_t<version_t> disconnect_edge(frame_t lhs, frame_t rhs, double time); expected_t<version_t> disconnect_edge(std::string_view lhs, std::string_view rhs, double time); // Disable all the implicit cast (to make sure to catch a call with the wrong type for the time) template <class... Args> expected_t<Pose3d> disconnect_frame(Args&&... args) = delete; template <class... Args> expected_t<Pose3d> disconnect_edge(Args&&... args) = delete; expected_t<std::string_view> get_frame_name(frame_t uid) const; expected_t<std::pair<Pose3d, double>> get_latest(frame_t lhs, frame_t rhs) const; expected_t<std::pair<Pose3d, double>> get_latest(std::string_view lhs, std::string_view rhs) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs, double time, PoseTreeEdgeHistory::AccessMethod method, version_t version) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs, double time, version_t version) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs, double time, PoseTreeEdgeHistory::AccessMethod method) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs, double time) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs, version_t version) const; expected_t<Pose3d> get(frame_t lhs, frame_t rhs) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, double time, PoseTreeEdgeHistory::AccessMethod method, version_t version) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, double time, version_t version) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, version_t version) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, double time, PoseTreeEdgeHistory::AccessMethod method) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, double time) const; expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs) const; // Disable all the implicit cast (to make sure to catch a call with the wrong type for the time) template <class... Args> expected_t<Pose3d> get(frame_t lhs, frame_t rhs, Args&&... args) const = delete; template <class... Args> expected_t<Pose3d> get(std::string_view lhs, std::string_view rhs, Args&&... args) const = delete; template <class... Args> expected_t<Pose2d> get_pose2_xy(Args&&... args) const { return get(std::forward<Args>(args)...).map([](const Pose3d& pose_3d) { return pose_3d.to_pose2_xy(); }); } expected_t<version_t> set(frame_t lhs, frame_t rhs, double time, const Pose3d& lhs_T_rhs); expected_t<version_t> set(std::string_view lhs, std::string_view rhs, double time, const Pose3d& lhs_T_rhs); expected_t<version_t> set(frame_t lhs, frame_t rhs, double time, const Pose2d& lhs_T_rhs) { return set(lhs, rhs, time, Pose3d::from_pose2_xy(lhs_T_rhs)); } expected_t<version_t> set(std::string_view lhs, std::string_view rhs, double time, const Pose2d& lhs_T_rhs) { return set(lhs, rhs, time, Pose3d::from_pose2_xy(lhs_T_rhs)); } // Then we disable all the calls not made with double. template <typename T, typename Pose> expected_t<version_t> set(frame_t lhs, frame_t rhs, T time, const Pose& lhs_T_rhs) = delete; template <typename T, typename Pose> expected_t<version_t> set(std::string_view lhs, std::string_view rhs, T time, const Pose& lhs_T_rhs) = delete; template <typename T> expected_t<void> get_edge_uids(T& container) const { std::shared_lock<std::shared_timed_mutex> lock(mutex_); container.clear(); if (container.capacity() < edges_map_.size()) { return unexpected_t(Error::kOutOfMemory); } for (const auto& key : edges_map_keys_) { if (!key) { return unexpected_t(Error::kLogicError); } const auto value = edges_map_.get(key.value()); if (!value) { return unexpected_t(Error::kLogicError); } const auto history = histories_map_.try_get(value.value()); if (!history) { return unexpected_t(Error::kLogicError); } if (!history.value()->connected()) { continue; } container.push_back(key.value()); } return expected_t<void>{}; } template <typename T> expected_t<void> get_edge_names(T& container) const { std::shared_lock<std::shared_timed_mutex> lock(mutex_); container.clear(); if (container.capacity() < edges_map_.size()) { return unexpected_t(Error::kOutOfMemory); } for (const auto& key : edges_map_keys_) { if (!key) { return unexpected_t(Error::kLogicError); } const auto value = edges_map_.get(key.value()); if (!value) { return unexpected_t(Error::kLogicError); } const auto history = histories_map_.try_get(value.value()); if (!history) { return unexpected_t(Error::kLogicError); } if (!history.value()->connected()) { continue; } auto lhs_name = get_frame_name(key.value().first); auto rhs_name = get_frame_name(key.value().second); if (!lhs_name || !rhs_name) { continue; } container.push_back({lhs_name.value(), rhs_name.value()}); } return expected_t<void>{}; } template <typename T> expected_t<void> get_frame_uids(T& container) const { std::shared_lock<std::shared_timed_mutex> lock(mutex_); container.clear(); if (container.capacity() < name_to_uid_map_keys_.size()) { return unexpected_t(Error::kOutOfMemory); } for (const auto& key : name_to_uid_map_keys_) { if (!key) { return unexpected_t(Error::kLogicError); } const auto value = name_to_uid_map_.get(key.value()); if (!value) { return unexpected_t(Error::kLogicError); } container.push_back(value.value()); } return expected_t<void>{}; } template <typename T> expected_t<void> get_frame_names(T& container) const { std::shared_lock<std::shared_timed_mutex> lock(mutex_); container.clear(); if (container.capacity() < name_to_uid_map_keys_.size()) { return unexpected_t(Error::kOutOfMemory); } for (const auto& key : name_to_uid_map_keys_) { if (!key) { return unexpected_t(Error::kLogicError); } container.push_back(key.value()); } return expected_t<void>{}; } expected_t<uid_t> add_create_frame_callback(CreateFrameCallback callback); expected_t<void> remove_create_frame_callback(uid_t cid); expected_t<uid_t> add_set_edge_callback(SetEdgeCallback callback); expected_t<void> remove_set_edge_callback(uid_t cid); static const char* error_to_str(Error error); private: struct FrameInfo { history_t* history; int32_t number_edges; int32_t maximum_number_edges; char name[kFrameNameMaximumLength + 1]; std::string_view name_view; int32_t distance_to_root; frame_t node_to_root; frame_t root; mutable version_t hint_version; mutable frame_t dfs_link; frame_t uid; }; // Implementation of find_or_create_frame expected_t<frame_t> find_or_create_frame_impl(std::string_view name, int32_t number_edges); // Implementation of find_frame expected_t<frame_t> find_frame_impl(std::string_view name) const; // Implementation of create_frame expected_t<frame_t> create_frame_impl(std::string_view name, int32_t number_edges); // Implementation of create_edges expected_t<version_t> create_edges_impl(frame_t lhs, frame_t rhs, int32_t maximum_length, PoseTreeEdgeHistory::AccessMethod method); // Implementation of delete_edge expected_t<version_t> delete_edge_impl(frame_t lhs, frame_t rhs, version_t version); // Update the path to the root for a given connected component starting from the given node. expected_t<void> update_root(frame_t root); // Implementation of get using the pre-computed path to the root as a hint. If it fails, it falls // back to get_dfs_impl. expected_t<Pose3d> get_impl(frame_t lhs, frame_t rhs, double time, PoseTreeEdgeHistory::AccessMethod method, version_t version) const; // Implementation of get that do a dfs to see if a path exists at a given time. expected_t<Pose3d> get_dfs_impl(frame_t lhs, frame_t rhs, double time, PoseTreeEdgeHistory::AccessMethod method, version_t version) const; mutable std::shared_timed_mutex mutex_; mutable std::mutex dfs_mutex_; mutable std::shared_timed_mutex create_frame_callbacks_mutex_; nvidia::UniqueIndexMap<CreateFrameCallback> create_frame_callbacks_; nvidia::FixedVector<uid_t> create_frame_callbacks_keys_; mutable std::shared_timed_mutex set_edge_callbacks_mutex_; nvidia::UniqueIndexMap<SetEdgeCallback> set_edge_callbacks_; nvidia::FixedVector<uid_t> set_edge_callbacks_keys_; // TODO(ben): We need to get rid of std::map pose_tree::HashMap<std::pair<frame_t, frame_t>, history_t> edges_map_; nvidia::FixedVector<std::pair<frame_t, frame_t>> edges_map_keys_; pose_tree::HashMap<std::string_view, frame_t> name_to_uid_map_; nvidia::FixedVector<std::string_view> name_to_uid_map_keys_; nvidia::UniqueIndexMap<FrameInfo> frame_map_; std::unique_ptr<frame_t[]> frames_stack_; // TODO(ben): We need to get rid of std::map nvidia::UniqueIndexMap<PoseTreeEdgeHistory> histories_map_; FirstFitAllocator<history_t> histories_management_; FirstFitAllocator<PoseTreeEdgeHistory::TimedPose> poses_management_; version_t version_{}; mutable version_t hint_version_{}; int32_t default_number_edges_{}; int32_t default_history_length_{}; uid_t frame_cb_latest_uid_{}; uid_t edge_cb_latest_uid_{}; }; } // namespace holoscan #endif/* HOLOSCAN_POSE_TREE_POSE_TREE_HPP */

© Copyright 2022-2025, NVIDIA. Last updated on Jul 1, 2025.