-
Notifications
You must be signed in to change notification settings - Fork 24
Feat/waypoint manager #645
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
Changes from 24 commits
38f1fdb
f3fc24b
d5bbaca
4ff98d5
4c30faa
dae4fa3
2b39764
676c54f
ebb220c
490e307
900f2af
7065c6a
7209ce3
87ed751
1818953
8248353
3aaf6f9
8784b2e
4d73ecb
76183bc
b82db78
ba2fdff
b1089b8
692e5ec
21c1d19
56ce067
8527941
cdc97f7
d1557c8
99def81
a2f413a
5826046
4169c98
3afe906
63f8854
80e8ce5
c65d013
76f49ee
2d27647
c0e1185
3a69dbb
546f836
1887321
b26adc7
f1f181a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -195,12 +195,12 @@ Eigen::Vector18d ReferenceFilterNode::fill_reference_state() { | |
| } | ||
|
|
||
| Eigen::Vector6d ReferenceFilterNode::fill_reference_goal( | ||
| const geometry_msgs::msg::PoseStamped& goal) { | ||
| double x{goal.pose.position.x}; | ||
| double y{goal.pose.position.y}; | ||
| double z{goal.pose.position.z}; | ||
| const geometry_msgs::msg::Pose& goal) { | ||
| double x{goal.position.x}; | ||
| double y{goal.position.y}; | ||
| double z{goal.position.z}; | ||
|
|
||
| const auto& o = goal.pose.orientation; | ||
| const auto& o = goal.orientation; | ||
| Eigen::Quaterniond q(o.w, o.x, o.y, o.z); | ||
| Eigen::Vector3d euler_angles = vortex::utils::math::quat_to_euler(q); | ||
| double roll{euler_angles(0)}; | ||
|
|
@@ -231,6 +231,43 @@ vortex_msgs::msg::ReferenceFilter ReferenceFilterNode::fill_reference_msg() { | |
| return feedback_msg; | ||
| } | ||
|
|
||
| Eigen::Vector6d ReferenceFilterNode::apply_mode_logic( | ||
| const Eigen::Vector6d& r_in, | ||
| uint8_t mode) { | ||
| Eigen::Vector6d r_out = r_in; | ||
|
|
||
| switch (mode) { | ||
| case vortex_msgs::msg::Waypoint::FULL_POSE: | ||
| break; | ||
|
|
||
| case vortex_msgs::msg::Waypoint::ONLY_POSITION: | ||
| r_out(3) = x_(3); | ||
| r_out(4) = x_(4); | ||
| r_out(5) = x_(5); | ||
| break; | ||
|
Comment on lines
+243
to
+247
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't logic like
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do think its fine that the wp manager/guidance controls which mode and so on, but the controller(s) should probably also know what mode it is performing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The locking is desired for this use case.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that the mode logic could be handled in the controller, but this setup currently works as a MVP, and fits nicely within the scope of the action.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. locking the drone to a desired orientation could be something that the state machine requests and guidance system does i don't see the point of the (DP) controller differenciating between locking orientation and locking pose it is the same thing but simpler, albeit could be better tuned to some extent if we separate them. The use case in this situation is more that we do not really want to change the position which would require the state machine to remember its last waypoint, recover the (x,y,z) coordinates and then send the same waypoint but with changed orientation which seemes tedious. Same goes for position only vice versa. The thing that could be something to discuss is if we want the state machine to only communicate its desires with the orchistrator/manager node, otherwise we could make the state machine directly send requests to DP guidance about locking orientation.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possible to just let each dof be addressable (i.e. Eigen::Index, strongly typed) and let whatever thing thats managing task execution lock whatever axes as required? (could also alias "common" things like position only, orientation only, z only, etc, etc). That way you keep the controller "dumb" in the sense that it has no knowledge of "mode", just "which axes are currently constrained". (I don't know anything about the current dp code so correct me if this is already the case 🔥) |
||
|
|
||
| case vortex_msgs::msg::Waypoint::FORWARD_HEADING: { | ||
| double dx = r_in(0) - x_(0); | ||
| double dy = r_in(1) - x_(1); | ||
|
|
||
| double forward_yaw = std::atan2(dy, dx); | ||
|
||
|
|
||
| r_out(3) = 0.0; | ||
| r_out(4) = 0.0; | ||
| r_out(5) = vortex::utils::math::ssa(forward_yaw); | ||
| break; | ||
| } | ||
|
|
||
| case vortex_msgs::msg::Waypoint::ONLY_ORIENTATION: | ||
| r_out(0) = x_(0); | ||
| r_out(1) = x_(1); | ||
| r_out(2) = x_(2); | ||
| break; | ||
| } | ||
|
|
||
| return r_out; | ||
| } | ||
|
|
||
| void ReferenceFilterNode::execute( | ||
| const std::shared_ptr<rclcpp_action::ServerGoalHandle< | ||
| vortex_msgs::action::ReferenceFilterWaypoint>> goal_handle) { | ||
|
|
@@ -243,9 +280,21 @@ void ReferenceFilterNode::execute( | |
|
|
||
| x_ = fill_reference_state(); | ||
|
|
||
| const geometry_msgs::msg::PoseStamped goal = goal_handle->get_goal()->goal; | ||
| const geometry_msgs::msg::Pose goal = | ||
| goal_handle->get_goal()->waypoint.pose; | ||
| uint8_t mode = goal_handle->get_goal()->waypoint.mode; | ||
| double convergence_threshold = | ||
| goal_handle->get_goal()->convergence_threshold; | ||
|
Comment on lines
+283
to
+287
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would try to use |
||
|
|
||
| if (convergence_threshold <= 0.0) { | ||
| convergence_threshold = 0.1; | ||
| spdlog::warn( | ||
| "ReferenceFilter: Invalid convergence_threshold received (<= 0). " | ||
| "Using default 0.1"); | ||
| } | ||
|
|
||
| r_ = fill_reference_goal(goal); | ||
| Eigen::Vector6d r_temp = fill_reference_goal(goal); | ||
| r_ = apply_mode_logic(r_temp, mode); | ||
|
|
||
| auto feedback = std::make_shared< | ||
| vortex_msgs::action::ReferenceFilterWaypoint::Feedback>(); | ||
|
|
@@ -274,12 +323,12 @@ void ReferenceFilterNode::execute( | |
|
|
||
| vortex_msgs::msg::ReferenceFilter feedback_msg = fill_reference_msg(); | ||
|
|
||
| feedback->feedback = feedback_msg; | ||
| feedback->reference = feedback_msg; | ||
|
|
||
| goal_handle->publish_feedback(feedback); | ||
| reference_pub_->publish(feedback_msg); | ||
|
|
||
| if ((x_.head(6) - r_.head(6)).norm() < 0.1) { | ||
| if ((x_.head(6) - r_.head(6)).norm() < convergence_threshold) { | ||
| result->success = true; | ||
| goal_handle->succeed(result); | ||
| x_.head(6) = r_.head(6); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| cmake_minimum_required(VERSION 3.8) | ||
| project(waypoint_manager) | ||
|
|
||
| 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_action REQUIRED) | ||
| find_package(rclcpp_components REQUIRED) | ||
| find_package(vortex_msgs REQUIRED) | ||
| find_package(tf2_geometry_msgs REQUIRED) | ||
| find_package(spdlog REQUIRED) | ||
| find_package(fmt REQUIRED) | ||
|
|
||
| include_directories(include) | ||
|
|
||
| set(LIB_NAME "${PROJECT_NAME}_component") | ||
|
|
||
| add_library(${LIB_NAME} SHARED | ||
| src/waypoint_manager_ros.cpp) | ||
|
|
||
| ament_target_dependencies(${LIB_NAME} PUBLIC | ||
| rclcpp | ||
| rclcpp_components | ||
| rclcpp_action | ||
| vortex_msgs | ||
| tf2_geometry_msgs | ||
| spdlog | ||
| fmt | ||
| ) | ||
|
|
||
| rclcpp_components_register_node( | ||
| ${LIB_NAME} | ||
| PLUGIN "WaypointManagerNode" | ||
| EXECUTABLE ${PROJECT_NAME}_node | ||
| ) | ||
|
|
||
| ament_export_targets(export_${LIB_NAME}) | ||
|
|
||
| install(TARGETS ${LIB_NAME} | ||
| EXPORT export_${LIB_NAME} | ||
| ARCHIVE DESTINATION lib | ||
| LIBRARY DESTINATION lib | ||
| RUNTIME DESTINATION bin | ||
| ) | ||
|
|
||
| install( | ||
| DIRECTORY include/ | ||
| DESTINATION include | ||
| ) | ||
|
|
||
| install(DIRECTORY | ||
| launch | ||
| DESTINATION share/${PROJECT_NAME}/ | ||
| ) | ||
|
|
||
| ament_package() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
|
|
||
|
|
||
| # Waypoint Manager — ROS 2 Node | ||
|
|
||
|
|
||
| The **Waypoint Manager** node coordinates mission-level navigation by managing waypoint queues, forwarding them to a **Reference Filter** for trajectory generation, and exposing both an **action interface** (for mission planners) and a **service interface** (for perception-driven waypoint updates). | ||
|
|
||
|
|
||
|
|
||
| # Interfaces | ||
|
|
||
| * **[WaypointManager](https://github.com/vortexntnu/vortex-msgs/blob/main/action/WaypointManager.action) (action server)** | ||
| * **[WaypointAddition](https://github.com/vortexntnu/vortex-msgs/blob/main/srv/WaypointAddition.srv) (service server)** | ||
| * **[ReferenceFilterWaypoint](https://github.com/vortexntnu/vortex-msgs/blob/main/action/ReferenceFilterWaypoint.action) (action client)** | ||
|
|
||
|
|
||
| # Behavior Summary | ||
|
|
||
| ### **Mission Start** | ||
|
|
||
| * On receiving a new action goal, any existing mission is aborted. | ||
| * Waypoints are stored, state is reset, and the first waypoint is sent to the reference filter. | ||
|
|
||
| ### **During Mission** | ||
|
|
||
| * Each waypoint is executed sequentially. | ||
| * Pose feedback from the reference filter is forwarded to the mission planner. | ||
| * `convergence_threshold` determines when a waypoint is reached. | ||
| * If `persistent = true`, the action does not end even when waypoints run out. | ||
|
|
||
| ### **Mission End** | ||
|
|
||
| * If `persistent = false`, the action completes when the last waypoint is reached. | ||
| * If cancelled externally, all state is cleared and reference filter goals are cancelled. | ||
|
|
||
| ### **Dynamic Waypoint Addition** | ||
|
|
||
| Allowed only during **persistent** missions: | ||
|
|
||
| * `overwrite = true` → clears old queue and restarts from new waypoints | ||
| * `overwrite = false` → appends waypoints to queue | ||
| * `priority = true` → turn on priority mode. In this mode additional service requests with `priority = false` are ignored. Priority mode is set to false again when there are no more waypoints in queue. | ||
|
|
||
| --- | ||
|
|
||
| ### Check available interfaces: | ||
|
|
||
| ```bash | ||
| ros2 action list | ||
| ros2 service list | ||
| ``` | ||
| beware of namespace! | ||
|
|
||
|
|
||
| ## Example Action Goal (CLI) | ||
|
|
||
| ```bash | ||
| ros2 action send_goal /orca/waypoint_manager vortex_msgs/action/WaypointManager \ | ||
| '{ | ||
| waypoints:[ | ||
| { | ||
| pose:{position:{x:5.0,y:0.0,z:0.0}, | ||
| orientation:{x:0,y:0,z:0,w:1}}, | ||
| mode:1 | ||
| } | ||
| ], | ||
| convergence_threshold:0.1, | ||
| persistent:false | ||
| }' | ||
| ``` | ||
|
|
||
|
|
||
| ## Example Waypoint Addition Service Call | ||
|
|
||
| ```bash | ||
| ros2 service call /orca/waypoint_addition vortex_msgs/srv/WaypointAddition \ | ||
| '{ | ||
| waypoints:[ | ||
| {pose:{position:{x:2,y:3,z:0},orientation:{x:0,y:0,z:0,w:1}},mode:1} | ||
| ], | ||
| overwrite:false, | ||
| priority:true | ||
| }' | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| #ifndef WAYPOINT_MANAGER__WAYPOINT_MANAGER_ROS_HPP_ | ||
| #define WAYPOINT_MANAGER__WAYPOINT_MANAGER_ROS_HPP_ | ||
|
|
||
| #include <memory> | ||
| #include <rclcpp/rclcpp.hpp> | ||
| #include <rclcpp_action/rclcpp_action.hpp> | ||
| #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp> | ||
|
|
||
| #include <vector> | ||
| #include <vortex_msgs/action/reference_filter_waypoint.hpp> | ||
| #include <vortex_msgs/action/waypoint_manager.hpp> | ||
| #include <vortex_msgs/msg/waypoint.hpp> | ||
| #include <vortex_msgs/srv/waypoint_addition.hpp> | ||
|
|
||
| namespace vortex::mission { | ||
|
|
||
| using WaypointManager = vortex_msgs::action::WaypointManager; | ||
| using WaypointManagerGoalHandle = | ||
| rclcpp_action::ServerGoalHandle<WaypointManager>; | ||
|
|
||
| using ReferenceFilterAction = vortex_msgs::action::ReferenceFilterWaypoint; | ||
| using ReferenceFilterGoalHandle = | ||
| rclcpp_action::ClientGoalHandle<ReferenceFilterAction>; | ||
|
|
||
| class WaypointManagerNode : public rclcpp::Node { | ||
| public: | ||
| explicit WaypointManagerNode( | ||
| const rclcpp::NodeOptions& options = rclcpp::NodeOptions()); | ||
|
|
||
| private: | ||
| // @brief Create the action server for WaypointManager. | ||
| void set_waypoint_action_server(); | ||
|
|
||
| // @brief Create the action client for ReferenceFilterWaypoint. | ||
| void set_reference_action_client(); | ||
|
|
||
| // @brief Create the service servers for WaypointAddition. | ||
| void set_waypoint_service_server(); | ||
|
|
||
| // @brief Convert ReferenceFilter feedback to a Pose message | ||
| // @param fb The ReferenceFilter feedback message | ||
| // @return The corresponding Pose message | ||
| geometry_msgs::msg::Pose reference_to_pose( | ||
| const ReferenceFilterAction::Feedback& fb) const; | ||
|
|
||
| // @brief Construct the result message for the WaypointManager action | ||
| // @param success Whether the action was successful | ||
| // @return The constructed result message | ||
| std::shared_ptr<vortex_msgs::action::WaypointManager_Result> | ||
| construct_result(bool success) const; | ||
|
|
||
| // @brief Clean up the mission state after completion or cancellation of a | ||
| // waypoint action. Cancel active goals and reset internal variables. Make | ||
| // system ready for next action. | ||
| void cleanup_mission_state(); | ||
|
|
||
| // @brief Send the next goal to the ReferenceFilter action server based on | ||
| // the current waypoint index or finish the waypoint action if all waypoints | ||
| // have been processed. | ||
| void send_next_reference_filter_goal(); | ||
|
|
||
| // @brief Handle incoming action goal requests | ||
| // @param uuid The goal UUID | ||
| // @param goal The goal message | ||
| // @return The goal response | ||
| rclcpp_action::GoalResponse handle_waypoint_goal( | ||
| const rclcpp_action::GoalUUID& uuid, | ||
| std::shared_ptr<const vortex_msgs::action::WaypointManager::Goal> | ||
| goal_msg); | ||
|
|
||
| // @brief Handle requests to cancel the waypoint action | ||
| // @param goal_handle The goal handle | ||
| // @return The cancel response | ||
| rclcpp_action::CancelResponse handle_waypoint_cancel( | ||
| const std::shared_ptr<rclcpp_action::ServerGoalHandle< | ||
| vortex_msgs::action::WaypointManager>> goal_handle); | ||
|
|
||
| // @brief Handle the accepted goal request | ||
| // @param goal_handle The goal handle | ||
| void handle_waypoint_accepted( | ||
| const std::shared_ptr<rclcpp_action::ServerGoalHandle< | ||
| vortex_msgs::action::WaypointManager>> goal_handle); | ||
|
|
||
| // @brief Handle incoming waypoint addition service requests | ||
| // Only accepted if waypoint action is running. | ||
| // @param request Incoming service request containing waypoint information. | ||
| // @param response Service response that should be populated and sent back | ||
| // to the caller. | ||
| void handle_waypoint_addition_service_request( | ||
| const std::shared_ptr<vortex_msgs::srv::WaypointAddition::Request> | ||
| request, | ||
| std::shared_ptr<vortex_msgs::srv::WaypointAddition::Response> response); | ||
|
|
||
| // @brief Send a goal to the reference filter | ||
| // @param goal_msg The action goal | ||
| void send_reference_filter_goal( | ||
| const vortex_msgs::action::ReferenceFilterWaypoint::Goal& goal_msg); | ||
|
|
||
| rclcpp_action::Client<vortex_msgs::action::ReferenceFilterWaypoint>:: | ||
| SharedPtr reference_filter_client_; | ||
| rclcpp_action::Server<vortex_msgs::action::WaypointManager>::SharedPtr | ||
| waypoint_action_server_; | ||
| rclcpp::Service<vortex_msgs::srv::WaypointAddition>::SharedPtr | ||
| waypoint_service_server_; | ||
|
|
||
| std::vector<vortex_msgs::msg::Waypoint> waypoints_{}; | ||
| std::size_t current_index_{0}; | ||
| double convergence_threshold_{0.1}; | ||
|
|
||
| bool persistent_action_mode_{false}; | ||
| bool priority_mode_{false}; | ||
|
|
||
| ReferenceFilterAction::Feedback latest_ref_feedback_; | ||
| bool have_reference_pose_{false}; | ||
| bool cancel_in_progress_{false}; | ||
|
||
|
|
||
| std::uint64_t mission_id_ = 0; | ||
|
Comment on lines
+111
to
+112
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cant wait to see mission number 18,446,744,073,709,551,615 😂 |
||
|
|
||
| std::shared_ptr<ReferenceFilterGoalHandle> active_reference_filter_goal_; | ||
| std::shared_ptr<WaypointManagerGoalHandle> active_action_goal_; | ||
| }; | ||
|
|
||
| } // namespace vortex::mission | ||
|
|
||
| #endif // WAYPOINT_MANAGER__WAYPOINT_MANAGER_ROS_HPP_ | ||
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.
Why is stamped removed?
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.
The function did not use the header, so no need to pass more than what is required
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.
vortex utils ros conversion here instead?