AutoAPMS
Streamlining behaviors in ROS 2
Loading...
Searching...
No Matches
direct_actuators_control.cpp
1// Copyright 2026 Robin Müller
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <limits>
16
17#include "auto_apms_px4/mode.hpp"
18#include "auto_apms_px4/mode_executor.hpp"
19#include "auto_apms_px4_interfaces/action/set_actuators.hpp"
20#include "px4_ros2/control/setpoint_types/direct_actuators.hpp"
21
22namespace auto_apms_px4
23{
24
25using SetActuatorsActionType = auto_apms_px4_interfaces::action::SetActuators;
26
27class SetActuatorsMode : public ModeBase<SetActuatorsActionType>
28{
29 std::shared_ptr<px4_ros2::DirectActuatorsSetpointType> actuator_setpoint_ptr_;
30 rclcpp::Time activation_time_;
31
32public:
33 SetActuatorsMode(
34 rclcpp::Node & node, px4_ros2::ModeBase::Settings settings, std::shared_ptr<ActionContextType> action_context_ptr)
35 : ModeBase{node, settings, action_context_ptr}
36 {
37 actuator_setpoint_ptr_ = std::make_shared<px4_ros2::DirectActuatorsSetpointType>(*this);
38 }
39
40private:
41 void onActivateWithGoal(std::shared_ptr<const Goal> /*goal_ptr*/) final
42 {
43 activation_time_ = node().get_clock()->now();
44 }
45
46 void stopActuators()
47 {
48 constexpr auto kNaN = std::numeric_limits<float>::quiet_NaN();
49 constexpr int kMaxMotors = px4_ros2::DirectActuatorsSetpointType::kMaxNumMotors;
50 constexpr int kMaxServos = px4_ros2::DirectActuatorsSetpointType::kMaxNumServos;
51 actuator_setpoint_ptr_->updateMotors(Eigen::Matrix<float, kMaxMotors, 1>::Constant(kNaN));
52 actuator_setpoint_ptr_->updateServos(Eigen::Matrix<float, kMaxServos, 1>::Constant(kNaN));
53 }
54
55 void onDeactivateWithGoal(std::shared_ptr<const Goal> /*goal_ptr*/) final { stopActuators(); }
56
57 void updateSetpointOnCancel(
58 float /*dt_s*/, std::shared_ptr<const Goal> /*goal_ptr*/, std::shared_ptr<Feedback> /*feedback_ptr*/,
59 std::shared_ptr<Result> /*result_ptr*/) final
60 {
61 stopActuators();
62 completed(px4_ros2::Result::Success);
63 }
64
65 void updateSetpointWithGoal(
66 float /*dt_s*/, std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> /*feedback_ptr*/,
67 std::shared_ptr<Result> /*result_ptr*/) final
68 {
69 constexpr auto kNaN = std::numeric_limits<float>::quiet_NaN();
70 constexpr int kMaxMotors = px4_ros2::DirectActuatorsSetpointType::kMaxNumMotors;
71 constexpr int kMaxServos = px4_ros2::DirectActuatorsSetpointType::kMaxNumServos;
72
73 // Build motor command vector (NaN = disarmed for unspecified channels)
74 Eigen::Matrix<float, kMaxMotors, 1> motor_cmds = Eigen::Matrix<float, kMaxMotors, 1>::Constant(kNaN);
75 for (size_t i = 0; i < std::min(goal_ptr->motor_commands.size(), static_cast<size_t>(kMaxMotors)); ++i) {
76 motor_cmds(i) = goal_ptr->motor_commands[i];
77 }
78
79 // Build servo command vector (NaN = disarmed for unspecified channels)
80 Eigen::Matrix<float, kMaxServos, 1> servo_cmds = Eigen::Matrix<float, kMaxServos, 1>::Constant(kNaN);
81 for (size_t i = 0; i < std::min(goal_ptr->servo_commands.size(), static_cast<size_t>(kMaxServos)); ++i) {
82 servo_cmds(i) = goal_ptr->servo_commands[i];
83 }
84
85 const rclcpp::Duration elapsed = node().get_clock()->now() - activation_time_;
86 const rclcpp::Duration hold_duration =
87 rclcpp::Duration::from_nanoseconds(static_cast<int64_t>(goal_ptr->hold_period_ms) * 1'000'000LL);
88
89 if (elapsed < hold_duration) {
90 // First call or still within hold period: send the requested commands
91 actuator_setpoint_ptr_->updateMotors(motor_cmds);
92 actuator_setpoint_ptr_->updateServos(servo_cmds);
93 } else {
94 // Hold period elapsed: stop all actuators and signal completion
95 stopActuators();
96 completed(px4_ros2::Result::Success);
97 }
98 }
99};
100
101class SetActuatorsSkill : public ModeExecutorFactory<SetActuatorsActionType, SetActuatorsMode>
102{
103public:
104 explicit SetActuatorsSkill(const rclcpp::NodeOptions & options)
105 : ModeExecutorFactory{
106 _AUTO_APMS_PX4__SET_ACTUATORS_ACTION_NAME, options, VehicleCommandClient::FlightMode::Unset, true}
107 {
108 }
109};
110
111} // namespace auto_apms_px4
112
113#include "rclcpp_components/register_node_macro.hpp"
114RCLCPP_COMPONENTS_REGISTER_NODE(auto_apms_px4::SetActuatorsSkill)
Generic template class for a custom PX4 mode.
Definition mode.hpp:42
Helper template class that creates a ModeExecutor for a custom PX4 mode implemented by inheriting fro...
Implementation of PX4 mode peers offered by px4_ros2_cpp enabling integration with AutoAPMS.
Definition mode.hpp:28