Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
38f1fdb
waypoint mode handling
jorgenfj Nov 24, 2025
f3fc24b
update tests with new message
jorgenfj Nov 24, 2025
d5bbaca
initial package setup
jorgenfj Nov 25, 2025
4ff98d5
ros interface function declaration
jorgenfj Nov 25, 2025
4c30faa
node setup
jorgenfj Nov 26, 2025
dae4fa3
working prototype
jorgenfj Nov 27, 2025
2b39764
reentrant cb, multithreaded
jorgenfj Nov 27, 2025
676c54f
single threaded impl
jorgenfj Nov 27, 2025
ebb220c
conv threshold action goal
jorgenfj Nov 29, 2025
490e307
default thresholdref conv value
jorgenfj Nov 29, 2025
900f2af
removed switching logic
jorgenfj Nov 29, 2025
7065c6a
removed timer execution
jorgenfj Nov 29, 2025
7209ce3
sim test utils
jorgenfj Nov 29, 2025
87ed751
waypoint_manager_test setup
jorgenfj Nov 29, 2025
1818953
no rendering test arg
jorgenfj Nov 29, 2025
8248353
waypoint tests, timeout error
jorgenfj Nov 29, 2025
3aaf6f9
test refactor
jorgenfj Nov 30, 2025
8784b2e
format
jorgenfj Dec 6, 2025
4d73ecb
rename utils package
jorgenfj Dec 6, 2025
76183bc
test suite and description
jorgenfj Dec 6, 2025
b82db78
first waypoint test
jorgenfj Dec 6, 2025
ba2fdff
removed unused function
jorgenfj Dec 6, 2025
b1089b8
renamed service field to priority. Added simple tests
jorgenfj Dec 7, 2025
692e5ec
waypoint manager readme
jorgenfj Dec 7, 2025
21c1d19
uniform attitude naming convention
jorgenfj Dec 9, 2025
56ce067
fix pr requests
jorgenfj Dec 9, 2025
8527941
update tests with new service fields
jorgenfj Dec 9, 2025
cdc97f7
four corner test
jorgenfj Dec 9, 2025
d1557c8
update util func name
jorgenfj Dec 10, 2025
99def81
update with new action def
jorgenfj Dec 10, 2025
a2f413a
removed failing build type
jorgenfj Dec 10, 2025
5826046
test dependencies
jorgenfj Dec 10, 2025
4169c98
ignore failing yasmin package
jorgenfj Dec 11, 2025
3afe906
remove __init__ files
jorgenfj Dec 11, 2025
63f8854
quat_to_euler in make_pose helper
jorgenfj Dec 15, 2025
80e8ce5
added __init__ file
jorgenfj Dec 17, 2025
c65d013
removed sim deps for test packages
jorgenfj Dec 17, 2025
76f49ee
added action shutdown handling
jorgenfj Dec 18, 2025
2d27647
removed waypoint manager set setup
jorgenfj Dec 18, 2025
c0e1185
added waypoint manager node tests
jorgenfj Dec 18, 2025
3a69dbb
waypoint manager 4 corner sim test
jorgenfj Dec 18, 2025
546f836
added missing launch testing test dependency
jorgenfj Dec 18, 2025
1887321
add sleep for topic discovery
jorgenfj Dec 18, 2025
b26adc7
fix action member field name
jorgenfj Dec 18, 2025
f1f181a
removed unnecessary if here
jorgenfj Dec 19, 2025
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
3 changes: 2 additions & 1 deletion .github/workflows/simulator-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ jobs:
setup_script: "tests/setup.sh"
test_scripts: '[
"tests/simulator_tests/waypoint_navigation/simulator_test.sh",
"tests/simulator_tests/los_test/simulator_test.sh"
"tests/simulator_tests/los_test/simulator_test.sh",
"tests/simulator_tests/waypoint_manager_test/simulator_test.sh"
]'
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,4 @@ qtcreator-*
# Emacs
.#*

# Catkin custom files
COLCON_IGNORE

# End of https://www.gitignore.io/api/ros
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ class ReferenceFilterNode : public rclcpp::Node {

Eigen::Vector18d fill_reference_state();

Eigen::Vector6d fill_reference_goal(
const geometry_msgs::msg::PoseStamped& goal);
Eigen::Vector6d fill_reference_goal(const geometry_msgs::msg::Pose& goal);

Comment on lines +77 to +78
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is stamped removed?

Copy link
Contributor Author

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

Copy link
Contributor

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?

Eigen::Vector6d apply_mode_logic(const Eigen::Vector6d& r_in, uint8_t mode);

vortex_msgs::msg::ReferenceFilter fill_reference_msg();

Expand Down
78 changes: 69 additions & 9 deletions guidance/reference_filter_dp/src/reference_filter_ros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)};
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't logic like ONLY_POSITION and ONLY_ORIENTATION handled in the motion controller(s) instead? Also with this you risk locking the desired orientation in awkward ways, instead of just not controlling said DOFs. If the locking behavior (fixing the desired value of one or more DOF) is something that is desired, that could easily be handled in the controller(s) in the same logic segment

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The locking is desired for this use case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
I feel the guidance and controller system could use a refactor with a "main orchestrator" or something, and this mode logic can be revamped then. The waypoint manager could potentially be incorporated into this "main orchestrator" and be extended to support LOS/velocity controller as well.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Member

@chrstrom chrstrom Dec 18, 2025

Choose a reason for hiding this comment

The 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_heading = std::atan2(dy, dx);

r_out(3) = 0.0;
r_out(4) = 0.0;
r_out(5) = vortex::utils::math::ssa(forward_heading);
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) {
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

I would try to use auto here, and elsewhere where it makes sense. It avoids visual clutter and potential accidental implicit conversions. However, do not use auto with Eigen types (eigen common pitfalls)


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>();
Expand Down Expand Up @@ -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);
Expand All @@ -292,6 +341,17 @@ void ReferenceFilterNode::execute(

loop_rate.sleep();
}
if (!rclcpp::ok() && goal_handle->is_active()) {
auto result = std::make_shared<
vortex_msgs::action::ReferenceFilterWaypoint::Result>();
result->success = false;

try {
goal_handle->abort(result);
} catch (...) {
// Ignore exceptions during shutdown
}
}
}

RCLCPP_COMPONENTS_REGISTER_NODE(ReferenceFilterNode)
Expand Down
2 changes: 2 additions & 0 deletions mission/FSM/docking/COLCON_IGNORE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignored temporarily to skip building while debugging yasmin upgrade
# Remove this file to include the package in colcon builds again
72 changes: 40 additions & 32 deletions mission/FSM/docking/src/docking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,18 @@ ApproachDockingStationState::create_goal_handler(

blackboard->set<bool>("is_home", false);

docking_fsm::PoseStamped docking_offset_goal =
blackboard->get<docking_fsm::PoseStamped>("docking_offset_goal");
docking_fsm::Pose docking_offset_goal =
blackboard->get<docking_fsm::PoseStamped>("docking_offset_goal").pose;

vortex_msgs::msg::Waypoint waypoint;
waypoint.pose = docking_offset_goal;

goal.goal = docking_offset_goal;
goal.waypoint = waypoint;

spdlog::info("Goal sent to action server:");
spdlog::info(" Position: x = {}, y = {}, z = {}",
docking_offset_goal.pose.position.x,
docking_offset_goal.pose.position.y,
docking_offset_goal.pose.position.z);
docking_offset_goal.position.x, docking_offset_goal.position.y,
docking_offset_goal.position.z);

return goal;
}
Expand All @@ -113,15 +115,15 @@ void ApproachDockingStationState::print_feedback(
std::shared_ptr<const docking_fsm::ApproachDockingAction::Feedback>
feedback) {
docking_fsm::Pose current_pose = docking_fsm::Pose();
current_pose.position.x = feedback->feedback.x;
current_pose.position.y = feedback->feedback.y;
current_pose.position.z = feedback->feedback.z;
current_pose.position.x = feedback->reference.x;
current_pose.position.y = feedback->reference.y;
current_pose.position.z = feedback->reference.z;

blackboard->set<docking_fsm::Pose>("current_pose", current_pose);

spdlog::debug("Current position: x = {}, y = {}, z = {}",
feedback->feedback.x, feedback->feedback.y,
feedback->feedback.z);
feedback->reference.x, feedback->reference.y,
feedback->reference.z);
}

GoAboveDockingStationState::GoAboveDockingStationState(
Expand All @@ -145,7 +147,9 @@ GoAboveDockingStationState::create_goal_handler(

auto docking_offset_goal =
blackboard->get<docking_fsm::PoseStamped>("docking_offset_goal");
goal.goal = docking_offset_goal;
vortex_msgs::msg::Waypoint waypoint;
waypoint.pose = docking_offset_goal.pose;
goal.waypoint = waypoint;

spdlog::info("Goal sent to action server:");
spdlog::info(" Position: x = {}, y = {}, z = {}",
Expand Down Expand Up @@ -179,15 +183,15 @@ void GoAboveDockingStationState::print_feedback(
std::shared_ptr<const docking_fsm::GoAboveDockingAction::Feedback>
feedback) {
docking_fsm::Pose current_pose = docking_fsm::Pose();
current_pose.position.x = feedback->feedback.x;
current_pose.position.y = feedback->feedback.y;
current_pose.position.z = feedback->feedback.z;
current_pose.position.x = feedback->reference.x;
current_pose.position.y = feedback->reference.y;
current_pose.position.z = feedback->reference.z;

blackboard->set<docking_fsm::Pose>("current_pose", current_pose);

spdlog::debug("Current position: x = {}, y = {}, z = {}",
feedback->feedback.x, feedback->feedback.y,
feedback->feedback.z);
feedback->reference.x, feedback->reference.y,
feedback->reference.z);
}

ConvergeDockingStationState::ConvergeDockingStationState(
Expand All @@ -213,7 +217,9 @@ ConvergeDockingStationState::create_goal_handler(

docking_fsm::PoseStamped dock_pose =
blackboard->get<docking_fsm::PoseStamped>("dock_pose");
goal.goal = dock_pose;
vortex_msgs::msg::Waypoint waypoint;
waypoint.pose = dock_pose.pose;
goal.waypoint = waypoint;

spdlog::info("Goal sent to action server:");
spdlog::info(" Position: x = {}, y = {}, z = {}",
Expand Down Expand Up @@ -242,14 +248,14 @@ void ConvergeDockingStationState::print_feedback(
std::shared_ptr<const docking_fsm::ConvergeDockingAction::Feedback>
feedback) {
docking_fsm::Pose current_pose = docking_fsm::Pose();
current_pose.position.x = feedback->feedback.x;
current_pose.position.y = feedback->feedback.y;
current_pose.position.z = feedback->feedback.z;
current_pose.position.x = feedback->reference.x;
current_pose.position.y = feedback->reference.y;
current_pose.position.z = feedback->reference.z;

blackboard->set<docking_fsm::Pose>("current_pose", current_pose);
spdlog::debug("Current position: x = {}, y = {}, z = {}",
feedback->feedback.x, feedback->feedback.y,
feedback->feedback.z);
feedback->reference.x, feedback->reference.y,
feedback->reference.z);
}

std::string DockedState(
Expand Down Expand Up @@ -286,7 +292,9 @@ docking_fsm::ReturnHomeAction::Goal ReturnHomeState::create_goal_handler(
docking_fsm::PoseStamped start_pose =
blackboard->get<docking_fsm::PoseStamped>("start_pose");

goal.goal = start_pose;
vortex_msgs::msg::Waypoint waypoint;
waypoint.pose = start_pose.pose;
goal.waypoint = waypoint;
spdlog::info("Goal sent to action server:");
spdlog::info(" Position: x = {}, y = {}, z = {}",
start_pose.pose.position.x, start_pose.pose.position.y,
Expand All @@ -313,17 +321,17 @@ void ReturnHomeState::print_feedback(
std::shared_ptr<yasmin::blackboard::Blackboard> blackboard,
std::shared_ptr<const docking_fsm::ReturnHomeAction::Feedback> feedback) {
docking_fsm::Pose current_pose = docking_fsm::Pose();
current_pose.position.x = feedback->feedback.x;
current_pose.position.y = feedback->feedback.y;
current_pose.position.z = feedback->feedback.z;
current_pose.orientation.x = feedback->feedback.roll;
current_pose.orientation.y = feedback->feedback.pitch;
current_pose.orientation.z = feedback->feedback.yaw;
current_pose.position.x = feedback->reference.x;
current_pose.position.y = feedback->reference.y;
current_pose.position.z = feedback->reference.z;
current_pose.orientation.x = feedback->reference.roll;
current_pose.orientation.y = feedback->reference.pitch;
current_pose.orientation.z = feedback->reference.yaw;

blackboard->set<docking_fsm::Pose>("current_pose", current_pose);
spdlog::debug("Current position: x = {}, y = {}, z = {}",
feedback->feedback.x, feedback->feedback.y,
feedback->feedback.z);
feedback->reference.x, feedback->reference.y,
feedback->reference.z);
}

std::string AbortState(
Expand Down
69 changes: 69 additions & 0 deletions mission/waypoint_manager/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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(vortex_utils 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
vortex_utils
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}/
)

if(BUILD_TESTING)
add_subdirectory(test)
endif()

ament_package()
Loading