|
| 1 | +// Copyright 2026 PickNik Inc. |
| 2 | +// All rights reserved. |
| 3 | +// |
| 4 | +// Unauthorized copying of this code base via any medium is strictly prohibited. |
| 5 | +// Proprietary and confidential. |
| 6 | + |
| 7 | +#pragma once |
| 8 | + |
| 9 | +#include <optional> |
| 10 | +#include <shared_mutex> |
| 11 | +#include <string> |
| 12 | + |
| 13 | +#include <behaviortree_cpp/action_node.h> |
| 14 | +#include <nav_msgs/msg/odometry.hpp> |
| 15 | +#include <rclcpp/subscription.hpp> |
| 16 | + |
| 17 | +#include <moveit_pro_behavior_interface/behavior_context.hpp> |
| 18 | +#include <moveit_pro_behavior_interface/shared_resources_node.hpp> |
| 19 | + |
| 20 | +namespace experimental_behaviors |
| 21 | +{ |
| 22 | +/** |
| 23 | + * @brief Long-lived odometry subscriber that publishes the latest received message every tick. |
| 24 | + * |
| 25 | + * @details Like the core `GetOdom` behavior in shape — `StatefulActionNode`, returns RUNNING |
| 26 | + * forever, halted by the parent on parallel completion — but does NOT write anything |
| 27 | + * to its output ports until at least one real message has arrived. This avoids a |
| 28 | + * cold-start race where downstream consumers (e.g. CalculatePoseOffset) see a |
| 29 | + * default-constructed Odometry with an empty `header.frame_id` on early ticks and |
| 30 | + * fail the tree. |
| 31 | + * |
| 32 | + * Intended to be used as a producer in a Parallel, with the consumer reading |
| 33 | + * `odometry_pose` from the blackboard. Pair with a seed (e.g. ConvertOdomToPoseStamped |
| 34 | + * on a fresh GetOdomInstance snapshot) so the consumer has a valid value to read on |
| 35 | + * its first tick before this behavior has received anything. |
| 36 | + * |
| 37 | + * | Data Port Name | Port Type | Object Type | |
| 38 | + * | -------------------- | --------- | ------------------------------- | |
| 39 | + * | odometry_topic_name | input | std::string | |
| 40 | + * | subscribed_odometry | output | nav_msgs::msg::Odometry | |
| 41 | + * | odometry_pose | output | geometry_msgs::msg::PoseStamped | |
| 42 | + */ |
| 43 | +class GetOdomLatest final : public moveit_pro::behaviors::SharedResourcesNode<BT::StatefulActionNode> |
| 44 | +{ |
| 45 | +public: |
| 46 | + GetOdomLatest(const std::string& name, const BT::NodeConfiguration& config, |
| 47 | + const std::shared_ptr<moveit_pro::behaviors::BehaviorContext>& shared_resources); |
| 48 | + |
| 49 | + static BT::PortsList providedPorts(); |
| 50 | + static BT::KeyValueVector metadata(); |
| 51 | + |
| 52 | + BT::NodeStatus onStart() override; |
| 53 | + BT::NodeStatus onRunning() override; |
| 54 | + void onHalted() override; |
| 55 | + |
| 56 | +private: |
| 57 | + std::shared_ptr<rclcpp::Subscription<nav_msgs::msg::Odometry>> odometry_subscriber_; |
| 58 | + std::shared_mutex odometry_mutex_; |
| 59 | + std::optional<nav_msgs::msg::Odometry> current_odometry_; |
| 60 | +}; |
| 61 | +} // namespace experimental_behaviors |
0 commit comments