From 7c63468e8bbff1da617ff1a746ade6281de24709 Mon Sep 17 00:00:00 2001 From: chfriedrich98 Date: Mon, 17 Feb 2025 11:56:05 +0100 Subject: [PATCH] mecanum: refactor code architecture --- .../airframes/4015_gz_r1_rover_mecanum | 48 +- msg/CMakeLists.txt | 5 +- msg/RoverMecanumGuidanceStatus.msg | 7 - msg/RoverMecanumSetpoint.msg | 11 - msg/RoverMecanumStatus.msg | 17 - src/modules/logger/logged_topics.cpp | 5 +- src/modules/rover_mecanum/CMakeLists.txt | 14 +- src/modules/rover_mecanum/Kconfig | 3 +- .../CMakeLists.txt | 9 +- .../MecanumAttControl/MecanumAttControl.cpp | 215 +++++++++ .../MecanumAttControl/MecanumAttControl.hpp | 142 ++++++ .../MecanumPosVelControl/CMakeLists.txt | 40 ++ .../MecanumPosVelControl.cpp | 426 ++++++++++++++++++ .../MecanumPosVelControl.hpp | 233 ++++++++++ .../CMakeLists.txt | 10 +- .../MecanumRateControl/MecanumRateControl.cpp | 190 ++++++++ .../MecanumRateControl/MecanumRateControl.hpp | 144 ++++++ src/modules/rover_mecanum/RoverMecanum.cpp | 344 ++++---------- src/modules/rover_mecanum/RoverMecanum.hpp | 126 +++--- .../RoverMecanumControl.cpp | 323 ------------- .../RoverMecanumControl.hpp | 177 -------- .../RoverMecanumGuidance.cpp | 206 --------- .../RoverMecanumGuidance.hpp | 139 ------ src/modules/rover_mecanum/module.yaml | 200 ++------ 24 files changed, 1634 insertions(+), 1400 deletions(-) delete mode 100644 msg/RoverMecanumGuidanceStatus.msg delete mode 100644 msg/RoverMecanumSetpoint.msg delete mode 100644 msg/RoverMecanumStatus.msg rename src/modules/rover_mecanum/{RoverMecanumGuidance => MecanumAttControl}/CMakeLists.txt (86%) create mode 100644 src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.cpp create mode 100644 src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.hpp create mode 100644 src/modules/rover_mecanum/MecanumPosVelControl/CMakeLists.txt create mode 100644 src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.cpp create mode 100644 src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.hpp rename src/modules/rover_mecanum/{RoverMecanumControl => MecanumRateControl}/CMakeLists.txt (86%) create mode 100644 src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.cpp create mode 100644 src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.hpp delete mode 100644 src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.cpp delete mode 100644 src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.hpp delete mode 100644 src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.cpp delete mode 100644 src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.hpp diff --git a/ROMFS/px4fmu_common/init.d-posix/airframes/4015_gz_r1_rover_mecanum b/ROMFS/px4fmu_common/init.d-posix/airframes/4015_gz_r1_rover_mecanum index e252b3cfa0..724e7613df 100644 --- a/ROMFS/px4fmu_common/init.d-posix/airframes/4015_gz_r1_rover_mecanum +++ b/ROMFS/px4fmu_common/init.d-posix/airframes/4015_gz_r1_rover_mecanum @@ -11,28 +11,38 @@ PX4_SIM_MODEL=${PX4_SIM_MODEL:=r1_rover_mecanum} param set-default SIM_GZ_EN 1 # Gazebo bridge -# Rover parameters -param set-default RM_WHEEL_TRACK 0.3 -param set-default RM_YAW_RATE_I 0.1 -param set-default RM_YAW_RATE_P 0.1 -param set-default RM_MAX_ACCEL 3 -param set-default RM_MAX_DECEL 5 -param set-default RM_MAX_JERK 5 -param set-default RM_MAX_SPEED 2 -param set-default RM_MAX_THR_SPD 2.2 -param set-default RM_MAX_THR_YAW_R 1.2 -param set-default RM_YAW_P 5 -param set-default RM_YAW_I 0.1 -param set-default RM_MAX_YAW_RATE 120 -param set-default RM_MAX_YAW_ACCEL 240 -param set-default RM_MISS_VEL_GAIN 1 -param set-default RM_SPEED_I 0.01 -param set-default RM_SPEED_P 0.1 +param set-default NAV_ACC_RAD 0.5 -# Pure pursuit parameters +# Mecanum Parameters +param set-default RM_WHEEL_TRACK 0.3 +param set-default RM_MAX_THR_YAW_R 1.2 +param set-default RM_MISS_SPD_GAIN 1 + +# Rover Control Parameters +param set-default RO_ACCEL_LIM 3 +param set-default RO_DECEL_LIM 5 +param set-default RO_JERK_LIM 30 +param set-default RO_MAX_THR_SPEED 2.1 + +# Rover Rate Control Parameters +param set-default RO_YAW_RATE_I 0.1 +param set-default RO_YAW_RATE_P 0.1 +param set-default RO_YAW_RATE_LIM 120 +param set-default RO_YAW_ACCEL_LIM 240 +param set-default RO_YAW_DECEL_LIM 1000 + +# Rover Attitude Control Parameters +param set-default RO_YAW_P 5 + +# Rover Position Control Parameters +param set-default RO_SPEED_LIM 2 +param set-default RO_SPEED_I 0.5 +param set-default RO_SPEED_P 1 + +# Pure Pursuit parameters +param set-default PP_LOOKAHD_GAIN 0.5 param set-default PP_LOOKAHD_MAX 10 param set-default PP_LOOKAHD_MIN 1 -param set-default PP_LOOKAHD_GAIN 0.5 # Simulated sensors param set-default SENS_EN_GPSSIM 1 diff --git a/msg/CMakeLists.txt b/msg/CMakeLists.txt index a88567b286..8ef4407504 100644 --- a/msg/CMakeLists.txt +++ b/msg/CMakeLists.txt @@ -173,14 +173,11 @@ set(msg_files RcParameterMap.msg RoverAttitudeSetpoint.msg RoverAttitudeStatus.msg - RoverVelocityStatus.msg RoverRateSetpoint.msg RoverRateStatus.msg RoverSteeringSetpoint.msg RoverThrottleSetpoint.msg - RoverMecanumGuidanceStatus.msg - RoverMecanumSetpoint.msg - RoverMecanumStatus.msg + RoverVelocityStatus.msg Rpm.msg RtlStatus.msg RtlTimeEstimate.msg diff --git a/msg/RoverMecanumGuidanceStatus.msg b/msg/RoverMecanumGuidanceStatus.msg deleted file mode 100644 index fba920033b..0000000000 --- a/msg/RoverMecanumGuidanceStatus.msg +++ /dev/null @@ -1,7 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -float32 lookahead_distance # [m] Lookahead distance of pure the pursuit controller -float32 heading_error # [rad] Heading error of the pure pursuit controller -float32 desired_speed # [m/s] Desired velocity magnitude (speed) - -# TOPICS rover_mecanum_guidance_status diff --git a/msg/RoverMecanumSetpoint.msg b/msg/RoverMecanumSetpoint.msg deleted file mode 100644 index 0cc9415be1..0000000000 --- a/msg/RoverMecanumSetpoint.msg +++ /dev/null @@ -1,11 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -float32 forward_speed_setpoint # [m/s] Desired forward speed -float32 forward_speed_setpoint_normalized # [-1, 1] Desired normalized forward speed -float32 lateral_speed_setpoint # [m/s] Desired lateral speed -float32 lateral_speed_setpoint_normalized # [-1, 1] Desired normalized lateral speed -float32 yaw_rate_setpoint # [rad/s] Desired yaw rate -float32 speed_diff_setpoint_normalized # [-1, 1] Normalized speed difference between the left and right wheels -float32 yaw_setpoint # [rad] Desired yaw (heading) - -# TOPICS rover_mecanum_setpoint diff --git a/msg/RoverMecanumStatus.msg b/msg/RoverMecanumStatus.msg deleted file mode 100644 index 7547fd5be6..0000000000 --- a/msg/RoverMecanumStatus.msg +++ /dev/null @@ -1,17 +0,0 @@ -uint64 timestamp # time since system start (microseconds) - -float32 measured_forward_speed # [m/s] Measured speed in body x direction. Positiv: forwards, Negativ: backwards -float32 adjusted_forward_speed_setpoint # [m/s] Speed setpoint after applying slew rate -float32 measured_lateral_speed # [m/s] Measured speed in body y direction. Positiv: right, Negativ: left -float32 adjusted_lateral_speed_setpoint # [m/s] Speed setpoint after applying slew rate -float32 measured_yaw_rate # [rad/s] Measured yaw rate -float32 clyaw_yaw_rate_setpoint # [rad/s] Yaw rate setpoint output by the closed loop yaw controller -float32 adjusted_yaw_rate_setpoint # [rad/s] Yaw rate setpoint from the closed loop yaw controller -float32 measured_yaw # [rad] Measured yaw -float32 adjusted_yaw_setpoint # [rad] Yaw setpoint after applying slew rate -float32 pid_yaw_rate_integral # Integral of the PID for the closed loop yaw rate controller -float32 pid_yaw_integral # Integral of the PID for the closed loop yaw controller -float32 pid_forward_throttle_integral # Integral of the PID for the closed loop forward speed controller -float32 pid_lateral_throttle_integral # Integral of the PID for the closed loop lateral speed controller - -# TOPICS rover_mecanum_status diff --git a/src/modules/logger/logged_topics.cpp b/src/modules/logger/logged_topics.cpp index b98d185aff..84d8e3eadd 100644 --- a/src/modules/logger/logged_topics.cpp +++ b/src/modules/logger/logged_topics.cpp @@ -105,14 +105,11 @@ void LoggedTopics::add_default_topics() add_topic("radio_status"); add_optional_topic("rover_attitude_setpoint", 100); add_optional_topic("rover_attitude_status", 100); - add_optional_topic("rover_velocity_status", 100); add_optional_topic("rover_rate_setpoint", 100); add_optional_topic("rover_rate_status", 100); add_optional_topic("rover_steering_setpoint", 100); add_optional_topic("rover_throttle_setpoint", 100); - add_optional_topic("rover_mecanum_guidance_status", 100); - add_optional_topic("rover_mecanum_setpoint", 100); - add_optional_topic("rover_mecanum_status", 100); + add_optional_topic("rover_velocity_status", 100); add_topic("rtl_time_estimate", 1000); add_topic("rtl_status", 2000); add_optional_topic("sensor_airflow", 100); diff --git a/src/modules/rover_mecanum/CMakeLists.txt b/src/modules/rover_mecanum/CMakeLists.txt index 8eea90fb2b..cf5e5b9187 100644 --- a/src/modules/rover_mecanum/CMakeLists.txt +++ b/src/modules/rover_mecanum/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2024 PX4 Development Team. All rights reserved. +# Copyright (c) 2025 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,8 +31,9 @@ # ############################################################################ -add_subdirectory(RoverMecanumGuidance) -add_subdirectory(RoverMecanumControl) +add_subdirectory(MecanumRateControl) +add_subdirectory(MecanumAttControl) +add_subdirectory(MecanumPosVelControl) px4_add_module( MODULE modules__rover_mecanum @@ -41,10 +42,11 @@ px4_add_module( RoverMecanum.cpp RoverMecanum.hpp DEPENDS - RoverMecanumGuidance - RoverMecanumControl + MecanumRateControl + MecanumAttControl + MecanumPosVelControl px4_work_queue - pure_pursuit + rover_control MODULE_CONFIG module.yaml ) diff --git a/src/modules/rover_mecanum/Kconfig b/src/modules/rover_mecanum/Kconfig index 46a24b3fc8..5c1778c67c 100644 --- a/src/modules/rover_mecanum/Kconfig +++ b/src/modules/rover_mecanum/Kconfig @@ -1,6 +1,5 @@ menuconfig MODULES_ROVER_MECANUM bool "rover_mecanum" default n - depends on MODULES_CONTROL_ALLOCATOR ---help--- - Enable support for control of mecanum rovers + Enable support for mecanum rovers diff --git a/src/modules/rover_mecanum/RoverMecanumGuidance/CMakeLists.txt b/src/modules/rover_mecanum/MecanumAttControl/CMakeLists.txt similarity index 86% rename from src/modules/rover_mecanum/RoverMecanumGuidance/CMakeLists.txt rename to src/modules/rover_mecanum/MecanumAttControl/CMakeLists.txt index a15f489245..34bad46d91 100644 --- a/src/modules/rover_mecanum/RoverMecanumGuidance/CMakeLists.txt +++ b/src/modules/rover_mecanum/MecanumAttControl/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2024 PX4 Development Team. All rights reserved. +# Copyright (c) 2025 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,8 +31,9 @@ # ############################################################################ -px4_add_library(RoverMecanumGuidance - RoverMecanumGuidance.cpp +px4_add_library(MecanumAttControl + MecanumAttControl.cpp ) -target_include_directories(RoverMecanumGuidance PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(MecanumAttControl PUBLIC PID) +target_include_directories(MecanumAttControl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.cpp b/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.cpp new file mode 100644 index 0000000000..0582c5f299 --- /dev/null +++ b/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "MecanumAttControl.hpp" + +using namespace time_literals; + +MecanumAttControl::MecanumAttControl(ModuleParams *parent) : ModuleParams(parent) +{ + _rover_rate_setpoint_pub.advertise(); + _rover_throttle_setpoint_pub.advertise(); + _rover_attitude_setpoint_pub.advertise(); + _rover_attitude_status_pub.advertise(); + updateParams(); +} + +void MecanumAttControl::updateParams() +{ + ModuleParams::updateParams(); + + if (_param_ro_yaw_rate_limit.get() > FLT_EPSILON) { + _max_yaw_rate = _param_ro_yaw_rate_limit.get() * M_DEG_TO_RAD_F; + } + + _pid_yaw.setGains(_param_ro_yaw_p.get(), 0.f, 0.f); + _pid_yaw.setIntegralLimit(_max_yaw_rate); + _pid_yaw.setOutputLimit(_max_yaw_rate); + _adjusted_yaw_setpoint.setSlewRate(_max_yaw_rate); +} + +void MecanumAttControl::updateAttControl() +{ + const hrt_abstime timestamp_prev = _timestamp; + _timestamp = hrt_absolute_time(); + _dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f; + + if (_vehicle_control_mode_sub.updated()) { + _vehicle_control_mode_sub.copy(&_vehicle_control_mode); + } + + if (_vehicle_attitude_sub.updated()) { + vehicle_attitude_s vehicle_attitude{}; + _vehicle_attitude_sub.copy(&vehicle_attitude); + matrix::Quatf vehicle_attitude_quaternion = matrix::Quatf(vehicle_attitude.q); + _vehicle_yaw = matrix::Eulerf(vehicle_attitude_quaternion).psi(); + } + + if (_vehicle_control_mode.flag_control_attitude_enabled && _vehicle_control_mode.flag_armed && runSanityChecks()) { + + if (_vehicle_control_mode.flag_control_manual_enabled || _vehicle_control_mode.flag_control_offboard_enabled) { + generateAttitudeSetpoint(); + } + + generateRateSetpoint(); + + } else { // Reset pid and slew rate when attitude control is not active + _pid_yaw.resetIntegral(); + _adjusted_yaw_setpoint.setForcedValue(0.f); + } + + // Publish attitude controller status (logging only) + rover_attitude_status_s rover_attitude_status; + rover_attitude_status.timestamp = _timestamp; + rover_attitude_status.measured_yaw = _vehicle_yaw; + rover_attitude_status.adjusted_yaw_setpoint = _adjusted_yaw_setpoint.getState(); + _rover_attitude_status_pub.publish(rover_attitude_status); + +} + +void MecanumAttControl::generateAttitudeSetpoint() +{ + const bool stab_mode_enabled = _vehicle_control_mode.flag_control_manual_enabled + && !_vehicle_control_mode.flag_control_position_enabled && _vehicle_control_mode.flag_control_attitude_enabled; + + if (stab_mode_enabled && _manual_control_setpoint_sub.updated()) { // Stab Mode + manual_control_setpoint_s manual_control_setpoint{}; + + if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { + + rover_throttle_setpoint_s rover_throttle_setpoint{}; + rover_throttle_setpoint.timestamp = _timestamp; + rover_throttle_setpoint.throttle_body_x = manual_control_setpoint.throttle; + rover_throttle_setpoint.throttle_body_y = manual_control_setpoint.roll; + _rover_throttle_setpoint_pub.publish(rover_throttle_setpoint); + + const float yaw_rate_setpoint = math::interpolate(math::deadzone(manual_control_setpoint.yaw, + _param_ro_yaw_stick_dz.get()), -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); + + if (fabsf(yaw_rate_setpoint) > FLT_EPSILON) { // Closed loop yaw rate control + _stab_yaw_setpoint = NAN; + _adjusted_yaw_setpoint.setForcedValue(0.f); + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = yaw_rate_setpoint; + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + + } else if (fabsf(rover_throttle_setpoint.throttle_body_x) > FLT_EPSILON + || fabsf(rover_throttle_setpoint.throttle_body_y) > + FLT_EPSILON) { // Closed loop yaw control if the yaw rate input is zero (keep current yaw) + if (!PX4_ISFINITE(_stab_yaw_setpoint)) { + _stab_yaw_setpoint = _vehicle_yaw; + } + + rover_attitude_setpoint_s rover_attitude_setpoint{}; + rover_attitude_setpoint.timestamp = _timestamp; + rover_attitude_setpoint.yaw_setpoint = _stab_yaw_setpoint; + _rover_attitude_setpoint_pub.publish(rover_attitude_setpoint); + + } else { // Reset yaw control and yaw rate setpoint + _stab_yaw_setpoint = NAN; + _adjusted_yaw_setpoint.setForcedValue(0.f); + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = 0.f; + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + } + + + } + + } else if (_vehicle_control_mode.flag_control_offboard_enabled) { // Offboard attitude control + trajectory_setpoint_s trajectory_setpoint{}; + _trajectory_setpoint_sub.copy(&trajectory_setpoint); + + if (_offboard_control_mode_sub.updated()) { + _offboard_control_mode_sub.copy(&_offboard_control_mode); + } + + const bool offboard_att_control = _offboard_control_mode.attitude; + + if (offboard_att_control && PX4_ISFINITE(trajectory_setpoint.yaw)) { + rover_attitude_setpoint_s rover_attitude_setpoint{}; + rover_attitude_setpoint.timestamp = _timestamp; + rover_attitude_setpoint.yaw_setpoint = trajectory_setpoint.yaw; + _rover_attitude_setpoint_pub.publish(rover_attitude_setpoint); + } + } +} + +void MecanumAttControl::generateRateSetpoint() +{ + if (_rover_attitude_setpoint_sub.updated()) { + _rover_attitude_setpoint_sub.copy(&_rover_attitude_setpoint); + } + + if (_rover_rate_setpoint_sub.updated()) { + _rover_rate_setpoint_sub.copy(&_rover_rate_setpoint); + } + + // Check if a new rate setpoint was already published from somewhere else + if (_rover_rate_setpoint.timestamp > _last_rate_setpoint_update + && _rover_rate_setpoint.timestamp > _rover_attitude_setpoint.timestamp) { + return; + } + + const float yaw_rate_setpoint = RoverControl::attitudeControl(_adjusted_yaw_setpoint, _pid_yaw, _max_yaw_rate, + _vehicle_yaw, _rover_attitude_setpoint.yaw_setpoint, _dt); + + _last_rate_setpoint_update = _timestamp; + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = math::constrain(yaw_rate_setpoint, -_max_yaw_rate, _max_yaw_rate); + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); +} + +bool MecanumAttControl::runSanityChecks() +{ + bool ret = true; + + if (_param_ro_yaw_rate_limit.get() < FLT_EPSILON) { + ret = false; + } + + if (_param_ro_yaw_p.get() < FLT_EPSILON) { + ret = false; + + if (_prev_param_check_passed) { + events::send(events::ID("mecanum_att_control_conf_invalid_yaw_p"), events::Log::Error, + "Invalid configuration of necessary parameter RO_YAW_P", _param_ro_yaw_p.get()); + } + } + + _prev_param_check_passed = ret; + return ret; +} diff --git a/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.hpp b/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.hpp new file mode 100644 index 0000000000..c6e9a3353e --- /dev/null +++ b/src/modules/rover_mecanum/MecanumAttControl/MecanumAttControl.hpp @@ -0,0 +1,142 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +// PX4 includes +#include +#include + +// Libraries +#include +#include +#include +#include +#include + +// uORB includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Class for mecanum attitude control. + */ +class MecanumAttControl : public ModuleParams +{ +public: + /** + * @brief Constructor for MecanumAttControl. + * @param parent The parent ModuleParams object. + */ + MecanumAttControl(ModuleParams *parent); + ~MecanumAttControl() = default; + + /** + * @brief Update attitude controller. + */ + void updateAttControl(); + +protected: + /** + * @brief Update the parameters of the module. + */ + void updateParams() override; + +private: + /** + * @brief Generate and publish roverAttitudeSetpoint from manualControlSetpoint (Stab Mode) + * or trajectorySetpoint (Offboard attitude control). + */ + void generateAttitudeSetpoint(); + + /** + * @brief Generate and publish roverRateSetpoint from roverAttitudeSetpoint. + */ + void generateRateSetpoint(); + + /** + * @brief Check if the necessary parameters are set. + * @return True if all checks pass. + */ + bool runSanityChecks(); + + // uORB subscriptions + uORB::Subscription _vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)}; + uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)}; + uORB::Subscription _trajectory_setpoint_sub{ORB_ID(trajectory_setpoint)}; + uORB::Subscription _offboard_control_mode_sub{ORB_ID(offboard_control_mode)}; + uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)}; + uORB::Subscription _actuator_motors_sub{ORB_ID(actuator_motors)}; + uORB::Subscription _rover_attitude_setpoint_sub{ORB_ID(rover_attitude_setpoint)}; + uORB::Subscription _rover_rate_setpoint_sub{ORB_ID(rover_rate_setpoint)}; + vehicle_control_mode_s _vehicle_control_mode{}; + rover_attitude_setpoint_s _rover_attitude_setpoint{}; + rover_rate_setpoint_s _rover_rate_setpoint{}; + offboard_control_mode_s _offboard_control_mode{}; + + // uORB publications + uORB::Publication _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)}; + uORB::Publication _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)}; + uORB::Publication _rover_attitude_setpoint_pub{ORB_ID(rover_attitude_setpoint)}; + uORB::Publication _rover_attitude_status_pub{ORB_ID(rover_attitude_status)}; + + // Variables + hrt_abstime _timestamp{0}; + hrt_abstime _last_rate_setpoint_update{0}; + float _vehicle_yaw{0.f}; + float _dt{0.f}; + float _max_yaw_rate{0.f}; + float _stab_yaw_setpoint{0.f}; // Yaw setpoint for stab mode, NAN if yaw rate is manually controlled [rad] + bool _prev_param_check_passed{true}; + + // Controllers + PID _pid_yaw; + SlewRateYaw _adjusted_yaw_setpoint; + + // Parameters + DEFINE_PARAMETERS( + (ParamFloat) _param_ro_yaw_rate_limit, + (ParamFloat) _param_ro_yaw_p, + (ParamFloat) _param_ro_yaw_stick_dz + ) +}; diff --git a/src/modules/rover_mecanum/MecanumPosVelControl/CMakeLists.txt b/src/modules/rover_mecanum/MecanumPosVelControl/CMakeLists.txt new file mode 100644 index 0000000000..692e4e661b --- /dev/null +++ b/src/modules/rover_mecanum/MecanumPosVelControl/CMakeLists.txt @@ -0,0 +1,40 @@ +############################################################################ +# +# Copyright (c) 2025 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +px4_add_library(MecanumPosVelControl + MecanumPosVelControl.cpp +) + +target_link_libraries(MecanumPosVelControl PUBLIC PID) +target_link_libraries(MecanumPosVelControl PUBLIC pure_pursuit) +target_include_directories(MecanumPosVelControl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.cpp b/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.cpp new file mode 100644 index 0000000000..a5ee63496e --- /dev/null +++ b/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "MecanumPosVelControl.hpp" + +using namespace time_literals; + +MecanumPosVelControl::MecanumPosVelControl(ModuleParams *parent) : ModuleParams(parent) +{ + _rover_rate_setpoint_pub.advertise(); + _rover_throttle_setpoint_pub.advertise(); + _rover_attitude_setpoint_pub.advertise(); + _rover_velocity_status_pub.advertise(); + updateParams(); +} + +void MecanumPosVelControl::updateParams() +{ + ModuleParams::updateParams(); + _pid_speed_x.setGains(_param_ro_speed_p.get(), _param_ro_speed_i.get(), 0.f); + _pid_speed_x.setIntegralLimit(1.f); + _pid_speed_x.setOutputLimit(1.f); + _pid_speed_y.setGains(_param_ro_speed_p.get(), _param_ro_speed_i.get(), 0.f); + _pid_speed_y.setIntegralLimit(1.f); + _pid_speed_y.setOutputLimit(1.f); + _max_yaw_rate = _param_ro_yaw_rate_limit.get() * M_DEG_TO_RAD_F; + + if (_param_ro_accel_limit.get() > FLT_EPSILON) { + _speed_x_setpoint.setSlewRate(_param_ro_accel_limit.get()); + _speed_y_setpoint.setSlewRate(_param_ro_accel_limit.get()); + } +} + +void MecanumPosVelControl::updatePosControl() +{ + const hrt_abstime timestamp_prev = _timestamp; + _timestamp = hrt_absolute_time(); + _dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f; + + updateSubscriptions(); + + if ((_vehicle_control_mode.flag_control_position_enabled || _vehicle_control_mode.flag_control_velocity_enabled) + && _vehicle_control_mode.flag_armed && runSanityChecks()) { + generateAttitudeSetpoint(); + + if (_param_ro_max_thr_speed.get() > FLT_EPSILON) { // Adjust speed setpoints if infeasible + if (_rover_steering_setpoint_sub.updated()) { + _rover_steering_setpoint_sub.copy(&_rover_steering_setpoint); + } + + float speed_body_x_setpoint_normalized = math::interpolate(_speed_body_x_setpoint, + -_param_ro_max_thr_speed.get(), _param_ro_max_thr_speed.get(), -1.f, 1.f); + + float speed_body_y_setpoint_normalized = math::interpolate(_speed_body_y_setpoint, + -_param_ro_max_thr_speed.get(), _param_ro_max_thr_speed.get(), -1.f, 1.f); + + const float total_speed = fabsf(speed_body_x_setpoint_normalized) + fabsf(speed_body_y_setpoint_normalized) + fabsf( + _rover_steering_setpoint.normalized_speed_diff); + + if (total_speed > 1.f) { + const float theta = atan2f(fabsf(speed_body_y_setpoint_normalized), fabsf(speed_body_x_setpoint_normalized)); + const float magnitude = (1.f - fabsf(_rover_steering_setpoint.normalized_speed_diff)) / (sinf(theta) + cosf(theta)); + const float normalization = 1.f / (sqrtf(powf(speed_body_x_setpoint_normalized, + 2.f) + powf(speed_body_y_setpoint_normalized, 2.f))); + speed_body_x_setpoint_normalized *= magnitude * normalization; + speed_body_y_setpoint_normalized *= magnitude * normalization; + _speed_body_x_setpoint = math::interpolate(speed_body_x_setpoint_normalized, -1.f, 1.f, + -_param_ro_max_thr_speed.get(), _param_ro_max_thr_speed.get()); + _speed_body_y_setpoint = math::interpolate(speed_body_y_setpoint_normalized, -1.f, 1.f, + -_param_ro_max_thr_speed.get(), _param_ro_max_thr_speed.get()); + } + } + + rover_throttle_setpoint_s rover_throttle_setpoint{}; + rover_throttle_setpoint.timestamp = _timestamp; + _speed_body_x_setpoint = fabsf(_speed_body_x_setpoint) > _param_ro_speed_th.get() ? _speed_body_x_setpoint : 0.f; + _speed_body_y_setpoint = fabsf(_speed_body_y_setpoint) > _param_ro_speed_th.get() ? _speed_body_y_setpoint : 0.f; + rover_throttle_setpoint.throttle_body_x = RoverControl::speedControl(_speed_x_setpoint, _pid_speed_x, + _speed_body_x_setpoint, _vehicle_speed_body_x, _param_ro_accel_limit.get(), _param_ro_decel_limit.get(), + _param_ro_max_thr_speed.get(), _dt); + rover_throttle_setpoint.throttle_body_y = RoverControl::speedControl(_speed_y_setpoint, _pid_speed_y, + _speed_body_y_setpoint, _vehicle_speed_body_y, _param_ro_accel_limit.get(), _param_ro_decel_limit.get(), + _param_ro_max_thr_speed.get(), _dt); + _rover_throttle_setpoint_pub.publish(rover_throttle_setpoint); + + } else { // Reset controller and slew rate when position control is not active + _pid_speed_x.resetIntegral(); + _speed_x_setpoint.setForcedValue(0.f); + _pid_speed_y.resetIntegral(); + _speed_y_setpoint.setForcedValue(0.f); + } + + // Publish position controller status (logging only) + rover_velocity_status_s rover_velocity_status; + rover_velocity_status.timestamp = _timestamp; + rover_velocity_status.measured_speed_body_x = _vehicle_speed_body_x; + rover_velocity_status.speed_body_x_setpoint = _speed_body_x_setpoint; + rover_velocity_status.adjusted_speed_body_x_setpoint = _speed_x_setpoint.getState(); + rover_velocity_status.measured_speed_body_y = _vehicle_speed_body_y; + rover_velocity_status.speed_body_y_setpoint = _speed_body_y_setpoint; + rover_velocity_status.adjusted_speed_body_y_setpoint = _speed_y_setpoint.getState(); + rover_velocity_status.pid_throttle_body_x_integral = _pid_speed_x.getIntegral(); + rover_velocity_status.pid_throttle_body_y_integral = _pid_speed_y.getIntegral(); + _rover_velocity_status_pub.publish(rover_velocity_status); +} + +void MecanumPosVelControl::updateSubscriptions() +{ + if (_vehicle_control_mode_sub.updated()) { + _vehicle_control_mode_sub.copy(&_vehicle_control_mode); + } + + if (_vehicle_attitude_sub.updated()) { + vehicle_attitude_s vehicle_attitude{}; + _vehicle_attitude_sub.copy(&vehicle_attitude); + _vehicle_attitude_quaternion = matrix::Quatf(vehicle_attitude.q); + _vehicle_yaw = matrix::Eulerf(_vehicle_attitude_quaternion).psi(); + } + + if (_vehicle_local_position_sub.updated()) { + vehicle_local_position_s vehicle_local_position{}; + _vehicle_local_position_sub.copy(&vehicle_local_position); + + if (!_global_ned_proj_ref.isInitialized() + || (_global_ned_proj_ref.getProjectionReferenceTimestamp() != vehicle_local_position.ref_timestamp)) { + _global_ned_proj_ref.initReference(vehicle_local_position.ref_lat, vehicle_local_position.ref_lon, + vehicle_local_position.ref_timestamp); + } + + _curr_pos_ned = Vector2f(vehicle_local_position.x, vehicle_local_position.y); + Vector3f velocity_in_local_frame(vehicle_local_position.vx, vehicle_local_position.vy, vehicle_local_position.vz); + Vector3f velocity_in_body_frame = _vehicle_attitude_quaternion.rotateVectorInverse(velocity_in_local_frame); + _vehicle_speed_body_x = fabsf(velocity_in_body_frame(0)) > _param_ro_speed_th.get() ? velocity_in_body_frame(0) : 0.f; + _vehicle_speed_body_y = fabsf(velocity_in_body_frame(1)) > _param_ro_speed_th.get() ? velocity_in_body_frame(1) : 0.f; + } + + if (_vehicle_status_sub.updated()) { + vehicle_status_s vehicle_status; + _vehicle_status_sub.copy(&vehicle_status); + _nav_state = vehicle_status.nav_state; + } + +} + +void MecanumPosVelControl::generateAttitudeSetpoint() +{ + if (_vehicle_control_mode.flag_control_manual_enabled + && _vehicle_control_mode.flag_control_position_enabled) { // Position Mode + manualPositionMode(); + + } else if (_vehicle_control_mode.flag_control_offboard_enabled) { // Offboard Control + if (_offboard_control_mode_sub.updated()) { + _offboard_control_mode_sub.copy(&_offboard_control_mode); + } + + if (_offboard_control_mode.position) { + offboardPositionMode(); + + } else if (_offboard_control_mode.velocity) { + offboardVelocityMode(); + } + + } else if (_vehicle_control_mode.flag_control_auto_enabled) { // Auto Mode + autoPositionMode(); + } +} + +void MecanumPosVelControl::manualPositionMode() +{ + manual_control_setpoint_s manual_control_setpoint{}; + + if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { + _speed_body_x_setpoint = math::interpolate(manual_control_setpoint.throttle, + -1.f, 1.f, -_param_ro_speed_limit.get(), _param_ro_speed_limit.get()); + _speed_body_y_setpoint = math::interpolate(manual_control_setpoint.roll, + -1.f, 1.f, -_param_ro_speed_limit.get(), _param_ro_speed_limit.get()); + const float yaw_rate_setpoint = math::interpolate(math::deadzone(manual_control_setpoint.yaw, + _param_ro_yaw_stick_dz.get()), -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); + + if (fabsf(yaw_rate_setpoint) > FLT_EPSILON) { // Closed loop yaw rate control + _pos_ctl_yaw_setpoint = NAN; + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = yaw_rate_setpoint; + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + + } else if ((fabsf(_speed_body_x_setpoint) > FLT_EPSILON + || fabsf(_speed_body_y_setpoint) > + FLT_EPSILON)) { // Course control if the steering input is zero (keep driving on a straight line) + const Vector3f velocity = Vector3f(_speed_body_x_setpoint, _speed_body_y_setpoint, 0.f); + const float velocity_magnitude_setpoint = velocity.norm(); + const Vector3f pos_ctl_course_direction_local = _vehicle_attitude_quaternion.rotateVector(velocity.normalized()); + const Vector2f pos_ctl_course_direction_temp = Vector2f(pos_ctl_course_direction_local(0), + pos_ctl_course_direction_local(1)); + + // Reset course control if course direction change is above threshold + if (fabsf(asinf(pos_ctl_course_direction_temp % _pos_ctl_course_direction)) > _param_rm_course_ctl_th.get()) { + _pos_ctl_yaw_setpoint = NAN; + } + + if (!PX4_ISFINITE(_pos_ctl_yaw_setpoint)) { + _pos_ctl_start_position_ned = _curr_pos_ned; + _pos_ctl_yaw_setpoint = _vehicle_yaw; + _pos_ctl_course_direction = pos_ctl_course_direction_temp; + } + + // Construct a 'target waypoint' for course control s.t. it is never within the maximum lookahead of the rover + const float vector_scaling = sqrtf(powf(_param_pp_lookahd_max.get(), + 2) + powf(_posctl_pure_pursuit.getCrosstrackError(), 2)) + _posctl_pure_pursuit.getDistanceOnLineSegment(); + const Vector2f target_waypoint_ned = _pos_ctl_start_position_ned + vector_scaling * _pos_ctl_course_direction; + const float bearing_setpoint = _posctl_pure_pursuit.calcDesiredHeading(target_waypoint_ned, _pos_ctl_start_position_ned, + _curr_pos_ned, velocity_magnitude_setpoint); + const float bearing_setpoint_body_frame = matrix::wrap_pi(bearing_setpoint - _vehicle_yaw); + _speed_body_x_setpoint = velocity_magnitude_setpoint * cosf(bearing_setpoint_body_frame); + _speed_body_y_setpoint = velocity_magnitude_setpoint * sinf(bearing_setpoint_body_frame); + rover_attitude_setpoint_s rover_attitude_setpoint{}; + rover_attitude_setpoint.timestamp = _timestamp; + rover_attitude_setpoint.yaw_setpoint = _pos_ctl_yaw_setpoint; + _rover_attitude_setpoint_pub.publish(rover_attitude_setpoint); + + } else { // Reset course control and yaw rate setpoint + _pos_ctl_yaw_setpoint = NAN; + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = 0.f; + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + } + } +} + +void MecanumPosVelControl::offboardPositionMode() +{ + trajectory_setpoint_s trajectory_setpoint{}; + _trajectory_setpoint_sub.copy(&trajectory_setpoint); + + // Translate trajectory setpoint to rover setpoints + const Vector2f target_waypoint_ned(trajectory_setpoint.position[0], trajectory_setpoint.position[1]); + const float distance_to_target = (target_waypoint_ned - _curr_pos_ned).norm(); + + if (target_waypoint_ned.isAllFinite() && distance_to_target > _param_nav_acc_rad.get()) { + const float velocity_magnitude_setpoint = math::min(math::trajectory::computeMaxSpeedFromDistance( + _param_ro_jerk_limit.get(), + _param_ro_decel_limit.get(), distance_to_target, 0.f), _param_ro_speed_limit.get()); + const float bearing_setpoint = _posctl_pure_pursuit.calcDesiredHeading(target_waypoint_ned, _curr_pos_ned, + _curr_pos_ned, fabsf(_speed_body_x_setpoint)); + const float bearing_setpoint_body_frame = matrix::wrap_pi(bearing_setpoint - _vehicle_yaw); + _speed_body_x_setpoint = velocity_magnitude_setpoint * cosf(bearing_setpoint_body_frame); + _speed_body_y_setpoint = velocity_magnitude_setpoint * sinf(bearing_setpoint_body_frame); + + } else { + _speed_body_x_setpoint = 0.f; + _speed_body_y_setpoint = 0.f; + } +} + +void MecanumPosVelControl::offboardVelocityMode() +{ + trajectory_setpoint_s trajectory_setpoint{}; + _trajectory_setpoint_sub.copy(&trajectory_setpoint); + + const Vector3f velocity_in_local_frame(trajectory_setpoint.velocity[0], trajectory_setpoint.velocity[1], + trajectory_setpoint.velocity[2]); + const Vector3f velocity_in_body_frame = _vehicle_attitude_quaternion.rotateVectorInverse(velocity_in_local_frame); + + if (velocity_in_body_frame.isAllFinite()) { + _speed_body_x_setpoint = velocity_in_body_frame(0); + _speed_body_y_setpoint = velocity_in_body_frame(1); + + } else { + _speed_body_x_setpoint = 0.f; + _speed_body_y_setpoint = 0.f; + } +} + +void MecanumPosVelControl::autoPositionMode() +{ + updateAutoSubscriptions(); + + const float distance_to_curr_wp = sqrt(powf(_curr_pos_ned(0) - _curr_wp_ned(0), + 2) + powf(_curr_pos_ned(1) - _curr_wp_ned(1), 2)); + + if (_nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) { // Check RTL arrival + _mission_finished = distance_to_curr_wp < _param_nav_acc_rad.get(); + } + + const float velocity_magnitude = calcVelocityMagnitude(_auto_speed, distance_to_curr_wp, _param_ro_decel_limit.get(), + _param_ro_jerk_limit.get(), _waypoint_transition_angle, _param_ro_speed_limit.get(), _param_rm_miss_spd_gain.get(), + _nav_state); + const float bearing_setpoint = _posctl_pure_pursuit.calcDesiredHeading(_curr_wp_ned, _prev_wp_ned, _curr_pos_ned, + velocity_magnitude); + const float bearing_setpoint_body_frame = matrix::wrap_pi(bearing_setpoint - _vehicle_yaw); + Vector2f desired_velocity(0.f, 0.f); + _speed_body_x_setpoint = _mission_finished ? 0.f : velocity_magnitude * cosf(bearing_setpoint_body_frame); + _speed_body_y_setpoint = _mission_finished ? 0.f : velocity_magnitude * sinf(bearing_setpoint_body_frame); + rover_attitude_setpoint_s rover_attitude_setpoint{}; + rover_attitude_setpoint.timestamp = _timestamp; + rover_attitude_setpoint.yaw_setpoint = _auto_yaw; + _rover_attitude_setpoint_pub.publish(rover_attitude_setpoint); +} + +void MecanumPosVelControl::updateAutoSubscriptions() +{ + if (_home_position_sub.updated()) { + home_position_s home_position{}; + _home_position_sub.copy(&home_position); + _home_position = Vector2d(home_position.lat, home_position.lon); + } + + if (_position_setpoint_triplet_sub.updated()) { + position_setpoint_triplet_s position_setpoint_triplet{}; + _position_setpoint_triplet_sub.copy(&position_setpoint_triplet); + + RoverControl::globalToLocalSetpointTriplet(_curr_wp_ned, _prev_wp_ned, _next_wp_ned, position_setpoint_triplet, + _curr_pos_ned, _home_position, _global_ned_proj_ref); + + _waypoint_transition_angle = RoverControl::calcWaypointTransitionAngle(_prev_wp_ned, _curr_wp_ned, _next_wp_ned); + + // Waypoint cruising speed + _auto_speed = position_setpoint_triplet.current.cruising_speed > 0.f ? math::constrain( + position_setpoint_triplet.current.cruising_speed, 0.f, _param_ro_speed_limit.get()) : _param_ro_speed_limit.get(); + + // Waypoint yaw setpoint + if (PX4_ISFINITE(position_setpoint_triplet.current.yaw)) { + _auto_yaw = position_setpoint_triplet.current.yaw; + + } else { + _auto_yaw = _vehicle_yaw; + } + } + + if (_mission_result_sub.updated()) { + mission_result_s mission_result{}; + _mission_result_sub.copy(&mission_result); + _mission_finished = mission_result.finished; + } +} + +float MecanumPosVelControl::calcVelocityMagnitude(const float auto_speed, const float distance_to_curr_wp, + const float max_decel, const float max_jerk, const float waypoint_transition_angle, const float max_speed, + const float miss_spd_gain, const int nav_state) +{ + float velocity_magnitude{auto_speed}; + + if (max_jerk > FLT_EPSILON && max_decel > FLT_EPSILON + && miss_spd_gain > FLT_EPSILON) { + float max_velocity_magnitude = velocity_magnitude; + + if (nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) { + max_velocity_magnitude = math::trajectory::computeMaxSpeedFromDistance(max_jerk, + max_decel, distance_to_curr_wp, 0.f); + + } else if (PX4_ISFINITE(waypoint_transition_angle)) { + const float speed_reduction = math::constrain(miss_spd_gain * math::interpolate(M_PI_F - waypoint_transition_angle, 0.f, + M_PI_F, 0.f, 1.f), 0.f, 1.f); + max_velocity_magnitude = math::trajectory::computeMaxSpeedFromDistance(max_jerk, max_decel, distance_to_curr_wp, + max_speed * (1.f - speed_reduction)); + } + + velocity_magnitude = math::constrain(max_velocity_magnitude, -auto_speed, auto_speed); + } + + return velocity_magnitude; +} + +bool MecanumPosVelControl::runSanityChecks() +{ + bool ret = true; + + if (_param_ro_yaw_rate_limit.get() < FLT_EPSILON) { + ret = false; + } + + if (_param_ro_speed_limit.get() < FLT_EPSILON) { + ret = false; + + if (_prev_param_check_passed) { + events::send(events::ID("mecanum_posVel_control_conf_invalid_speed_lim"), events::Log::Error, + "Invalid configuration of necessary parameter RO_SPEED_LIM", _param_ro_speed_limit.get()); + } + + } + + if (_param_ro_max_thr_speed.get() < FLT_EPSILON && _param_ro_speed_p.get() < FLT_EPSILON) { + ret = false; + + if (_prev_param_check_passed) { + events::send(events::ID("mecanum_posVel_control_conf_invalid_speed_control"), events::Log::Error, + "Invalid configuration for speed control: Neither feed forward (RO_MAX_THR_SPD) nor feedback (RO_SPEED_P) is setup", + _param_ro_max_thr_speed.get(), + _param_ro_speed_p.get()); + } + } + + _prev_param_check_passed = ret; + return ret; +} diff --git a/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.hpp b/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.hpp new file mode 100644 index 0000000000..4659588fdf --- /dev/null +++ b/src/modules/rover_mecanum/MecanumPosVelControl/MecanumPosVelControl.hpp @@ -0,0 +1,233 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +// PX4 includes +#include +#include + +// Libraries +#include +#include +#include +#include +#include +#include +#include + +// uORB includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace matrix; + +/** + * @brief Class for mecanum position/velocity control. + */ +class MecanumPosVelControl : public ModuleParams +{ +public: + /** + * @brief Constructor for MecanumPosVelControl. + * @param parent The parent ModuleParams object. + */ + MecanumPosVelControl(ModuleParams *parent); + ~MecanumPosVelControl() = default; + + /** + * @brief Update position controller. + */ + void updatePosControl(); + +protected: + /** + * @brief Update the parameters of the module. + */ + void updateParams() override; + +private: + /** + * @brief Update uORB subscriptions used in position controller. + */ + void updateSubscriptions(); + + /** + * @brief Generate and publish roverAttitudeSetpoint from manualControlSetpoint (Position Mode) or + * trajcetorySetpoint (Offboard position or velocity control) or + * positionSetpointTriplet (Auto Mode). + */ + void generateAttitudeSetpoint(); + + /** + * @brief Generate and publish roverThrottleSetpoint and roverAttitudeSetpoint or roverRateSetpoint from manualControlSetpoint. + */ + void manualPositionMode(); + + /** + * @brief Generate and publish roverAttitudeSetpoint from position of trajectorySetpoint. + */ + void offboardPositionMode(); + + /** + * @brief Generate and publish roverAttitudeSetpoint from velocity of trajectorySetpoint. + */ + void offboardVelocityMode(); + + /** + * @brief Generate and publish roverThrottleSetpoint and roverAttitudeSetpoint from positionSetpointTriplet. + */ + void autoPositionMode(); + + /** + * @brief Update uORB subscriptions used for auto modes. + */ + void updateAutoSubscriptions(); + + /** + * @brief Calculate the velocity magnitude setpoint. During waypoint transition the speed is restricted to + * Maximum_speed * (1 - normalized_transition_angle * RM_MISS_VEL_GAIN). + * On straight lines it is based on a speed trajectory such that the rover will arrive at the next waypoint transition + * with the desired waypoiny transition speed under consideration of the maximum deceleration and jerk. + * @param auto_speed Default auto speed [m/s]. + * @param distance_to_curr_wp Distance to the current waypoint [m]. + * @param max_decel Maximum allowed deceleration [m/s^2]. + * @param max_jerk Maximum allowed jerk [m/s^3]. + * @param waypoint_transition_angle Angle between the prevWP-currWP and currWP-nextWP line segments [rad] + * @param max_speed Maximum velocity magnitude setpoint [m/s] + * @param miss_spd_gain Tuning parameter for the speed reduction during waypoint transition. + * @param nav_state Vehicle navigation state + * @return Velocity magnitude setpoint [m/s]. + */ + float calcVelocityMagnitude(float auto_speed, float distance_to_curr_wp, float max_decel, float max_jerk, + float waypoint_transition_angle, float max_speed, float miss_spd_gain, int nav_state); + + /** + * @brief Check if the necessary parameters are set. + * @return True if all checks pass. + */ + bool runSanityChecks(); + + // uORB subscriptions + uORB::Subscription _vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)}; + uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)}; + uORB::Subscription _trajectory_setpoint_sub{ORB_ID(trajectory_setpoint)}; + uORB::Subscription _offboard_control_mode_sub{ORB_ID(offboard_control_mode)}; + uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)}; + uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)}; + uORB::Subscription _position_setpoint_triplet_sub{ORB_ID(position_setpoint_triplet)}; + uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)}; + uORB::Subscription _local_position_sub{ORB_ID(vehicle_local_position)}; + uORB::Subscription _mission_result_sub{ORB_ID(mission_result)}; + uORB::Subscription _home_position_sub{ORB_ID(home_position)}; + uORB::Subscription _rover_steering_setpoint_sub{ORB_ID(rover_steering_setpoint)}; + vehicle_control_mode_s _vehicle_control_mode{}; + offboard_control_mode_s _offboard_control_mode{}; + rover_steering_setpoint_s _rover_steering_setpoint{}; + + // uORB publications + uORB::Publication _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)}; + uORB::Publication _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)}; + uORB::Publication _rover_attitude_setpoint_pub{ORB_ID(rover_attitude_setpoint)}; + uORB::Publication _rover_velocity_status_pub{ORB_ID(rover_velocity_status)}; + uORB::Publication _position_controller_status_pub{ORB_ID(position_controller_status)}; + + // Variables + hrt_abstime _timestamp{0}; + Quatf _vehicle_attitude_quaternion{}; + Vector2d _home_position{}; + Vector2f _curr_pos_ned{}; + Vector2f _pos_ctl_course_direction{}; + Vector2f _pos_ctl_start_position_ned{}; + Vector2f _curr_wp_ned{}; + Vector2f _prev_wp_ned{}; + Vector2f _next_wp_ned{}; + float _vehicle_speed_body_x{0.f}; + float _vehicle_speed_body_y{0.f}; + float _vehicle_yaw{0.f}; + float _max_yaw_rate{0.f}; + float _speed_body_x_setpoint{0.f}; + float _speed_body_y_setpoint{0.f}; + float _pos_ctl_yaw_setpoint{0.f}; // Yaw setpoint for manual position mode, NAN if yaw rate is manually controlled [rad] + float _dt{0.f}; + float _auto_speed{0.f}; + float _auto_yaw{0.f}; + float _waypoint_transition_angle{0.f}; // Angle between the prevWP-currWP and currWP-nextWP line segments [rad] + int _nav_state{0}; + bool _mission_finished{false}; + bool _prev_param_check_passed{true}; + + // Controllers + PID _pid_speed_x; + PID _pid_speed_y; + SlewRate _speed_x_setpoint; + SlewRate _speed_y_setpoint; + + // Class Instances + PurePursuit _posctl_pure_pursuit{this}; // Pure pursuit library + MapProjection _global_ned_proj_ref{}; // Transform global to NED coordinates + + DEFINE_PARAMETERS( + (ParamFloat) _param_rm_miss_spd_gain, + (ParamFloat) _param_rm_course_ctl_th, + (ParamFloat) _param_ro_max_thr_speed, + (ParamFloat) _param_ro_speed_p, + (ParamFloat) _param_ro_speed_i, + (ParamFloat) _param_ro_yaw_stick_dz, + (ParamFloat) _param_ro_accel_limit, + (ParamFloat) _param_ro_decel_limit, + (ParamFloat) _param_ro_jerk_limit, + (ParamFloat) _param_ro_speed_limit, + (ParamFloat) _param_ro_speed_th, + (ParamFloat) _param_pp_lookahd_max, + (ParamFloat) _param_ro_yaw_rate_limit, + (ParamFloat) _param_nav_acc_rad + + ) +}; diff --git a/src/modules/rover_mecanum/RoverMecanumControl/CMakeLists.txt b/src/modules/rover_mecanum/MecanumRateControl/CMakeLists.txt similarity index 86% rename from src/modules/rover_mecanum/RoverMecanumControl/CMakeLists.txt rename to src/modules/rover_mecanum/MecanumRateControl/CMakeLists.txt index 61b64980f1..5a876fab55 100644 --- a/src/modules/rover_mecanum/RoverMecanumControl/CMakeLists.txt +++ b/src/modules/rover_mecanum/MecanumRateControl/CMakeLists.txt @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (c) 2024 PX4 Development Team. All rights reserved. +# Copyright (c) 2025 PX4 Development Team. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,9 +31,9 @@ # ############################################################################ -px4_add_library(RoverMecanumControl - RoverMecanumControl.cpp +px4_add_library(MecanumRateControl + MecanumRateControl.cpp ) -target_link_libraries(RoverMecanumControl PUBLIC PID) -target_include_directories(RoverMecanumControl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(MecanumRateControl PUBLIC PID) +target_include_directories(MecanumRateControl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.cpp b/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.cpp new file mode 100644 index 0000000000..49f1ae5a8c --- /dev/null +++ b/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "MecanumRateControl.hpp" + +using namespace time_literals; + +MecanumRateControl::MecanumRateControl(ModuleParams *parent) : ModuleParams(parent) +{ + _rover_rate_setpoint_pub.advertise(); + _rover_throttle_setpoint_pub.advertise(); + _rover_steering_setpoint_pub.advertise(); + _rover_rate_status_pub.advertise(); + updateParams(); +} + +void MecanumRateControl::updateParams() +{ + ModuleParams::updateParams(); + _max_yaw_rate = _param_ro_yaw_rate_limit.get() * M_DEG_TO_RAD_F; + _max_yaw_accel = _param_ro_yaw_accel_limit.get() * M_DEG_TO_RAD_F; + _max_yaw_decel = _param_ro_yaw_decel_limit.get() * M_DEG_TO_RAD_F; + _pid_yaw_rate.setGains(_param_ro_yaw_rate_p.get(), _param_ro_yaw_rate_i.get(), 0.f); + _pid_yaw_rate.setIntegralLimit(1.f); + _pid_yaw_rate.setOutputLimit(1.f); + _adjusted_yaw_rate_setpoint.setSlewRate(_max_yaw_accel); +} + +void MecanumRateControl::updateRateControl() +{ + const hrt_abstime timestamp_prev = _timestamp; + _timestamp = hrt_absolute_time(); + _dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f; + + if (_vehicle_control_mode_sub.updated()) { + _vehicle_control_mode_sub.copy(&_vehicle_control_mode); + } + + if (_vehicle_angular_velocity_sub.updated()) { + vehicle_angular_velocity_s vehicle_angular_velocity{}; + _vehicle_angular_velocity_sub.copy(&vehicle_angular_velocity); + _vehicle_yaw_rate = fabsf(vehicle_angular_velocity.xyz[2]) > _param_ro_yaw_rate_th.get() * M_DEG_TO_RAD_F ? + vehicle_angular_velocity.xyz[2] : 0.f; + } + + if (_vehicle_control_mode.flag_control_rates_enabled && _vehicle_control_mode.flag_armed && runSanityChecks()) { + if (_vehicle_control_mode.flag_control_manual_enabled || _vehicle_control_mode.flag_control_offboard_enabled) { + generateRateSetpoint(); + } + + generateSteeringSetpoint(); + + } else { // Reset controller and slew rate when rate control is not active + _pid_yaw_rate.resetIntegral(); + _adjusted_yaw_rate_setpoint.setForcedValue(0.f); + } + + // Publish rate controller status (logging only) + rover_rate_status_s rover_rate_status; + rover_rate_status.timestamp = _timestamp; + rover_rate_status.measured_yaw_rate = _vehicle_yaw_rate; + rover_rate_status.adjusted_yaw_rate_setpoint = _adjusted_yaw_rate_setpoint.getState(); + rover_rate_status.pid_yaw_rate_integral = _pid_yaw_rate.getIntegral(); + _rover_rate_status_pub.publish(rover_rate_status); + +} + +void MecanumRateControl::generateRateSetpoint() +{ + const bool acro_mode_enabled = _vehicle_control_mode.flag_control_manual_enabled + && !_vehicle_control_mode.flag_control_position_enabled && !_vehicle_control_mode.flag_control_attitude_enabled; + + if (acro_mode_enabled && _manual_control_setpoint_sub.updated()) { // Acro Mode + manual_control_setpoint_s manual_control_setpoint{}; + + if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { + rover_throttle_setpoint_s rover_throttle_setpoint{}; + rover_throttle_setpoint.timestamp = _timestamp; + rover_throttle_setpoint.throttle_body_x = manual_control_setpoint.throttle; + rover_throttle_setpoint.throttle_body_y = manual_control_setpoint.roll; + _rover_throttle_setpoint_pub.publish(rover_throttle_setpoint); + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = math::interpolate (manual_control_setpoint.yaw, -1.f, 1.f, + -_max_yaw_rate, _max_yaw_rate); + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + } + + } else if (_vehicle_control_mode.flag_control_offboard_enabled) { // Offboard rate control + trajectory_setpoint_s trajectory_setpoint{}; + _trajectory_setpoint_sub.copy(&trajectory_setpoint); + + if (_offboard_control_mode_sub.updated()) { + _offboard_control_mode_sub.copy(&_offboard_control_mode); + } + + const bool offboard_rate_control = _offboard_control_mode.body_rate && !_offboard_control_mode.attitude; + + if (offboard_rate_control && PX4_ISFINITE(trajectory_setpoint.yawspeed)) { + rover_rate_setpoint_s rover_rate_setpoint{}; + rover_rate_setpoint.timestamp = _timestamp; + rover_rate_setpoint.yaw_rate_setpoint = trajectory_setpoint.yawspeed; + _rover_rate_setpoint_pub.publish(rover_rate_setpoint); + } + } +} + +void MecanumRateControl::generateSteeringSetpoint() +{ + if (_rover_rate_setpoint_sub.updated()) { + _rover_rate_setpoint_sub.copy(&_rover_rate_setpoint); + + } + + float speed_diff_normalized{0.f}; + + if (PX4_ISFINITE(_rover_rate_setpoint.yaw_rate_setpoint) && PX4_ISFINITE(_vehicle_yaw_rate)) { + const float yaw_rate_setpoint = fabsf(_rover_rate_setpoint.yaw_rate_setpoint) > _param_ro_yaw_rate_th.get() * + M_DEG_TO_RAD_F ? + _rover_rate_setpoint.yaw_rate_setpoint : 0.f; + speed_diff_normalized = RoverControl::rateControl(_adjusted_yaw_rate_setpoint, _pid_yaw_rate, + yaw_rate_setpoint, _vehicle_yaw_rate, _param_rm_max_thr_yaw_r.get(), _max_yaw_accel, + _max_yaw_decel, _param_rm_wheel_track.get(), _dt); + } + + rover_steering_setpoint_s rover_steering_setpoint{}; + rover_steering_setpoint.timestamp = _timestamp; + rover_steering_setpoint.normalized_speed_diff = speed_diff_normalized; + _rover_steering_setpoint_pub.publish(rover_steering_setpoint); +} + +bool MecanumRateControl::runSanityChecks() +{ + bool ret = true; + + if (_param_ro_yaw_rate_limit.get() < FLT_EPSILON) { + ret = false; + + if (_prev_param_check_passed) { + events::send(events::ID("mecanum_rate_control_conf_invalid_yaw_rate_lim"), events::Log::Error, + "Invalid configuration of necessary parameter RO_YAW_RATE_LIM", _param_ro_yaw_rate_limit.get()); + } + + } + + if ((_param_rm_wheel_track.get() < FLT_EPSILON || _param_rm_max_thr_yaw_r.get() < FLT_EPSILON) + && _param_ro_yaw_rate_p.get() < FLT_EPSILON) { + ret = false; + + if (_prev_param_check_passed) { + events::send(events::ID("mecanum_rate_control_conf_invalid_rate_control"), events::Log::Error, + "Invalid configuration for rate control: Neither feed forward (RM_MAX_THR_YAW_R) nor feedback (RO_YAW_RATE_P) is setup", + _param_rm_wheel_track.get(), + _param_rm_max_thr_yaw_r.get(), _param_ro_yaw_rate_p.get()); + } + } + + _prev_param_check_passed = ret; + return ret; +} diff --git a/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.hpp b/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.hpp new file mode 100644 index 0000000000..3e61c4cb1b --- /dev/null +++ b/src/modules/rover_mecanum/MecanumRateControl/MecanumRateControl.hpp @@ -0,0 +1,144 @@ +/**************************************************************************** + * + * Copyright (c) 2025 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +// PX4 includes +#include +#include + +// Libraries +#include +#include +#include +#include + +// uORB includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Class for mecanum rate control. + */ +class MecanumRateControl : public ModuleParams +{ +public: + /** + * @brief Constructor for MecanumRateControl. + * @param parent The parent ModuleParams object. + */ + MecanumRateControl(ModuleParams *parent); + ~MecanumRateControl() = default; + + /** + * @brief Update rate controller. + */ + void updateRateControl(); + +protected: + /** + * @brief Update the parameters of the module. + */ + void updateParams() override; + +private: + + /** + * @brief Generate and publish roverRateSetpoint from manualControlSetpoint (Acro Mode) + * or trajectorySetpoint (Offboard rate control). + */ + void generateRateSetpoint(); + + /** + * @brief Generate and publish roverSteeringSetpoint from RoverRateSetpoint. + */ + void generateSteeringSetpoint(); + + /** + * @brief Check if the necessary parameters are set. + * @return True if all checks pass. + */ + bool runSanityChecks(); + + // uORB subscriptions + uORB::Subscription _vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)}; + uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)}; + uORB::Subscription _trajectory_setpoint_sub{ORB_ID(trajectory_setpoint)}; + uORB::Subscription _offboard_control_mode_sub{ORB_ID(offboard_control_mode)}; + uORB::Subscription _rover_rate_setpoint_sub{ORB_ID(rover_rate_setpoint)}; + uORB::Subscription _vehicle_angular_velocity_sub{ORB_ID(vehicle_angular_velocity)}; + uORB::Subscription _actuator_motors_sub{ORB_ID(actuator_motors)}; + vehicle_control_mode_s _vehicle_control_mode{}; + offboard_control_mode_s _offboard_control_mode{}; + rover_rate_setpoint_s _rover_rate_setpoint{}; + + // uORB publications + uORB::Publication _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)}; + uORB::Publication _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)}; + uORB::Publication _rover_steering_setpoint_pub{ORB_ID(rover_steering_setpoint)}; + uORB::Publication _rover_rate_status_pub{ORB_ID(rover_rate_status)}; + + // Variables + hrt_abstime _timestamp{0}; + float _max_yaw_rate{0.f}; + float _max_yaw_accel{0.f}; + float _max_yaw_decel{0.f}; + float _vehicle_yaw_rate{0.f}; + float _dt{0.f}; // Time since last update [s]. + bool _prev_param_check_passed{true}; + + // Controllers + PID _pid_yaw_rate; + SlewRate _adjusted_yaw_rate_setpoint{0.f}; + + DEFINE_PARAMETERS( + (ParamFloat) _param_rm_wheel_track, + (ParamFloat) _param_rm_max_thr_yaw_r, + (ParamFloat) _param_ro_yaw_rate_limit, + (ParamFloat) _param_ro_yaw_rate_th, + (ParamFloat) _param_ro_yaw_rate_p, + (ParamFloat) _param_ro_yaw_rate_i, + (ParamFloat) _param_ro_yaw_accel_limit, + (ParamFloat) _param_ro_yaw_decel_limit + ) +}; diff --git a/src/modules/rover_mecanum/RoverMecanum.cpp b/src/modules/rover_mecanum/RoverMecanum.cpp index b0729acc02..f881fcdc5d 100644 --- a/src/modules/rover_mecanum/RoverMecanum.cpp +++ b/src/modules/rover_mecanum/RoverMecanum.cpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2023-2024 PX4 Development Team. All rights reserved. + * Copyright (c) 2025 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,15 +32,16 @@ ****************************************************************************/ #include "RoverMecanum.hpp" -using namespace matrix; + using namespace time_literals; RoverMecanum::RoverMecanum() : ModuleParams(nullptr), ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::rate_ctrl) { + _rover_throttle_setpoint_pub.advertise(); + _rover_steering_setpoint_pub.advertise(); updateParams(); - _rover_mecanum_setpoint_pub.advertise(); } bool RoverMecanum::init() @@ -53,266 +54,117 @@ void RoverMecanum::updateParams() { ModuleParams::updateParams(); - _max_yaw_rate = _param_rm_max_yaw_rate.get() * M_DEG_TO_RAD_F; + if (_param_ro_accel_limit.get() > FLT_EPSILON && _param_ro_max_thr_speed.get() > FLT_EPSILON) { + _throttle_body_x_setpoint.setSlewRate(_param_ro_accel_limit.get() / _param_ro_max_thr_speed.get()); + _throttle_body_y_setpoint.setSlewRate(_param_ro_accel_limit.get() / _param_ro_max_thr_speed.get()); + } } void RoverMecanum::Run() -{ - if (should_exit()) { - ScheduleClear(); - exit_and_cleanup(); - return; - } - - updateSubscriptions(); - - // Generate and publish attitude and velocity setpoints - hrt_abstime timestamp = hrt_absolute_time(); - - switch (_nav_state) { - case vehicle_status_s::NAVIGATION_STATE_MANUAL: { - manual_control_setpoint_s manual_control_setpoint{}; - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - - if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = NAN; - rover_mecanum_setpoint.forward_speed_setpoint_normalized = manual_control_setpoint.throttle; - rover_mecanum_setpoint.lateral_speed_setpoint = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = manual_control_setpoint.roll; - rover_mecanum_setpoint.yaw_rate_setpoint = NAN; - - if (_max_yaw_rate > FLT_EPSILON && _param_rm_max_thr_yaw_r.get() > FLT_EPSILON) { - const float scaled_yaw_rate_input = math::interpolate(manual_control_setpoint.yaw, - -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); - const float speed_diff = scaled_yaw_rate_input * _param_rm_wheel_track.get() / 2.f; - rover_mecanum_setpoint.speed_diff_setpoint_normalized = math::interpolate(speed_diff, - -_param_rm_max_thr_yaw_r.get(), _param_rm_max_thr_yaw_r.get(), -1.f, 1.f); - - } else { - rover_mecanum_setpoint.speed_diff_setpoint_normalized = manual_control_setpoint.yaw; - - } - - rover_mecanum_setpoint.yaw_setpoint = NAN; - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); - - } - } break; - - case vehicle_status_s::NAVIGATION_STATE_ACRO: { - manual_control_setpoint_s manual_control_setpoint{}; - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - - if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = NAN; - rover_mecanum_setpoint.forward_speed_setpoint_normalized = manual_control_setpoint.throttle; - rover_mecanum_setpoint.lateral_speed_setpoint = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = manual_control_setpoint.roll; - rover_mecanum_setpoint.yaw_rate_setpoint = math::interpolate(manual_control_setpoint.yaw, - -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); - rover_mecanum_setpoint.speed_diff_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_setpoint = NAN; - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); - } - } break; - - case vehicle_status_s::NAVIGATION_STATE_STAB: { - manual_control_setpoint_s manual_control_setpoint{}; - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - - if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = NAN; - rover_mecanum_setpoint.forward_speed_setpoint_normalized = manual_control_setpoint.throttle; - rover_mecanum_setpoint.lateral_speed_setpoint = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = manual_control_setpoint.roll; - rover_mecanum_setpoint.yaw_rate_setpoint = math::interpolate(math::deadzone(manual_control_setpoint.yaw, - STICK_DEADZONE), - -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); - rover_mecanum_setpoint.speed_diff_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_setpoint = NAN; - - if (fabsf(rover_mecanum_setpoint.yaw_rate_setpoint) > FLT_EPSILON - || (fabsf(rover_mecanum_setpoint.forward_speed_setpoint_normalized) < FLT_EPSILON - && fabsf(rover_mecanum_setpoint.lateral_speed_setpoint_normalized) < FLT_EPSILON)) { // Closed loop yaw rate control - _yaw_ctl = false; - - } else { // Closed loop yaw control - - if (!_yaw_ctl) { - _desired_yaw = _vehicle_yaw; - _yaw_ctl = true; - } - - rover_mecanum_setpoint.yaw_setpoint = _desired_yaw; - rover_mecanum_setpoint.yaw_rate_setpoint = NAN; - } - - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); - } - } break; - - case vehicle_status_s::NAVIGATION_STATE_POSCTL: { - manual_control_setpoint_s manual_control_setpoint{}; - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - - if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = math::interpolate(manual_control_setpoint.throttle, - -1.f, 1.f, -_param_rm_max_speed.get(), _param_rm_max_speed.get());; - rover_mecanum_setpoint.forward_speed_setpoint_normalized = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint = math::interpolate(manual_control_setpoint.roll, - -1.f, 1.f, -_param_rm_max_speed.get(), _param_rm_max_speed.get()); - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_rate_setpoint = math::interpolate(math::deadzone(manual_control_setpoint.yaw, - STICK_DEADZONE), - -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); - rover_mecanum_setpoint.speed_diff_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_setpoint = NAN; - - // Reset cruise control if the manual input changes - if (_yaw_ctl && (!(fabsf(manual_control_setpoint.throttle - _prev_throttle) > STICK_DEADZONE) - || !(fabsf(manual_control_setpoint.roll - _prev_roll) > STICK_DEADZONE))) { - _yaw_ctl = false; - _prev_throttle = manual_control_setpoint.throttle; - _prev_roll = manual_control_setpoint.roll; - - } else if (!_yaw_ctl) { - _prev_throttle = manual_control_setpoint.throttle; - _prev_roll = manual_control_setpoint.roll; - } - - - if (fabsf(rover_mecanum_setpoint.yaw_rate_setpoint) > FLT_EPSILON - || (fabsf(rover_mecanum_setpoint.forward_speed_setpoint) < FLT_EPSILON - && fabsf(rover_mecanum_setpoint.lateral_speed_setpoint) < FLT_EPSILON)) { // Closed loop yaw rate control - rover_mecanum_setpoint.yaw_rate_setpoint = math::interpolate(manual_control_setpoint.yaw, - -1.f, 1.f, -_max_yaw_rate, _max_yaw_rate); - rover_mecanum_setpoint.yaw_setpoint = NAN; - _yaw_ctl = false; - - } else { // Cruise control - const Vector3f velocity = Vector3f(rover_mecanum_setpoint.forward_speed_setpoint, - rover_mecanum_setpoint.lateral_speed_setpoint, 0.f); - const float desired_velocity_magnitude = velocity.norm(); - - if (!_yaw_ctl) { - _desired_yaw = _vehicle_yaw; - _yaw_ctl = true; - _pos_ctl_start_position_ned = _curr_pos_ned; - const Vector3f pos_ctl_course_direction_local = _vehicle_attitude_quaternion.rotateVector(velocity.normalized()); - _pos_ctl_course_direction = Vector2f(pos_ctl_course_direction_local(0), pos_ctl_course_direction_local(1)); - - } - - // Construct a 'target waypoint' for course control s.t. it is never within the maximum lookahead of the rover - const float vector_scaling = sqrtf(powf(_param_pp_lookahd_max.get(), - 2) + powf(_posctl_pure_pursuit.getCrosstrackError(), 2)) + _posctl_pure_pursuit.getDistanceOnLineSegment(); - const Vector2f target_waypoint_ned = _pos_ctl_start_position_ned + vector_scaling * _pos_ctl_course_direction; - const float desired_heading = _posctl_pure_pursuit.calcDesiredHeading(target_waypoint_ned, _pos_ctl_start_position_ned, - _curr_pos_ned, desired_velocity_magnitude); - const float heading_error = matrix::wrap_pi(desired_heading - _vehicle_yaw); - const Vector2f desired_velocity = desired_velocity_magnitude * Vector2f(cosf(heading_error), sinf(heading_error)); - rover_mecanum_setpoint.forward_speed_setpoint = desired_velocity(0); - rover_mecanum_setpoint.lateral_speed_setpoint = desired_velocity(1); - rover_mecanum_setpoint.yaw_setpoint = _desired_yaw; - rover_mecanum_setpoint.yaw_rate_setpoint = NAN; - } - - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); - } - } break; - - case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION: - case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL: - _rover_mecanum_guidance.computeGuidance(_vehicle_yaw, _nav_state); - break; - - default: // Unimplemented nav states will stop the rover - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = NAN; - rover_mecanum_setpoint.forward_speed_setpoint_normalized = 0.f; - rover_mecanum_setpoint.lateral_speed_setpoint = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = 0.f; - rover_mecanum_setpoint.yaw_rate_setpoint = NAN; - rover_mecanum_setpoint.speed_diff_setpoint_normalized = 0.f; - rover_mecanum_setpoint.yaw_setpoint = NAN; - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); - break; - } - - if (!_armed) { // Reset when disarmed - _rover_mecanum_control.resetControllers(); - _yaw_ctl = false; - } - - _rover_mecanum_control.computeMotorCommands(_vehicle_yaw, _vehicle_yaw_rate, _vehicle_forward_speed, - _vehicle_lateral_speed); - -} - -void RoverMecanum::updateSubscriptions() { if (_parameter_update_sub.updated()) { - parameter_update_s parameter_update; - _parameter_update_sub.copy(¶meter_update); updateParams(); } - if (_vehicle_status_sub.updated()) { - vehicle_status_s vehicle_status{}; - _vehicle_status_sub.copy(&vehicle_status); + const hrt_abstime timestamp_prev = _timestamp; + _timestamp = hrt_absolute_time(); + _dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f; - if (vehicle_status.nav_state != _nav_state) { - _rover_mecanum_control.resetControllers(); - _yaw_ctl = false; + _mecanum_pos_vel_control.updatePosControl(); + _mecanum_att_control.updateAttControl(); + _mecanum_rate_control.updateRateControl(); - if (vehicle_status.nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION - || vehicle_status.nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) { - _rover_mecanum_guidance.setDesiredYaw(_vehicle_yaw); - } - } - - _nav_state = vehicle_status.nav_state; - _armed = vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED; + if (_vehicle_control_mode_sub.updated()) { + _vehicle_control_mode_sub.copy(&_vehicle_control_mode); } - if (_vehicle_local_position_sub.updated()) { - vehicle_local_position_s vehicle_local_position{}; - _vehicle_local_position_sub.copy(&vehicle_local_position); - _curr_pos_ned = Vector2f(vehicle_local_position.x, vehicle_local_position.y); - Vector3f velocity_in_local_frame(vehicle_local_position.vx, vehicle_local_position.vy, vehicle_local_position.vz); - Vector3f velocity_in_body_frame = _vehicle_attitude_quaternion.rotateVectorInverse(velocity_in_local_frame); - // Apply threshold to the velocity measurement to cut off measurement noise when standing still - _vehicle_forward_speed = fabsf(velocity_in_body_frame(0)) > SPEED_THRESHOLD ? velocity_in_body_frame(0) : 0.f; - _vehicle_lateral_speed = fabsf(velocity_in_body_frame(1)) > SPEED_THRESHOLD ? velocity_in_body_frame(1) : 0.f; + const bool full_manual_mode_enabled = _vehicle_control_mode.flag_control_manual_enabled + && !_vehicle_control_mode.flag_control_position_enabled && !_vehicle_control_mode.flag_control_attitude_enabled + && !_vehicle_control_mode.flag_control_rates_enabled; + + if (full_manual_mode_enabled) { // Manual mode + generateSteeringSetpoint(); } - if (_vehicle_angular_velocity_sub.updated()) { - vehicle_angular_velocity_s vehicle_angular_velocity{}; - _vehicle_angular_velocity_sub.copy(&vehicle_angular_velocity); - - // Apply threshold to the yaw rate measurement if the rover is standing still to avoid stuttering due to closed loop yaw(rate) control - if ((fabsf(_vehicle_forward_speed) > FLT_EPSILON && fabsf(_vehicle_lateral_speed) > FLT_EPSILON) - || fabsf(vehicle_angular_velocity.xyz[2]) > YAW_RATE_THRESHOLD) { - _vehicle_yaw_rate = vehicle_angular_velocity.xyz[2]; - - } else { - _vehicle_yaw_rate = 0.f; - } + if (_vehicle_control_mode.flag_armed) { + generateActuatorSetpoint(); } - if (_vehicle_attitude_sub.updated()) { - vehicle_attitude_s vehicle_attitude{}; - _vehicle_attitude_sub.copy(&vehicle_attitude); - _vehicle_attitude_quaternion = matrix::Quatf(vehicle_attitude.q); - _vehicle_yaw = matrix::Eulerf(_vehicle_attitude_quaternion).psi(); +} + +void RoverMecanum::generateSteeringSetpoint() +{ + manual_control_setpoint_s manual_control_setpoint{}; + + if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) { + rover_steering_setpoint_s rover_steering_setpoint{}; + rover_steering_setpoint.timestamp = _timestamp; + rover_steering_setpoint.normalized_speed_diff = manual_control_setpoint.yaw; + _rover_steering_setpoint_pub.publish(rover_steering_setpoint); + rover_throttle_setpoint_s rover_throttle_setpoint{}; + rover_throttle_setpoint.timestamp = _timestamp; + rover_throttle_setpoint.throttle_body_x = manual_control_setpoint.throttle; + rover_throttle_setpoint.throttle_body_y = manual_control_setpoint.roll; + _rover_throttle_setpoint_pub.publish(rover_throttle_setpoint); + } +} + +void RoverMecanum::generateActuatorSetpoint() +{ + if (_rover_throttle_setpoint_sub.updated()) { + _rover_throttle_setpoint_sub.copy(&_rover_throttle_setpoint); } + if (_actuator_motors_sub.updated()) { + actuator_motors_s actuator_motors{}; + _actuator_motors_sub.copy(&actuator_motors); + _current_throttle_body_x = (actuator_motors.control[0] + actuator_motors.control[1]) / 2.f; + _current_throttle_body_y = (actuator_motors.control[2] - actuator_motors.control[0]) / 2.f; + } + + if (_rover_steering_setpoint_sub.updated()) { + _rover_steering_setpoint_sub.copy(&_rover_steering_setpoint); + } + + const float throttle_body_x = RoverControl::throttleControl(_throttle_body_x_setpoint, + _rover_throttle_setpoint.throttle_body_x, _current_throttle_body_x, _param_ro_accel_limit.get(), + _param_ro_decel_limit.get(), _param_ro_max_thr_speed.get(), _dt); + const float throttle_body_y = RoverControl::throttleControl(_throttle_body_y_setpoint, + _rover_throttle_setpoint.throttle_body_y, _current_throttle_body_y, _param_ro_accel_limit.get(), + _param_ro_decel_limit.get(), _param_ro_max_thr_speed.get(), _dt); + actuator_motors_s actuator_motors{}; + actuator_motors.reversible_flags = _param_r_rev.get(); + computeInverseKinematics(throttle_body_x, throttle_body_y, + _rover_steering_setpoint.normalized_speed_diff).copyTo(actuator_motors.control); + actuator_motors.timestamp = _timestamp; + _actuator_motors_pub.publish(actuator_motors); + + +} + +Vector4f RoverMecanum::computeInverseKinematics(float throttle_body_x, float throttle_body_y, + const float speed_diff_normalized) +{ + const float total_speed = fabsf(throttle_body_x) + fabsf(throttle_body_y) + fabsf(speed_diff_normalized); + + if (total_speed > 1.f) { // Adjust speed setpoints if infeasible + const float theta = atan2f(fabsf(throttle_body_y), fabsf(throttle_body_x)); + const float magnitude = (1.f - fabsf(speed_diff_normalized)) / (sinf(theta) + cosf(theta)); + const float normalization = 1.f / (sqrtf(powf(throttle_body_x, 2.f) + powf(throttle_body_y, 2.f))); + throttle_body_x *= magnitude * normalization; + throttle_body_y *= magnitude * normalization; + + } + + // Calculate motor commands + const float input_data[3] = {throttle_body_x, throttle_body_y, speed_diff_normalized}; + const Matrix input(input_data); + const float m_data[12] = {1.f, -1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, -1.f, 1.f, -1.f, 1.f}; + const Matrix m(m_data); + const Vector4f motor_commands = m * input; + + return motor_commands; } int RoverMecanum::task_spawn(int argc, char *argv[]) @@ -340,7 +192,7 @@ int RoverMecanum::task_spawn(int argc, char *argv[]) int RoverMecanum::custom_command(int argc, char *argv[]) { - return print_usage("unk_timestampn command"); + return print_usage("unknown command"); } int RoverMecanum::print_usage(const char *reason) @@ -352,7 +204,7 @@ int RoverMecanum::print_usage(const char *reason) PRINT_MODULE_DESCRIPTION( R"DESCR_STR( ### Description -Rover Mecanum controller. +Rover mecanum module. )DESCR_STR"); PRINT_MODULE_USAGE_NAME("rover_mecanum", "controller"); diff --git a/src/modules/rover_mecanum/RoverMecanum.hpp b/src/modules/rover_mecanum/RoverMecanum.hpp index 7c57a1138f..1812e1c7c4 100644 --- a/src/modules/rover_mecanum/RoverMecanum.hpp +++ b/src/modules/rover_mecanum/RoverMecanum.hpp @@ -1,6 +1,6 @@ /**************************************************************************** * - * Copyright (c) 2024 PX4 Development Team. All rights reserved. + * Copyright (c) 2025 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,41 +39,34 @@ #include #include #include -#include + +// Libraries +#include +#include // uORB includes -#include #include -#include +#include +#include #include -#include -#include -#include -#include -#include - -// Standard libraries -#include -#include +#include +#include +#include +#include +#include // Local includes -#include "RoverMecanumGuidance/RoverMecanumGuidance.hpp" -#include "RoverMecanumControl/RoverMecanumControl.hpp" - -// Constants -static constexpr float YAW_RATE_THRESHOLD = - 0.02f; // [rad/s] Threshold for the yaw rate measurement to avoid stuttering when the rover is standing still -static constexpr float SPEED_THRESHOLD = - 0.1f; // [m/s] Threshold for the speed measurement to cut off measurement noise when the rover is standing still -static constexpr float STICK_DEADZONE = - 0.1f; // [0, 1] Percentage of stick input range that will be interpreted as zero around the stick centered value - -using namespace time_literals; +#include "MecanumRateControl/MecanumRateControl.hpp" +#include "MecanumAttControl/MecanumAttControl.hpp" +#include "MecanumPosVelControl/MecanumPosVelControl.hpp" class RoverMecanum : public ModuleBase, public ModuleParams, public px4::ScheduledWorkItem { public: + /** + * @brief Constructor for RoverMecanum + */ RoverMecanum(); ~RoverMecanum() override = default; @@ -89,54 +82,69 @@ public: bool init(); protected: + /** + * @brief Update the parameters of the module. + */ void updateParams() override; private: void Run() override; /** - * @brief Update uORB subscriptions. + * @brief Generate and publish roverSteeringSetpoint from manualControlSetpoint (Manual Mode). */ - void updateSubscriptions(); + void generateSteeringSetpoint(); - // uORB Subscriptions - uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)}; + /** + * @brief Generate and publish actuatorMotors setpoints from roverThrottleSetpoint/roverSteeringSetpoint. + */ + void generateActuatorSetpoint(); + + /** + * @brief Compute normalized motor commands based on normalized setpoints. + * @param throttle_body_x Normalized speed in body x direction [-1, 1]. + * @param throttle_body_y Normalized speed in body y direction [-1, 1]. + * @param speed_diff_normalized Speed difference between left and right wheels [-1, 1]. + * @return Motor speeds [-1, 1]. + */ + Vector4f computeInverseKinematics(float throttle_body_x, float throttle_body_y, const float speed_diff_normalized); + + // uORB subscriptions uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)}; - uORB::Subscription _vehicle_angular_velocity_sub{ORB_ID(vehicle_angular_velocity)}; - uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)}; - uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)}; - uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)}; + uORB::Subscription _rover_steering_setpoint_sub{ORB_ID(rover_steering_setpoint)}; + uORB::Subscription _rover_throttle_setpoint_sub{ORB_ID(rover_throttle_setpoint)}; + uORB::Subscription _vehicle_control_mode_sub{ORB_ID(vehicle_control_mode)}; + uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)}; + uORB::Subscription _actuator_motors_sub{ORB_ID(actuator_motors)}; + vehicle_control_mode_s _vehicle_control_mode{}; + rover_steering_setpoint_s _rover_steering_setpoint{}; + rover_throttle_setpoint_s _rover_throttle_setpoint{}; - // uORB Publications - uORB::Publication _rover_mecanum_setpoint_pub{ORB_ID(rover_mecanum_setpoint)}; + // uORB publications + uORB::PublicationMulti _actuator_motors_pub{ORB_ID(actuator_motors)}; + uORB::Publication _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)}; + uORB::Publication _rover_steering_setpoint_pub{ORB_ID(rover_steering_setpoint)}; - // Instances - RoverMecanumGuidance _rover_mecanum_guidance{this}; - RoverMecanumControl _rover_mecanum_control{this}; - PurePursuit _posctl_pure_pursuit{this}; // Pure pursuit library + // Class instances + MecanumRateControl _mecanum_rate_control{this}; + MecanumAttControl _mecanum_att_control{this}; + MecanumPosVelControl _mecanum_pos_vel_control{this}; // Variables - matrix::Quatf _vehicle_attitude_quaternion{}; - float _vehicle_yaw_rate{0.f}; - float _vehicle_forward_speed{0.f}; - float _vehicle_lateral_speed{0.f}; - float _vehicle_yaw{0.f}; - float _max_yaw_rate{0.f}; - int _nav_state{0}; - bool _yaw_ctl{false}; // Indicates if the rover is doing yaw or yaw rate control in position mode - float _desired_yaw{0.f}; // Yaw setpoint for position mode - Vector2f _pos_ctl_start_position_ned{}; - Vector2f _pos_ctl_course_direction{}; - Vector2f _curr_pos_ned{}; - float _prev_throttle{0.f}; - float _prev_roll{0.f}; - bool _armed{false}; + hrt_abstime _timestamp{0}; + float _dt{0.f}; + float _current_throttle_body_x{0.f}; + float _current_throttle_body_y{0.f}; + // Controllers + SlewRate _throttle_body_x_setpoint{0.f}; + SlewRate _throttle_body_y_setpoint{0.f}; + + // Parameters DEFINE_PARAMETERS( - (ParamFloat) _param_rm_max_speed, - (ParamFloat) _param_rm_wheel_track, - (ParamFloat) _param_rm_max_thr_yaw_r, - (ParamFloat) _param_rm_max_yaw_rate, - (ParamFloat) _param_pp_lookahd_max + (ParamInt) _param_r_rev, + (ParamFloat) _param_ro_accel_limit, + (ParamFloat) _param_ro_decel_limit, + (ParamFloat) _param_ro_max_thr_speed ) }; diff --git a/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.cpp b/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.cpp deleted file mode 100644 index c581236784..0000000000 --- a/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.cpp +++ /dev/null @@ -1,323 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2023-2024 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include "RoverMecanumControl.hpp" - -#include - -using namespace matrix; -using namespace time_literals; - -RoverMecanumControl::RoverMecanumControl(ModuleParams *parent) : ModuleParams(parent) -{ - updateParams(); - _rover_mecanum_status_pub.advertise(); -} - -void RoverMecanumControl::updateParams() -{ - ModuleParams::updateParams(); - - _pid_yaw_rate.setGains(_param_rm_yaw_rate_p.get(), _param_rm_yaw_rate_i.get(), 0.f); - _pid_yaw_rate.setIntegralLimit(1.f); - _pid_yaw_rate.setOutputLimit(1.f); - - _pid_forward_throttle.setGains(_param_rm_p_gain_speed.get(), _param_rm_i_gain_speed.get(), 0.f); - _pid_forward_throttle.setIntegralLimit(1.f); - _pid_forward_throttle.setOutputLimit(1.f); - - _pid_lateral_throttle.setGains(_param_rm_p_gain_speed.get(), _param_rm_i_gain_speed.get(), 0.f); - _pid_lateral_throttle.setIntegralLimit(1.f); - _pid_lateral_throttle.setOutputLimit(1.f); - - _max_yaw_rate = _param_rm_max_yaw_rate.get() * M_DEG_TO_RAD_F; - _max_yaw_accel = _param_rm_max_yaw_accel.get() * M_DEG_TO_RAD_F; - _pid_yaw.setGains(_param_rm_p_gain_yaw.get(), _param_rm_i_gain_yaw.get(), 0.f); - _pid_yaw.setIntegralLimit(_max_yaw_rate); - _pid_yaw.setOutputLimit(_max_yaw_rate); - - // Update slew rates - if (_max_yaw_rate > FLT_EPSILON) { - _yaw_setpoint_with_yaw_rate_limit.setSlewRate(_max_yaw_rate); - } - - if (_max_yaw_accel > FLT_EPSILON) { - _yaw_rate_with_accel_limit.setSlewRate(_max_yaw_accel); - } -} - -void RoverMecanumControl::computeMotorCommands(const float vehicle_yaw, const float vehicle_yaw_rate, - const float vehicle_forward_speed, const float vehicle_lateral_speed) -{ - // Timestamps - hrt_abstime timestamp_prev = _timestamp; - _timestamp = hrt_absolute_time(); - const float dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f; - - // Update mecanum setpoint - _rover_mecanum_setpoint_sub.update(&_rover_mecanum_setpoint); - - // Closed loop yaw control - if (PX4_ISFINITE(_rover_mecanum_setpoint.yaw_setpoint)) { - _yaw_setpoint_with_yaw_rate_limit.update(matrix::wrap_pi(_rover_mecanum_setpoint.yaw_setpoint), dt); - _rover_mecanum_status.adjusted_yaw_setpoint = matrix::wrap_pi(_yaw_setpoint_with_yaw_rate_limit.getState()); - _pid_yaw.setSetpoint( - matrix::wrap_pi(_yaw_setpoint_with_yaw_rate_limit.getState() - - vehicle_yaw)); // error as setpoint to take care of wrapping - _rover_mecanum_setpoint.yaw_rate_setpoint = _pid_yaw.update(0.f, dt); - _rover_mecanum_status.clyaw_yaw_rate_setpoint = _rover_mecanum_setpoint.yaw_rate_setpoint; - - } else { - _pid_yaw.resetIntegral(); - _yaw_setpoint_with_yaw_rate_limit.setForcedValue(vehicle_yaw); - } - - // Yaw rate control - float speed_diff_normalized{0.f}; - - if (PX4_ISFINITE(_rover_mecanum_setpoint.yaw_rate_setpoint)) { // Closed loop yaw rate control - speed_diff_normalized = calcNormalizedSpeedDiff(_rover_mecanum_setpoint.yaw_rate_setpoint, vehicle_yaw_rate, - _param_rm_max_thr_yaw_r.get(), _max_yaw_accel, _param_rm_wheel_track.get(), dt, _yaw_rate_with_accel_limit, - _pid_yaw_rate, false); - _rover_mecanum_status.adjusted_yaw_rate_setpoint = _yaw_rate_with_accel_limit.getState(); - - } else { // Use normalized setpoint - speed_diff_normalized = calcNormalizedSpeedDiff(_rover_mecanum_setpoint.speed_diff_setpoint_normalized, - vehicle_yaw_rate, - _param_rm_max_thr_yaw_r.get(), _max_yaw_accel, _param_rm_wheel_track.get(), dt, _yaw_rate_with_accel_limit, - _pid_yaw_rate, true); - } - - // Speed control - float forward_speed_normalized{0.f}; - float lateral_speed_normalized{0.f}; - - if (PX4_ISFINITE(_rover_mecanum_setpoint.forward_speed_setpoint) - && PX4_ISFINITE(_rover_mecanum_setpoint.lateral_speed_setpoint)) { // Closed loop speed control - - if (_param_rm_max_thr_spd.get() > FLT_EPSILON) { - - forward_speed_normalized = math::interpolate(_rover_mecanum_setpoint.forward_speed_setpoint, - -_param_rm_max_thr_spd.get(), _param_rm_max_thr_spd.get(), -1.f, 1.f); - - lateral_speed_normalized = math::interpolate(_rover_mecanum_setpoint.lateral_speed_setpoint, - -_param_rm_max_thr_spd.get(), _param_rm_max_thr_spd.get(), -1.f, 1.f); - - const float total_speed = fabsf(forward_speed_normalized) + fabsf(lateral_speed_normalized) + fabsf( - speed_diff_normalized); - - if (total_speed > 1.f) { // Adjust speed setpoints if infeasible - const float theta = atan2f(fabsf(lateral_speed_normalized), fabsf(forward_speed_normalized)); - const float magnitude = (1.f - fabsf(speed_diff_normalized)) / (sinf(theta) + cosf(theta)); - const float normalization = 1.f / (sqrtf(powf(forward_speed_normalized, 2.f) + powf(lateral_speed_normalized, 2.f))); - forward_speed_normalized *= magnitude * normalization; - lateral_speed_normalized *= magnitude * normalization; - _rover_mecanum_setpoint.forward_speed_setpoint = math::interpolate(forward_speed_normalized, -1.f, 1.f, - -_param_rm_max_thr_spd.get(), _param_rm_max_thr_spd.get()); - _rover_mecanum_setpoint.lateral_speed_setpoint = math::interpolate(lateral_speed_normalized, -1.f, 1.f, - -_param_rm_max_thr_spd.get(), _param_rm_max_thr_spd.get()); - } - } - - forward_speed_normalized = calcNormalizedSpeedSetpoint(_rover_mecanum_setpoint.forward_speed_setpoint, - vehicle_forward_speed, _param_rm_max_thr_spd.get(), _forward_speed_setpoint_with_accel_limit, _pid_forward_throttle, - _param_rm_max_accel.get(), _param_rm_max_decel.get(), dt, false); - lateral_speed_normalized = calcNormalizedSpeedSetpoint(_rover_mecanum_setpoint.lateral_speed_setpoint, - vehicle_lateral_speed, _param_rm_max_thr_spd.get(), _lateral_speed_setpoint_with_accel_limit, _pid_lateral_throttle, - _param_rm_max_accel.get(), _param_rm_max_decel.get(), dt, false); - _rover_mecanum_status.adjusted_forward_speed_setpoint = _forward_speed_setpoint_with_accel_limit.getState(); - _rover_mecanum_status.adjusted_lateral_speed_setpoint = _lateral_speed_setpoint_with_accel_limit.getState(); - - - } else if (PX4_ISFINITE(_rover_mecanum_setpoint.forward_speed_setpoint_normalized) - && PX4_ISFINITE(_rover_mecanum_setpoint.lateral_speed_setpoint_normalized)) { // Use normalized setpoint - forward_speed_normalized = calcNormalizedSpeedSetpoint(_rover_mecanum_setpoint.forward_speed_setpoint_normalized, - vehicle_forward_speed, _param_rm_max_thr_spd.get(), _forward_speed_setpoint_with_accel_limit, _pid_forward_throttle, - _param_rm_max_accel.get(), _param_rm_max_decel.get(), dt, true); - lateral_speed_normalized = calcNormalizedSpeedSetpoint(_rover_mecanum_setpoint.lateral_speed_setpoint_normalized, - vehicle_lateral_speed, _param_rm_max_thr_spd.get(), _lateral_speed_setpoint_with_accel_limit, _pid_lateral_throttle, - _param_rm_max_accel.get(), _param_rm_max_decel.get(), dt, true); - - } - - // Publish rover mecanum status (logging) - _rover_mecanum_status.timestamp = _timestamp; - _rover_mecanum_status.measured_forward_speed = vehicle_forward_speed; - _rover_mecanum_status.measured_lateral_speed = vehicle_lateral_speed; - _rover_mecanum_status.measured_yaw_rate = vehicle_yaw_rate; - _rover_mecanum_status.measured_yaw = vehicle_yaw; - _rover_mecanum_status.pid_yaw_rate_integral = _pid_yaw_rate.getIntegral(); - _rover_mecanum_status.pid_yaw_integral = _pid_yaw.getIntegral(); - _rover_mecanum_status.pid_forward_throttle_integral = _pid_forward_throttle.getIntegral(); - _rover_mecanum_status.pid_lateral_throttle_integral = _pid_lateral_throttle.getIntegral(); - _rover_mecanum_status_pub.publish(_rover_mecanum_status); - - // Publish to motors - actuator_motors_s actuator_motors{}; - actuator_motors.reversible_flags = _param_r_rev.get(); - computeInverseKinematics(forward_speed_normalized, lateral_speed_normalized, - speed_diff_normalized).copyTo(actuator_motors.control); - actuator_motors.timestamp = _timestamp; - _actuator_motors_pub.publish(actuator_motors); - -} - -float RoverMecanumControl::calcNormalizedSpeedDiff(const float yaw_rate_setpoint, const float vehicle_yaw_rate, - const float max_thr_yaw_r, - const float max_yaw_accel, const float wheel_track, const float dt, SlewRate &yaw_rate_with_accel_limit, - PID &pid_yaw_rate, const bool normalized) -{ - float slew_rate_normalization{1.f}; - - if (normalized) { // Slew rate needs to be normalized if the setpoint is normalized - slew_rate_normalization = max_thr_yaw_r > FLT_EPSILON ? max_thr_yaw_r : 0.f; - } - - if (max_yaw_accel > FLT_EPSILON && slew_rate_normalization > FLT_EPSILON) { - yaw_rate_with_accel_limit.setSlewRate(max_yaw_accel / slew_rate_normalization); - yaw_rate_with_accel_limit.update(yaw_rate_setpoint, dt); - - } else { - yaw_rate_with_accel_limit.setForcedValue(yaw_rate_setpoint); - } - - // Transform yaw rate into speed difference - float speed_diff_normalized{0.f}; - - if (normalized) { - speed_diff_normalized = yaw_rate_with_accel_limit.getState(); - - } else { - if (wheel_track > FLT_EPSILON && max_thr_yaw_r > FLT_EPSILON) { // Feedforward - const float speed_diff = yaw_rate_with_accel_limit.getState() * wheel_track / - 2.f; - speed_diff_normalized = math::interpolate(speed_diff, -max_thr_yaw_r, - max_thr_yaw_r, -1.f, 1.f); - } - - _pid_yaw_rate.setSetpoint(yaw_rate_with_accel_limit.getState()); - speed_diff_normalized = math::constrain(speed_diff_normalized + - _pid_yaw_rate.update(vehicle_yaw_rate, dt), - -1.f, 1.f); // Feedback - - - } - - return math::constrain(speed_diff_normalized, -1.f, 1.f); - -} - -float RoverMecanumControl::calcNormalizedSpeedSetpoint(const float speed_setpoint, - const float vehicle_speed, const float max_thr_spd, SlewRate &speed_setpoint_with_accel_limit, - PID &pid_throttle, const float max_accel, const float max_decel, const float dt, const bool normalized) -{ - float slew_rate_normalization{1.f}; - - if (normalized) { // Slew rate needs to be normalized if the setpoint is normalized - slew_rate_normalization = max_thr_spd > FLT_EPSILON ? max_thr_spd : 0.f; - } - - // Apply acceleration and deceleration limit - if (fabsf(speed_setpoint) >= fabsf(speed_setpoint_with_accel_limit.getState())) { - if (max_accel > FLT_EPSILON && slew_rate_normalization > FLT_EPSILON) { - speed_setpoint_with_accel_limit.setSlewRate(max_accel / slew_rate_normalization); - speed_setpoint_with_accel_limit.update(speed_setpoint, dt); - - } else { - speed_setpoint_with_accel_limit.setForcedValue(speed_setpoint); - - } - - } else if (max_decel > FLT_EPSILON && slew_rate_normalization > FLT_EPSILON) { - speed_setpoint_with_accel_limit.setSlewRate(max_decel / slew_rate_normalization); - speed_setpoint_with_accel_limit.update(speed_setpoint, dt); - - } else { - speed_setpoint_with_accel_limit.setForcedValue(speed_setpoint); - } - - // Calculate normalized forward speed setpoint - float forward_speed_normalized{0.f}; - - if (normalized) { - forward_speed_normalized = speed_setpoint_with_accel_limit.getState(); - - } else { // Closed loop speed control - - if (_param_rm_max_thr_spd.get() > FLT_EPSILON) { // Feedforward - forward_speed_normalized = math::interpolate(speed_setpoint_with_accel_limit.getState(), - -max_thr_spd, max_thr_spd, - -1.f, 1.f); - } - - pid_throttle.setSetpoint(speed_setpoint_with_accel_limit.getState()); - forward_speed_normalized += pid_throttle.update(vehicle_speed, dt); // Feedback - - } - - return math::constrain(forward_speed_normalized, -1.f, 1.f); - -} - -matrix::Vector4f RoverMecanumControl::computeInverseKinematics(float forward_speed_normalized, - float lateral_speed_normalized, - float speed_diff) -{ - const float total_speed = fabsf(forward_speed_normalized) + fabsf(lateral_speed_normalized) + fabsf(speed_diff); - - if (total_speed > 1.f) { // Adjust speed setpoints if infeasible - const float theta = atan2f(fabsf(lateral_speed_normalized), fabsf(forward_speed_normalized)); - const float magnitude = (1.f - fabsf(speed_diff)) / (sinf(theta) + cosf(theta)); - const float normalization = 1.f / (sqrtf(powf(forward_speed_normalized, 2.f) + powf(lateral_speed_normalized, 2.f))); - forward_speed_normalized *= magnitude * normalization; - lateral_speed_normalized *= magnitude * normalization; - - } - - // Calculate motor commands - const float input_data[3] = {forward_speed_normalized, lateral_speed_normalized, speed_diff}; - const Matrix input(input_data); - const float m_data[12] = {1.f, -1.f, -1.f, 1.f, 1.f, 1.f, 1.f, 1.f, -1.f, 1.f, -1.f, 1.f}; - const Matrix m(m_data); - const Vector4f motor_commands = m * input; - - return motor_commands; -} - -void RoverMecanumControl::resetControllers() -{ - _pid_forward_throttle.resetIntegral(); - _pid_lateral_throttle.resetIntegral(); - _pid_yaw_rate.resetIntegral(); - _pid_yaw.resetIntegral(); -} diff --git a/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.hpp b/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.hpp deleted file mode 100644 index 53ae266383..0000000000 --- a/src/modules/rover_mecanum/RoverMecanumControl/RoverMecanumControl.hpp +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2024 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#pragma once - -// PX4 includes -#include - -// uORB includes -#include -#include -#include -#include -#include -#include -#include -#include - -// Standard libraries -#include -#include -#include -#include -#include - -using namespace matrix; - -/** - * @brief Class for mecanum rover guidance. - */ -class RoverMecanumControl : public ModuleParams -{ -public: - /** - * @brief Constructor for RoverMecanumControl. - * @param parent The parent ModuleParams object. - */ - RoverMecanumControl(ModuleParams *parent); - ~RoverMecanumControl() = default; - - /** - * @brief Compute motor commands based on setpoints - * @param vehicle_yaw Yaw of the vehicle [rad] - * @param vehicle_yaw_rate Yaw rate of the vehicle [rad/s] - * @param vehicle_forward_speed Forward speed of the vehicle [m/s] - * @param vehicle_lateral_speed Lateral speed of the vehicle [m/s] - */ - void computeMotorCommands(float vehicle_yaw, float vehicle_yaw_rate, float vehicle_forward_speed, - float vehicle_lateral_speed); - - /** - * @brief Reset PID controllers - */ - void resetControllers(); - -protected: - /** - * @brief Update the parameters of the module. - */ - void updateParams() override; - -private: - /** - * @brief Compute normalized speed diff setpoint between the left and right wheels and apply slew rates. - * @param yaw_rate_setpoint Yaw rate setpoint [rad/s or normalized [-1, 1]]. - * @param vehicle_yaw_rate Measured yaw rate [rad/s]. - * @param max_thr_yaw_r Yaw rate turning left/right wheels at max speed in opposite directions [m/s]. - * @param max_yaw_accel Maximum allowed yaw acceleration for the rover [rad/s^2]. - * @param wheel_track Wheel track [m]. - * @param dt Time since last update [s]. - * @param yaw_rate_with_accel_limit Yaw rate slew rate. - * @param pid_yaw_rate Yaw rate PID - * @param normalized Indicates if the forward speed setpoint is already normalized. - * @return Normalized speed differece setpoint with applied slew rates [-1, 1]. - */ - float calcNormalizedSpeedDiff(float yaw_rate_setpoint, float vehicle_yaw_rate, float max_thr_yaw_r, float max_yaw_accel, - float wheel_track, float dt, SlewRate &yaw_rate_with_accel_limit, PID &pid_yaw_rate, bool normalized); - /** - * @brief Compute normalized speed setpoint and apply slew rates. - * to the speed setpoint and doing closed loop speed control if enabled. - * @param speed_setpoint Speed setpoint [m/s]. - * @param vehicle_speed Actual speed [m/s]. - * @param max_thr_spd Speed the rover drives at maximum throttle [m/s]. - * @param speed_setpoint_with_accel_limit Speed slew rate. - * @param pid_throttle Throttle PID - * @param max_accel Maximum acceleration [m/s^2] - * @param max_decel Maximum deceleration [m/s^2] - * @param dt Time since last update [s]. - * @param normalized Indicates if the speed setpoint is already normalized. - * @return Normalized speed setpoint with applied slew rates [-1, 1]. - */ - float calcNormalizedSpeedSetpoint(float speed_setpoint, float vehicle_forward_speed, float max_thr_spd, - SlewRate &speed_setpoint_with_accel_limit, PID &pid_throttle, float max_accel, float max_decel, - float dt, bool normalized); - - /** - * @brief Turn normalized speed setpoints into normalized motor commands. - * - * @param forward_speed Normalized linear speed in body forward direction [-1, 1]. - * @param lateral_speed Normalized linear speed in body lateral direction [-1, 1]. - * @param speed_diff Normalized speed difference between left and right wheels [-1, 1]. - * @return matrix::Vector4f: Normalized motor inputs [-1, 1] - */ - matrix::Vector4f computeInverseKinematics(float forward_speed, float lateral_speed, float speed_diff); - - // uORB subscriptions - uORB::Subscription _rover_mecanum_setpoint_sub{ORB_ID(rover_mecanum_setpoint)}; - - // uORB publications - uORB::PublicationMulti _actuator_motors_pub{ORB_ID(actuator_motors)}; - uORB::Publication _rover_mecanum_status_pub{ORB_ID(rover_mecanum_status)}; - rover_mecanum_status_s _rover_mecanum_status{}; - - // Variables - rover_mecanum_setpoint_s _rover_mecanum_setpoint{}; - hrt_abstime _timestamp{0}; - float _max_yaw_rate{0.f}; - float _max_yaw_accel{0.f}; - - // Controllers - PID _pid_forward_throttle; // PID for the closed loop forward speed control - PID _pid_lateral_throttle; // PID for the closed loop lateral speed control - PID _pid_yaw; // PID for the closed loop yaw control - PID _pid_yaw_rate; // PID for the closed loop yaw rate control - SlewRate _forward_speed_setpoint_with_accel_limit{0.f}; - SlewRate _lateral_speed_setpoint_with_accel_limit{0.f}; - SlewRate _yaw_rate_with_accel_limit{0.f}; - SlewRateYaw _yaw_setpoint_with_yaw_rate_limit; - - // Parameters - DEFINE_PARAMETERS( - (ParamFloat) _param_rm_wheel_track, - (ParamFloat) _param_rm_max_thr_spd, - (ParamFloat) _param_rm_max_accel, - (ParamFloat) _param_rm_max_decel, - (ParamFloat) _param_rm_max_thr_yaw_r, - (ParamFloat) _param_rm_max_yaw_rate, - (ParamFloat) _param_rm_max_yaw_accel, - (ParamFloat) _param_rm_yaw_rate_p, - (ParamFloat) _param_rm_yaw_rate_i, - (ParamFloat) _param_rm_p_gain_speed, - (ParamFloat) _param_rm_i_gain_speed, - (ParamFloat) _param_rm_p_gain_yaw, - (ParamFloat) _param_rm_i_gain_yaw, - (ParamInt) _param_r_rev - ) -}; diff --git a/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.cpp b/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.cpp deleted file mode 100644 index b72478d746..0000000000 --- a/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2023-2024 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#include "RoverMecanumGuidance.hpp" - -#include - -using namespace matrix; -using namespace time_literals; - -RoverMecanumGuidance::RoverMecanumGuidance(ModuleParams *parent) : ModuleParams(parent) -{ - updateParams(); - _max_velocity_magnitude = _param_rm_max_speed.get(); - _rover_mecanum_guidance_status_pub.advertise(); -} - -void RoverMecanumGuidance::updateParams() -{ - ModuleParams::updateParams(); -} - -void RoverMecanumGuidance::computeGuidance(const float yaw, const int nav_state) -{ - updateSubscriptions(); - - // Calculate desired velocity magnitude - float desired_velocity_magnitude{_max_velocity_magnitude}; - const float distance_to_curr_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1), - _curr_wp(0), _curr_wp(1)); - - if (_param_rm_max_jerk.get() > FLT_EPSILON && _param_rm_max_accel.get() > FLT_EPSILON - && _param_rm_miss_vel_gain.get() > FLT_EPSILON) { - const float speed_reduction = math::constrain(_param_rm_miss_vel_gain.get() * math::interpolate( - M_PI_F - _waypoint_transition_angle, 0.f, - M_PI_F, 0.f, 1.f), 0.f, 1.f); - desired_velocity_magnitude = math::trajectory::computeMaxSpeedFromDistance(_param_rm_max_jerk.get(), - _param_rm_max_accel.get(), distance_to_curr_wp, _max_velocity_magnitude * (1.f - speed_reduction)); - desired_velocity_magnitude = math::constrain(desired_velocity_magnitude, -_max_velocity_magnitude, - _max_velocity_magnitude); - } - - - // Calculate heading error - const float desired_heading = _pure_pursuit.calcDesiredHeading(_curr_wp_ned, _prev_wp_ned, _curr_pos_ned, - desired_velocity_magnitude); - const float heading_error = matrix::wrap_pi(desired_heading - yaw); - - // Catch return to launch - const float distance_to_next_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1), - _next_wp(0), _next_wp(1)); - - if (nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) { - _mission_finished = distance_to_next_wp < _param_nav_acc_rad.get(); - } - - // Guidance logic - Vector2f desired_velocity(0.f, 0.f); - - if (!_mission_finished && distance_to_curr_wp > _param_nav_acc_rad.get()) { - desired_velocity = desired_velocity_magnitude * Vector2f(cosf(heading_error), sinf(heading_error)); - } - - // Timestamp - hrt_abstime timestamp = hrt_absolute_time(); - - // Publish mecanum controller status (logging) - rover_mecanum_guidance_status_s rover_mecanum_guidance_status{}; - rover_mecanum_guidance_status.timestamp = timestamp; - rover_mecanum_guidance_status.lookahead_distance = _pure_pursuit.getLookaheadDistance(); - rover_mecanum_guidance_status.heading_error = heading_error; - rover_mecanum_guidance_status.desired_speed = desired_velocity_magnitude; - _rover_mecanum_guidance_status_pub.publish(rover_mecanum_guidance_status); - - // Publish setpoints - rover_mecanum_setpoint_s rover_mecanum_setpoint{}; - rover_mecanum_setpoint.timestamp = timestamp; - rover_mecanum_setpoint.forward_speed_setpoint = desired_velocity(0); - rover_mecanum_setpoint.forward_speed_setpoint_normalized = NAN; - rover_mecanum_setpoint.lateral_speed_setpoint = desired_velocity(1); - rover_mecanum_setpoint.lateral_speed_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_rate_setpoint = NAN; - rover_mecanum_setpoint.speed_diff_setpoint_normalized = NAN; - rover_mecanum_setpoint.yaw_setpoint = _desired_yaw; - _rover_mecanum_setpoint_pub.publish(rover_mecanum_setpoint); -} - -void RoverMecanumGuidance::updateSubscriptions() -{ - if (_vehicle_global_position_sub.updated()) { - vehicle_global_position_s vehicle_global_position{}; - _vehicle_global_position_sub.copy(&vehicle_global_position); - _curr_pos = Vector2d(vehicle_global_position.lat, vehicle_global_position.lon); - } - - if (_local_position_sub.updated()) { - vehicle_local_position_s local_position{}; - _local_position_sub.copy(&local_position); - - if (!_global_ned_proj_ref.isInitialized() - || (_global_ned_proj_ref.getProjectionReferenceTimestamp() != local_position.ref_timestamp)) { - _global_ned_proj_ref.initReference(local_position.ref_lat, local_position.ref_lon, local_position.ref_timestamp); - } - - _curr_pos_ned = Vector2f(local_position.x, local_position.y); - } - - if (_position_setpoint_triplet_sub.updated()) { - updateWaypoints(); - } - - if (_mission_result_sub.updated()) { - mission_result_s mission_result{}; - _mission_result_sub.copy(&mission_result); - _mission_finished = mission_result.finished; - } - - if (_home_position_sub.updated()) { - home_position_s home_position{}; - _home_position_sub.copy(&home_position); - _home_position = Vector2d(home_position.lat, home_position.lon); - } -} - -void RoverMecanumGuidance::updateWaypoints() -{ - position_setpoint_triplet_s position_setpoint_triplet{}; - _position_setpoint_triplet_sub.copy(&position_setpoint_triplet); - - // Global waypoint coordinates - if (position_setpoint_triplet.current.valid && PX4_ISFINITE(position_setpoint_triplet.current.lat) - && PX4_ISFINITE(position_setpoint_triplet.current.lon)) { - _curr_wp = Vector2d(position_setpoint_triplet.current.lat, position_setpoint_triplet.current.lon); - - } else { - _curr_wp = Vector2d(0, 0); - } - - if (position_setpoint_triplet.previous.valid && PX4_ISFINITE(position_setpoint_triplet.previous.lat) - && PX4_ISFINITE(position_setpoint_triplet.previous.lon)) { - _prev_wp = Vector2d(position_setpoint_triplet.previous.lat, position_setpoint_triplet.previous.lon); - - } else { - _prev_wp = _curr_pos; - } - - if (position_setpoint_triplet.next.valid && PX4_ISFINITE(position_setpoint_triplet.next.lat) - && PX4_ISFINITE(position_setpoint_triplet.next.lon)) { - _next_wp = Vector2d(position_setpoint_triplet.next.lat, position_setpoint_triplet.next.lon); - - } else { - _next_wp = _home_position; - } - - // NED waypoint coordinates - _curr_wp_ned = _global_ned_proj_ref.project(_curr_wp(0), _curr_wp(1)); - _prev_wp_ned = _global_ned_proj_ref.project(_prev_wp(0), _prev_wp(1)); - _next_wp_ned = _global_ned_proj_ref.project(_next_wp(0), _next_wp(1)); - - // Distances - const Vector2f curr_to_next_wp_ned = _next_wp_ned - _curr_wp_ned; - const Vector2f curr_to_prev_wp_ned = _prev_wp_ned - _curr_wp_ned; - float cosin = curr_to_prev_wp_ned.unit_or_zero() * curr_to_next_wp_ned.unit_or_zero(); - cosin = math::constrain(cosin, -1.f, 1.f); // Protect against float precision problem - _waypoint_transition_angle = acosf(cosin); - - // Waypoint cruising speed - _max_velocity_magnitude = position_setpoint_triplet.current.cruising_speed > FLT_EPSILON ? math::constrain( - position_setpoint_triplet.current.cruising_speed, 0.f, - _param_rm_max_speed.get()) : _param_rm_max_speed.get(); - - // Waypoint yaw setpoint - if (PX4_ISFINITE(position_setpoint_triplet.current.yaw)) { - _desired_yaw = position_setpoint_triplet.current.yaw; - } -} diff --git a/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.hpp b/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.hpp deleted file mode 100644 index 0970b83646..0000000000 --- a/src/modules/rover_mecanum/RoverMecanumGuidance/RoverMecanumGuidance.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/**************************************************************************** - * - * Copyright (c) 2024 PX4 Development Team. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name PX4 nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#pragma once - -// PX4 includes -#include -#include - -// uORB includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Standard libraries -#include -#include -#include -#include -#include - -using namespace matrix; - -/** - * @brief Class for mecanum rover guidance. - */ -class RoverMecanumGuidance : public ModuleParams -{ -public: - /** - * @brief Constructor for RoverMecanumGuidance. - * @param parent The parent ModuleParams object. - */ - RoverMecanumGuidance(ModuleParams *parent); - ~RoverMecanumGuidance() = default; - - /** - * @brief Compute guidance for the vehicle. - * @param yaw The yaw orientation of the vehicle in radians. - * @param nav_state Navigation state of the rover. - */ - void computeGuidance(float yaw, int nav_state); - - void setDesiredYaw(float desired_yaw) { _desired_yaw = desired_yaw; }; - -protected: - /** - * @brief Update the parameters of the module. - */ - void updateParams() override; - -private: - /** - * @brief Update uORB subscriptions - */ - void updateSubscriptions(); - - /** - * @brief Update global/ned waypoint coordinates - */ - void updateWaypoints(); - - // uORB subscriptions - uORB::Subscription _position_setpoint_triplet_sub{ORB_ID(position_setpoint_triplet)}; - uORB::Subscription _vehicle_global_position_sub{ORB_ID(vehicle_global_position)}; - uORB::Subscription _mission_result_sub{ORB_ID(mission_result)}; - uORB::Subscription _local_position_sub{ORB_ID(vehicle_local_position)}; - uORB::Subscription _home_position_sub{ORB_ID(home_position)}; - - // uORB publications - uORB::Publication _rover_mecanum_guidance_status_pub{ORB_ID(rover_mecanum_guidance_status)}; - uORB::Publication _rover_mecanum_setpoint_pub{ORB_ID(rover_mecanum_setpoint)}; - - // Variables - MapProjection _global_ned_proj_ref{}; // Transform global to ned coordinates. - PurePursuit _pure_pursuit{this}; // Pure pursuit library - bool _mission_finished{false}; - - // Waypoints - Vector2d _curr_pos{}; - Vector2f _curr_pos_ned{}; - Vector2d _prev_wp{}; - Vector2f _prev_wp_ned{}; - Vector2d _curr_wp{}; - Vector2f _curr_wp_ned{}; - Vector2d _next_wp{}; - Vector2f _next_wp_ned{}; - Vector2d _home_position{}; - float _max_velocity_magnitude{0.f}; - float _waypoint_transition_angle{0.f}; // Angle between the prevWP-currWP and currWP-nextWP line segments [rad] - float _desired_yaw{0.f}; - - // Parameters - DEFINE_PARAMETERS( - (ParamFloat) _param_rm_max_speed, - (ParamFloat) _param_nav_acc_rad, - (ParamFloat) _param_rm_max_jerk, - (ParamFloat) _param_rm_max_accel, - (ParamFloat) _param_rm_max_yaw_rate, - (ParamFloat) _param_rm_miss_vel_gain - ) -}; diff --git a/src/modules/rover_mecanum/module.yaml b/src/modules/rover_mecanum/module.yaml index 8d70ec90f8..ff58891ea1 100644 --- a/src/modules/rover_mecanum/module.yaml +++ b/src/modules/rover_mecanum/module.yaml @@ -3,47 +3,17 @@ module_name: Rover Mecanum parameters: - group: Rover Mecanum definitions: - RM_WHEEL_TRACK: - description: - short: Wheel track - long: Distance from the center of the right wheels to the center of the left wheels. - type: float - unit: m - min: 0.001 - max: 100 - increment: 0.001 - decimal: 3 - default: 0.5 - - RM_MAX_YAW_RATE: - description: - short: Maximum allowed yaw rate for the rover. - long: | - This parameter is used to cap desired yaw rates and map controller inputs to desired yaw rates in acro mode. - type: float - unit: deg/s - min: 0.01 - max: 1000 - increment: 0.01 - decimal: 2 - default: 90 - - RM_MAX_YAW_ACCEL: - description: - short: Maximum allowed yaw acceleration for the rover - long: | - This parameter is used to cap desired yaw acceleration. This is used to adjust incoming yaw rate setpoints - to a feasible yaw rate setpoint based on the physical limitation on how fast the yaw rate can change. - This leads to a smooth setpoint trajectory for the closed loop yaw rate controller to track. - Set to -1 to disable. - type: float - unit: deg/s^2 - min: -1 - max: 1000 - increment: 0.01 - decimal: 2 - default: -1 + description: + short: Wheel track + long: Distance from the center of the right wheel to the center of the left wheel. + type: float + unit: m + min: 0 + max: 100 + increment: 0.001 + decimal: 3 + default: 0 RM_MAX_THR_YAW_R: description: @@ -52,7 +22,7 @@ parameters: This parameter is used to calculate the feedforward term of the closed loop yaw rate control. The controller first calculates the required speed difference between the left and right motor to achieve the desired yaw rate. This desired speed difference is then linearly mapped to normalized motor commands. - A good starting point is twice the speed the rover drives at maximum throttle (RM_MAX_THRTL_SPD)). + A good starting point is half the speed the rover drives at maximum throttle (RD_MAX_THR_SPD)). Increase this parameter if the rover turns faster than the setpoint, and decrease if the rover turns slower. type: float unit: m/s @@ -60,149 +30,37 @@ parameters: max: 100 increment: 0.01 decimal: 2 - default: 2 - - RM_YAW_RATE_P: - description: - short: Proportional gain for the closed-loop yaw rate controller. - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 1 - - RM_YAW_RATE_I: - description: - short: Integral gain for the closed-loop yaw rate controller. - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 2 default: 0 - RM_YAW_P: + RM_MISS_SPD_GAIN: description: - short: Proportional gain for closed loop yaw controller - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 1 - - RM_YAW_I: - description: - short: Integral gain for closed loop yaw controller - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 0.1 - - RM_MAX_SPEED: - description: - short: Maximum speed the rover is allowed to drive + short: Tuning parameter for the speed reduction during waypoint transition long: | - This parameter is used cap the maximum speed the rover is allowed to drive - and to map stick inputs to desired speeds in position mode. + The waypoint transition speed is calculated as: + Transition_speed = Maximum_speed * (1 - normalized_transition_angle * RM_MISS_VEL_GAIN) + The normalized transition angle is the angle between the line segment from prev-curr waypoint and + curr-next waypoint interpolated from [0, 180] -> [0, 1]. + Higher value -> More speed reduction during waypoint transitions. + Set to -1 to disable any speed reduction during waypoint transition. type: float - unit: m/s - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 7 - - RM_MAX_THR_SPD: - description: - short: Speed the rover drives at maximum throttle - long: | - This parameter is used to calculate the feedforward term of the closed loop speed control which linearly - maps desired speeds to normalized motor commands [-1. 1]. - A good starting point is the observed ground speed when the rover drives at maximum throttle in manual mode. - Increase this parameter if the rover is faster than the setpoint, and decrease if the rover is slower. - type: float - unit: m/s - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 1 - - RM_SPEED_P: - description: - short: Proportional gain for speed controller - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 3 - default: 1 - - RM_SPEED_I: - description: - short: Integral gain for ground speed controller - type: float - min: 0 - max: 100 - increment: 0.01 - decimal: 3 - default: 0 - - RM_MAX_JERK: - description: - short: Maximum jerk - long: Limit for forwards acc/deceleration change. - type: float - unit: m/s^3 - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 0.5 - - RM_MAX_ACCEL: - description: - short: Maximum acceleration - long: Maximum acceleration is used to limit the acceleration of the rover - type: float - unit: m/s^2 - min: 0 - max: 100 - increment: 0.01 - decimal: 2 - default: 0.5 - - RM_MAX_DECEL: - description: - short: Maximum deceleration - long: | - Maximum decelaration is used to limit the deceleration of the rover. - Set to -1 to disable, causing the rover to decelerate as fast as possible. - Caution: Disabling the deceleration limit also disables the slow down effect in auto modes. - type: float - unit: m/s^2 min: -1 max: 100 increment: 0.01 decimal: 2 default: -1 - RM_MISS_VEL_GAIN: + RM_COURSE_CTL_TH: description: - short: Tuning parameter for the velocity reduction during waypoint transition + short: Threshold to update course control in manual position mode long: | - The waypoint transition speed is calculated as: - Transition_speed = Maximum_speed * (1 - normalized_transition_angle * RM_MISS_VEL_GAIN) - The normalized transition angle is the angle between the line segment from prev-curr WP and curr-next WP - interpolated from [0, 180] -> [0, 1]. - Higher value -> More velocity reduction during cornering. + Threshold for the angle between the active cruise direction and the cruise direction given + by the stick inputs. + This can be understood as a deadzone for the combined stick inputs for forward/backwards + and lateral speed which defines a course direction. type: float - min: 0.05 - max: 100 + unit: rad + min: 0 + max: 3.14 increment: 0.01 decimal: 2 - default: 1 + default: 0.17