mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
mecanum: refactor code architecture
This commit is contained in:
parent
d602b569b1
commit
7c63468e8b
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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})
|
||||
@ -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<float>(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<float>(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;
|
||||
}
|
||||
@ -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 <px4_platform_common/module_params.h>
|
||||
#include <px4_platform_common/events.h>
|
||||
|
||||
// Libraries
|
||||
#include <lib/rover_control/RoverControl.hpp>
|
||||
#include <lib/pid/PID.hpp>
|
||||
#include <lib/slew_rate/SlewRateYaw.hpp>
|
||||
#include <math.h>
|
||||
#include <matrix/matrix/math.hpp>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/rover_rate_setpoint.h>
|
||||
#include <uORB/topics/rover_throttle_setpoint.h>
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/topics/vehicle_attitude.h>
|
||||
#include <uORB/topics/rover_attitude_status.h>
|
||||
#include <uORB/topics/rover_attitude_setpoint.h>
|
||||
#include <uORB/topics/actuator_motors.h>
|
||||
#include <uORB/topics/offboard_control_mode.h>
|
||||
#include <uORB/topics/trajectory_setpoint.h>
|
||||
|
||||
/**
|
||||
* @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_s> _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)};
|
||||
uORB::Publication<rover_throttle_setpoint_s> _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)};
|
||||
uORB::Publication<rover_attitude_setpoint_s> _rover_attitude_setpoint_pub{ORB_ID(rover_attitude_setpoint)};
|
||||
uORB::Publication<rover_attitude_status_s> _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<float> _adjusted_yaw_setpoint;
|
||||
|
||||
// Parameters
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_LIM>) _param_ro_yaw_rate_limit,
|
||||
(ParamFloat<px4::params::RO_YAW_P>) _param_ro_yaw_p,
|
||||
(ParamFloat<px4::params::RO_YAW_STICK_DZ>) _param_ro_yaw_stick_dz
|
||||
)
|
||||
};
|
||||
@ -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})
|
||||
@ -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<float>(_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<float>(_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<float>(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<float>(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<float>(manual_control_setpoint.throttle,
|
||||
-1.f, 1.f, -_param_ro_speed_limit.get(), _param_ro_speed_limit.get());
|
||||
_speed_body_y_setpoint = math::interpolate<float>(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<float>(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<float>(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<float, float>(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;
|
||||
}
|
||||
@ -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 <px4_platform_common/module_params.h>
|
||||
#include <px4_platform_common/events.h>
|
||||
|
||||
// Libraries
|
||||
#include <lib/rover_control/RoverControl.hpp>
|
||||
#include <lib/pid/PID.hpp>
|
||||
#include <matrix/matrix/math.hpp>
|
||||
#include <lib/slew_rate/SlewRate.hpp>
|
||||
#include <lib/pure_pursuit/PurePursuit.hpp>
|
||||
#include <lib/geo/geo.h>
|
||||
#include <math.h>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/rover_rate_setpoint.h>
|
||||
#include <uORB/topics/rover_throttle_setpoint.h>
|
||||
#include <uORB/topics/rover_velocity_status.h>
|
||||
#include <uORB/topics/rover_attitude_setpoint.h>
|
||||
#include <uORB/topics/rover_steering_setpoint.h>
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/topics/trajectory_setpoint.h>
|
||||
#include <uORB/topics/vehicle_attitude.h>
|
||||
#include <uORB/topics/offboard_control_mode.h>
|
||||
#include <uORB/topics/position_setpoint_triplet.h>
|
||||
#include <uORB/topics/vehicle_local_position.h>
|
||||
#include <uORB/topics/vehicle_status.h>
|
||||
#include <uORB/topics/position_controller_status.h>
|
||||
#include <uORB/topics/mission_result.h>
|
||||
#include <uORB/topics/home_position.h>
|
||||
|
||||
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_s> _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)};
|
||||
uORB::Publication<rover_throttle_setpoint_s> _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)};
|
||||
uORB::Publication<rover_attitude_setpoint_s> _rover_attitude_setpoint_pub{ORB_ID(rover_attitude_setpoint)};
|
||||
uORB::Publication<rover_velocity_status_s> _rover_velocity_status_pub{ORB_ID(rover_velocity_status)};
|
||||
uORB::Publication<position_controller_status_s> _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<float> _speed_x_setpoint;
|
||||
SlewRate<float> _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<px4::params::RM_MISS_SPD_GAIN>) _param_rm_miss_spd_gain,
|
||||
(ParamFloat<px4::params::RM_COURSE_CTL_TH>) _param_rm_course_ctl_th,
|
||||
(ParamFloat<px4::params::RO_MAX_THR_SPEED>) _param_ro_max_thr_speed,
|
||||
(ParamFloat<px4::params::RO_SPEED_P>) _param_ro_speed_p,
|
||||
(ParamFloat<px4::params::RO_SPEED_I>) _param_ro_speed_i,
|
||||
(ParamFloat<px4::params::RO_YAW_STICK_DZ>) _param_ro_yaw_stick_dz,
|
||||
(ParamFloat<px4::params::RO_ACCEL_LIM>) _param_ro_accel_limit,
|
||||
(ParamFloat<px4::params::RO_DECEL_LIM>) _param_ro_decel_limit,
|
||||
(ParamFloat<px4::params::RO_JERK_LIM>) _param_ro_jerk_limit,
|
||||
(ParamFloat<px4::params::RO_SPEED_LIM>) _param_ro_speed_limit,
|
||||
(ParamFloat<px4::params::RO_SPEED_TH>) _param_ro_speed_th,
|
||||
(ParamFloat<px4::params::PP_LOOKAHD_MAX>) _param_pp_lookahd_max,
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_LIM>) _param_ro_yaw_rate_limit,
|
||||
(ParamFloat<px4::params::NAV_ACC_RAD>) _param_nav_acc_rad
|
||||
|
||||
)
|
||||
};
|
||||
@ -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})
|
||||
@ -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<float> (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<float>(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<float, float, float>(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;
|
||||
}
|
||||
@ -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 <px4_platform_common/module_params.h>
|
||||
#include <px4_platform_common/events.h>
|
||||
|
||||
// Libraries
|
||||
#include <lib/rover_control/RoverControl.hpp>
|
||||
#include <lib/pid/PID.hpp>
|
||||
#include <lib/slew_rate/SlewRate.hpp>
|
||||
#include <math.h>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/rover_rate_setpoint.h>
|
||||
#include <uORB/topics/rover_throttle_setpoint.h>
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/topics/vehicle_angular_velocity.h>
|
||||
#include <uORB/topics/rover_steering_setpoint.h>
|
||||
#include <uORB/topics/rover_rate_status.h>
|
||||
#include <uORB/topics/actuator_motors.h>
|
||||
#include <uORB/topics/offboard_control_mode.h>
|
||||
#include <uORB/topics/trajectory_setpoint.h>
|
||||
|
||||
/**
|
||||
* @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_s> _rover_rate_setpoint_pub{ORB_ID(rover_rate_setpoint)};
|
||||
uORB::Publication<rover_throttle_setpoint_s> _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)};
|
||||
uORB::Publication<rover_steering_setpoint_s> _rover_steering_setpoint_pub{ORB_ID(rover_steering_setpoint)};
|
||||
uORB::Publication<rover_rate_status_s> _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<float> _adjusted_yaw_rate_setpoint{0.f};
|
||||
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamFloat<px4::params::RM_WHEEL_TRACK>) _param_rm_wheel_track,
|
||||
(ParamFloat<px4::params::RM_MAX_THR_YAW_R>) _param_rm_max_thr_yaw_r,
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_LIM>) _param_ro_yaw_rate_limit,
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_TH>) _param_ro_yaw_rate_th,
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_P>) _param_ro_yaw_rate_p,
|
||||
(ParamFloat<px4::params::RO_YAW_RATE_I>) _param_ro_yaw_rate_i,
|
||||
(ParamFloat<px4::params::RO_YAW_ACCEL_LIM>) _param_ro_yaw_accel_limit,
|
||||
(ParamFloat<px4::params::RO_YAW_DECEL_LIM>) _param_ro_yaw_decel_limit
|
||||
)
|
||||
};
|
||||
@ -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<float>(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<float>(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<float>(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<float>(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<float>(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<float>(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<float>(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<float>(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<float, 3, 1> 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<float, 4, 3> 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");
|
||||
|
||||
@ -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 <px4_platform_common/module.h>
|
||||
#include <px4_platform_common/module_params.h>
|
||||
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
|
||||
#include <lib/pure_pursuit/PurePursuit.hpp>
|
||||
|
||||
// Libraries
|
||||
#include <lib/rover_control/RoverControl.hpp>
|
||||
#include <lib/slew_rate/SlewRate.hpp>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/PublicationMulti.hpp>
|
||||
#include <uORB/topics/parameter_update.h>
|
||||
#include <uORB/topics/vehicle_status.h>
|
||||
#include <uORB/topics/vehicle_attitude.h>
|
||||
#include <uORB/topics/vehicle_angular_velocity.h>
|
||||
#include <uORB/topics/vehicle_local_position.h>
|
||||
#include <uORB/topics/rover_mecanum_setpoint.h>
|
||||
|
||||
// Standard libraries
|
||||
#include <lib/pid/PID.hpp>
|
||||
#include <matrix/matrix/math.hpp>
|
||||
#include <uORB/topics/actuator_motors.h>
|
||||
#include <uORB/topics/rover_steering_setpoint.h>
|
||||
#include <uORB/topics/rover_throttle_setpoint.h>
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
|
||||
// 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<RoverMecanum>, 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_s> _rover_mecanum_setpoint_pub{ORB_ID(rover_mecanum_setpoint)};
|
||||
// uORB publications
|
||||
uORB::PublicationMulti<actuator_motors_s> _actuator_motors_pub{ORB_ID(actuator_motors)};
|
||||
uORB::Publication<rover_throttle_setpoint_s> _rover_throttle_setpoint_pub{ORB_ID(rover_throttle_setpoint)};
|
||||
uORB::Publication<rover_steering_setpoint_s> _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<float> _throttle_body_x_setpoint{0.f};
|
||||
SlewRate<float> _throttle_body_y_setpoint{0.f};
|
||||
|
||||
// Parameters
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamFloat<px4::params::RM_MAX_SPEED>) _param_rm_max_speed,
|
||||
(ParamFloat<px4::params::RM_WHEEL_TRACK>) _param_rm_wheel_track,
|
||||
(ParamFloat<px4::params::RM_MAX_THR_YAW_R>) _param_rm_max_thr_yaw_r,
|
||||
(ParamFloat<px4::params::RM_MAX_YAW_RATE>) _param_rm_max_yaw_rate,
|
||||
(ParamFloat<px4::params::PP_LOOKAHD_MAX>) _param_pp_lookahd_max
|
||||
(ParamInt<px4::params::CA_R_REV>) _param_r_rev,
|
||||
(ParamFloat<px4::params::RO_ACCEL_LIM>) _param_ro_accel_limit,
|
||||
(ParamFloat<px4::params::RO_DECEL_LIM>) _param_ro_decel_limit,
|
||||
(ParamFloat<px4::params::RO_MAX_THR_SPEED>) _param_ro_max_thr_speed
|
||||
)
|
||||
};
|
||||
|
||||
@ -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 <mathlib/math/Limits.hpp>
|
||||
|
||||
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<float>(_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<float>(_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<float>(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<float>(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<float> &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<float>(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<float> &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<float>(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<float, 3, 1> 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<float, 4, 3> 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();
|
||||
}
|
||||
@ -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 <px4_platform_common/module_params.h>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/PublicationMulti.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/rover_mecanum_setpoint.h>
|
||||
#include <uORB/topics/rover_mecanum_status.h>
|
||||
#include <uORB/topics/actuator_motors.h>
|
||||
#include <lib/slew_rate/SlewRate.hpp>
|
||||
#include <lib/slew_rate/SlewRateYaw.hpp>
|
||||
|
||||
// Standard libraries
|
||||
#include <lib/pid/PID.hpp>
|
||||
#include <matrix/matrix/math.hpp>
|
||||
#include <matrix/math.hpp>
|
||||
#include <mathlib/mathlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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<float> &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<float> &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_s> _actuator_motors_pub{ORB_ID(actuator_motors)};
|
||||
uORB::Publication<rover_mecanum_status_s> _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<float> _forward_speed_setpoint_with_accel_limit{0.f};
|
||||
SlewRate<float> _lateral_speed_setpoint_with_accel_limit{0.f};
|
||||
SlewRate<float> _yaw_rate_with_accel_limit{0.f};
|
||||
SlewRateYaw<float> _yaw_setpoint_with_yaw_rate_limit;
|
||||
|
||||
// Parameters
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamFloat<px4::params::RM_WHEEL_TRACK>) _param_rm_wheel_track,
|
||||
(ParamFloat<px4::params::RM_MAX_THR_SPD>) _param_rm_max_thr_spd,
|
||||
(ParamFloat<px4::params::RM_MAX_ACCEL>) _param_rm_max_accel,
|
||||
(ParamFloat<px4::params::RM_MAX_DECEL>) _param_rm_max_decel,
|
||||
(ParamFloat<px4::params::RM_MAX_THR_YAW_R>) _param_rm_max_thr_yaw_r,
|
||||
(ParamFloat<px4::params::RM_MAX_YAW_RATE>) _param_rm_max_yaw_rate,
|
||||
(ParamFloat<px4::params::RM_MAX_YAW_ACCEL>) _param_rm_max_yaw_accel,
|
||||
(ParamFloat<px4::params::RM_YAW_RATE_P>) _param_rm_yaw_rate_p,
|
||||
(ParamFloat<px4::params::RM_YAW_RATE_I>) _param_rm_yaw_rate_i,
|
||||
(ParamFloat<px4::params::RM_SPEED_P>) _param_rm_p_gain_speed,
|
||||
(ParamFloat<px4::params::RM_SPEED_I>) _param_rm_i_gain_speed,
|
||||
(ParamFloat<px4::params::RM_YAW_P>) _param_rm_p_gain_yaw,
|
||||
(ParamFloat<px4::params::RM_YAW_I>) _param_rm_i_gain_yaw,
|
||||
(ParamInt<px4::params::CA_R_REV>) _param_r_rev
|
||||
)
|
||||
};
|
||||
@ -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 <mathlib/math/Limits.hpp>
|
||||
|
||||
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<float>(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;
|
||||
}
|
||||
}
|
||||
@ -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 <px4_platform_common/module_params.h>
|
||||
#include <lib/pure_pursuit/PurePursuit.hpp>
|
||||
|
||||
// uORB includes
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/Subscription.hpp>
|
||||
#include <uORB/topics/position_setpoint_triplet.h>
|
||||
#include <uORB/topics/vehicle_global_position.h>
|
||||
#include <uORB/topics/vehicle_local_position.h>
|
||||
#include <uORB/topics/mission_result.h>
|
||||
#include <uORB/topics/home_position.h>
|
||||
#include <uORB/topics/vehicle_status.h>
|
||||
#include <uORB/topics/rover_mecanum_guidance_status.h>
|
||||
#include <uORB/topics/rover_mecanum_setpoint.h>
|
||||
|
||||
// Standard libraries
|
||||
#include <matrix/matrix/math.hpp>
|
||||
#include <matrix/math.hpp>
|
||||
#include <mathlib/mathlib.h>
|
||||
#include <lib/geo/geo.h>
|
||||
#include <math.h>
|
||||
|
||||
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_s> _rover_mecanum_guidance_status_pub{ORB_ID(rover_mecanum_guidance_status)};
|
||||
uORB::Publication<rover_mecanum_setpoint_s> _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<px4::params::RM_MAX_SPEED>) _param_rm_max_speed,
|
||||
(ParamFloat<px4::params::NAV_ACC_RAD>) _param_nav_acc_rad,
|
||||
(ParamFloat<px4::params::RM_MAX_JERK>) _param_rm_max_jerk,
|
||||
(ParamFloat<px4::params::RM_MAX_ACCEL>) _param_rm_max_accel,
|
||||
(ParamFloat<px4::params::RM_MAX_YAW_RATE>) _param_rm_max_yaw_rate,
|
||||
(ParamFloat<px4::params::RM_MISS_VEL_GAIN>) _param_rm_miss_vel_gain
|
||||
)
|
||||
};
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user