ackermann: restructure module

This commit is contained in:
chfriedrich98
2024-08-30 11:04:17 +02:00
committed by chfriedrich98
parent 79ec39e561
commit 6a7edac10d
16 changed files with 606 additions and 327 deletions
@@ -16,15 +16,17 @@ param set-default NAV_ACC_RAD 0.5
param set-default RA_ACC_RAD_GAIN 2
param set-default RA_ACC_RAD_MAX 3
param set-default RA_MAX_ACCEL 1.5
param set-default RA_MAX_DECEL 10
param set-default RA_MAX_JERK 15
param set-default RA_MAX_SPEED 3
param set-default RA_MAX_SPEED 5
param set-default RA_MAX_THR_SPEED 6
param set-default RA_MAX_STR_ANG 0.5236
param set-default RA_MAX_STR_RATE 360
param set-default RA_MISS_VEL_DEF 3
param set-default RA_MISS_VEL_GAIN 5
param set-default RA_MISS_VEL_MIN 1
param set-default RA_MISS_SPD_DEF 5
param set-default RA_MISS_SPD_GAIN 5
param set-default RA_MISS_SPD_MIN 1
param set-default RA_SPEED_I 0.01
param set-default RA_SPEED_P 2
param set-default RA_SPEED_P 0.1
param set-default RA_WHEEL_BASE 0.321
# Pure Pursuit parameters
+1
View File
@@ -185,6 +185,7 @@ set(msg_files
RegisterExtComponentReply.msg
RegisterExtComponentRequest.msg
RoverAckermannGuidanceStatus.msg
RoverAckermannSetpoint.msg
RoverAckermannStatus.msg
RoverDifferentialGuidanceStatus.msg
RoverDifferentialSetpoint.msg
-2
View File
@@ -1,8 +1,6 @@
uint64 timestamp # time since system start (microseconds)
float32 desired_speed # [m/s] Rover desired ground speed
float32 lookahead_distance # [m] Lookahead distance of pure the pursuit controller
float32 heading_error # [deg] Heading error of the pure pursuit controller
float32 pid_throttle_integral # [-1, 1] Integral of the PID for the normalized throttle to control the rover speed during missions
# TOPICS rover_ackermann_guidance_status
+8
View File
@@ -0,0 +1,8 @@
uint64 timestamp # time since system start (microseconds)
float32 forward_speed_setpoint # [m/s] Desired forward speed for the rover
float32 forward_speed_setpoint_normalized # [-1, 1] Desired normalized forward speed for the rover
float32 steering_setpoint # [rad/s] Desired steering for the rover
float32 steering_setpoint_normalized # [-1, 1] Desired normalized steering for the rover
# TOPICS rover_ackermann_setpoint
+5 -3
View File
@@ -1,7 +1,9 @@
uint64 timestamp # time since system start (microseconds)
float32 throttle_setpoint # [-1, 1] Normalized throttle setpoint
float32 steering_setpoint # [-1, 1] Normalized steering setpoint
float32 actual_speed # [m/s] Rover ground speed
float32 measured_forward_speed # [m/s] Measured speed in body x direction. Forwards: positiv, Backwards: negativ
float32 adjusted_forward_speed_setpoint # [m/s] Speed setpoint after applying slew rate
float32 steering_setpoint_normalized # [-1, 1] Normalized steering setpoint
float32 adjusted_steering_setpoint_normalized # [-1, 1] Normalized steering setpoint after applying slew rate
float32 pid_throttle_integral # Integral of the PID for the closed loop speed controller
# TOPICS rover_ackermann_status
+1
View File
@@ -104,6 +104,7 @@ void LoggedTopics::add_default_topics()
add_optional_topic("px4io_status");
add_topic("radio_status");
add_optional_topic("rover_ackermann_guidance_status", 100);
add_optional_topic("rover_ackermann_setpoint", 100);
add_optional_topic("rover_ackermann_status", 100);
add_optional_topic("rover_differential_guidance_status", 100);
add_optional_topic("rover_differential_setpoint", 100);
@@ -32,6 +32,7 @@
############################################################################
add_subdirectory(RoverAckermannGuidance)
add_subdirectory(RoverAckermannControl)
px4_add_module(
MODULE modules__rover_ackermann
@@ -41,6 +42,7 @@ px4_add_module(
RoverAckermann.hpp
DEPENDS
RoverAckermannGuidance
RoverAckermannControl
px4_work_queue
SlewRate
pure_pursuit
+46 -106
View File
@@ -33,14 +33,11 @@
#include "RoverAckermann.hpp"
using namespace time_literals;
using namespace matrix;
RoverAckermann::RoverAckermann() :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::rate_ctrl)
{
_rover_ackermann_status_pub.advertise();
_rover_ackermann_setpoint_pub.advertise();
updateParams();
}
@@ -53,16 +50,6 @@ bool RoverAckermann::init()
void RoverAckermann::updateParams()
{
ModuleParams::updateParams();
// Update slew rates
if (_param_ra_max_accel.get() > FLT_EPSILON && _param_ra_max_speed.get() > FLT_EPSILON) {
_throttle_with_accel_limit.setSlewRate(_param_ra_max_accel.get() / _param_ra_max_speed.get());
}
if (_param_ra_max_steering_rate.get() > FLT_EPSILON && _param_ra_max_steer_angle.get() > FLT_EPSILON) {
_steering_with_rate_limit.setSlewRate((M_DEG_TO_RAD_F * _param_ra_max_steering_rate.get()) /
_param_ra_max_steer_angle.get());
}
}
void RoverAckermann::Run()
@@ -75,45 +62,47 @@ void RoverAckermann::Run()
updateSubscriptions();
// Timestamps
hrt_abstime timestamp_prev = _timestamp;
_timestamp = hrt_absolute_time();
const float dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f;
// Generate and publish speed and steering setpoints
hrt_abstime timestamp = hrt_absolute_time();
// Generate motor setpoints
if (_armed) {
switch (_nav_state) {
case vehicle_status_s::NAVIGATION_STATE_MANUAL: {
manual_control_setpoint_s manual_control_setpoint{};
switch (_nav_state) {
case vehicle_status_s::NAVIGATION_STATE_MANUAL: {
manual_control_setpoint_s manual_control_setpoint{};
if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) {
_motor_setpoint.steering = manual_control_setpoint.roll;
_motor_setpoint.throttle = manual_control_setpoint.throttle;
}
if (_manual_control_setpoint_sub.update(&manual_control_setpoint)) {
rover_ackermann_setpoint_s rover_ackermann_setpoint{};
rover_ackermann_setpoint.timestamp = timestamp;
rover_ackermann_setpoint.forward_speed_setpoint = NAN;
rover_ackermann_setpoint.forward_speed_setpoint_normalized = manual_control_setpoint.throttle;
rover_ackermann_setpoint.steering_setpoint = NAN;
rover_ackermann_setpoint.steering_setpoint_normalized = manual_control_setpoint.roll;
_rover_ackermann_setpoint_pub.publish(rover_ackermann_setpoint);
}
} break;
} break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
_motor_setpoint = _ackermann_guidance.computeGuidance(_nav_state);
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
_ackermann_guidance.computeGuidance(_vehicle_forward_speed, _vehicle_yaw, _nav_state, _armed);
break;
default: // Unimplemented nav states will stop the rover
_motor_setpoint.steering = 0.f;
_motor_setpoint.throttle = 0.f;
_throttle_with_accel_limit.setForcedValue(0.f);
_steering_with_rate_limit.setForcedValue(0.f);
break;
}
} else { // Reset on disarm
_motor_setpoint.steering = 0.f;
_motor_setpoint.throttle = 0.f;
_throttle_with_accel_limit.setForcedValue(0.f);
_steering_with_rate_limit.setForcedValue(0.f);
default: // Unimplemented nav states will stop the rover
rover_ackermann_setpoint_s rover_ackermann_setpoint{};
rover_ackermann_setpoint.timestamp = timestamp;
rover_ackermann_setpoint.forward_speed_setpoint = NAN;
rover_ackermann_setpoint.forward_speed_setpoint_normalized = 0.f;
rover_ackermann_setpoint.steering_setpoint = NAN;
rover_ackermann_setpoint.steering_setpoint_normalized = 0.f;
_rover_ackermann_setpoint_pub.publish(rover_ackermann_setpoint);
break;
}
publishMotorSetpoints(applySlewRates(_motor_setpoint, dt));
if (!_armed) {
_ackermann_control.resetControllers();
}
_ackermann_control.computeMotorCommands(_vehicle_forward_speed, _vehicle_yaw);
}
void RoverAckermann::updateSubscriptions()
@@ -129,69 +118,20 @@ void RoverAckermann::updateSubscriptions()
_armed = vehicle_status.arming_state == 2;
}
if (_local_position_sub.updated()) {
vehicle_local_position_s local_position{};
_local_position_sub.copy(&local_position);
const Vector3f rover_velocity = {local_position.vx, local_position.vy, local_position.vz};
_actual_speed = rover_velocity.norm();
}
}
motor_setpoint_struct RoverAckermann::applySlewRates(motor_setpoint_struct motor_setpoint, const float dt)
{
// Sanitize actuator commands
if (!PX4_ISFINITE(motor_setpoint.steering)) {
motor_setpoint.steering = 0.f;
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 (!PX4_ISFINITE(motor_setpoint.throttle)) {
motor_setpoint.throttle = 0.f;
if (_vehicle_local_position_sub.updated()) {
vehicle_local_position_s vehicle_local_position{};
_vehicle_local_position_sub.copy(&vehicle_local_position);
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_forward_speed = velocity_in_body_frame(0);
}
// Acceleration slew rate
if (_param_ra_max_accel.get() > FLT_EPSILON && _param_ra_max_speed.get() > FLT_EPSILON
&& fabsf(motor_setpoint.throttle) > fabsf(_throttle_with_accel_limit.getState())) {
_throttle_with_accel_limit.update(motor_setpoint.throttle, dt);
} else {
_throttle_with_accel_limit.setForcedValue(motor_setpoint.throttle);
}
// Steering slew rate
if (_param_ra_max_steering_rate.get() > FLT_EPSILON && _param_ra_max_steer_angle.get() > FLT_EPSILON) {
_steering_with_rate_limit.update(motor_setpoint.steering, dt);
} else {
_steering_with_rate_limit.setForcedValue(motor_setpoint.steering);
}
motor_setpoint_struct motor_setpoint_temp{};
motor_setpoint_temp.steering = math::constrain(_steering_with_rate_limit.getState(), -1.f, 1.f);
motor_setpoint_temp.throttle = math::constrain(_throttle_with_accel_limit.getState(), -1.f, 1.f);
return motor_setpoint_temp;
}
void RoverAckermann::publishMotorSetpoints(motor_setpoint_struct motor_setpoint_with_slew_rates)
{
// Publish rover Ackermann status (logging)
rover_ackermann_status_s rover_ackermann_status{};
rover_ackermann_status.timestamp = _timestamp;
rover_ackermann_status.throttle_setpoint = _motor_setpoint.throttle;
rover_ackermann_status.steering_setpoint = _motor_setpoint.steering;
rover_ackermann_status.actual_speed = _actual_speed;
_rover_ackermann_status_pub.publish(rover_ackermann_status);
// Publish to motor
actuator_motors_s actuator_motors{};
actuator_motors.reversible_flags = _param_r_rev.get();
actuator_motors.control[0] = motor_setpoint_with_slew_rates.throttle;
actuator_motors.timestamp = _timestamp;
_actuator_motors_pub.publish(actuator_motors);
// Publish to servo
actuator_servos_s actuator_servos{};
actuator_servos.control[0] = motor_setpoint_with_slew_rates.steering;
actuator_servos.timestamp = _timestamp;
_actuator_servos_pub.publish(actuator_servos);
}
int RoverAckermann::task_spawn(int argc, char *argv[])
+11 -37
View File
@@ -39,27 +39,25 @@
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <lib/slew_rate/SlewRate.hpp>
// uORB includes
#include <uORB/Publication.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/topics/manual_control_setpoint.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/actuator_motors.h>
#include <uORB/topics/actuator_servos.h>
#include <uORB/topics/rover_ackermann_status.h>
#include <uORB/topics/rover_ackermann_setpoint.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_attitude.h>
// Standard library includes
#include <math.h>
#include <matrix/matrix/math.hpp>
// Local includes
#include "RoverAckermannGuidance/RoverAckermannGuidance.hpp"
using motor_setpoint_struct = RoverAckermannGuidance::motor_setpoint;
#include "RoverAckermannControl/RoverAckermannControl.hpp"
using namespace time_literals;
@@ -96,49 +94,25 @@ private:
*/
void updateSubscriptions();
/**
* @brief Apply slew rates to motor setpoints.
* @param motor_setpoint Normalized steering and throttle setpoints.
* @param dt Time since last update [s].
* @return Motor setpoint with applied slew rates.
*/
motor_setpoint_struct applySlewRates(motor_setpoint_struct motor_setpoint, float dt);
/**
* @brief Publish motor setpoints to ActuatorMotors/ActuatorServos and logging values to RoverAckermannStatus.
* @param motor_setpoint_with_slew_rate Normalized motor_setpoint with applied slew rates.
*/
void publishMotorSetpoints(motor_setpoint_struct motor_setpoint_with_slew_rates);
// uORB subscriptions
uORB::Subscription _manual_control_setpoint_sub{ORB_ID(manual_control_setpoint)};
uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::Subscription _local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
// uORB publications
uORB::PublicationMulti<actuator_motors_s> _actuator_motors_pub{ORB_ID(actuator_motors)};
uORB::Publication<actuator_servos_s> _actuator_servos_pub{ORB_ID(actuator_servos)};
uORB::Publication<rover_ackermann_status_s> _rover_ackermann_status_pub{ORB_ID(rover_ackermann_status)};
uORB::Publication<rover_ackermann_setpoint_s> _rover_ackermann_setpoint_pub{ORB_ID(rover_ackermann_setpoint)};
// Class instances
RoverAckermannGuidance _ackermann_guidance{this};
RoverAckermannControl _ackermann_control{this};
// Variables
matrix::Quatf _vehicle_attitude_quaternion{};
int _nav_state{0};
motor_setpoint_struct _motor_setpoint;
hrt_abstime _timestamp{0};
float _actual_speed{0.f};
SlewRate<float> _steering_with_rate_limit{0.f};
SlewRate<float> _throttle_with_accel_limit{0.f};
float _vehicle_forward_speed{0.f};
float _vehicle_yaw{0.f};
bool _armed{false};
// Parameters
DEFINE_PARAMETERS(
(ParamInt<px4::params::CA_R_REV>) _param_r_rev,
(ParamFloat<px4::params::RA_MAX_STR_ANG>) _param_ra_max_steer_angle,
(ParamFloat<px4::params::RA_MAX_SPEED>) _param_ra_max_speed,
(ParamFloat<px4::params::RA_MAX_ACCEL>) _param_ra_max_accel,
(ParamFloat<px4::params::RA_MAX_STR_RATE>) _param_ra_max_steering_rate
)
};
@@ -0,0 +1,39 @@
############################################################################
#
# 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.
#
############################################################################
px4_add_library(RoverAckermannControl
RoverAckermannControl.cpp
)
target_link_libraries(RoverAckermannControl PUBLIC pid)
target_include_directories(RoverAckermannControl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
@@ -0,0 +1,191 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "RoverAckermannControl.hpp"
#include <mathlib/math/Limits.hpp>
using namespace matrix;
RoverAckermannControl::RoverAckermannControl(ModuleParams *parent) : ModuleParams(parent)
{
updateParams();
_rover_ackermann_status_pub.advertise();
pid_init(&_pid_throttle, PID_MODE_DERIVATIV_NONE, 0.001f);
}
void RoverAckermannControl::updateParams()
{
ModuleParams::updateParams();
pid_set_parameters(&_pid_throttle,
_param_ra_p_speed.get(), // Proportional gain
_param_ra_i_speed.get(), // Integral gain
0, // Derivative gain
1, // Integral limit
1); // Output limit
// Update slew rates
if (_param_ra_max_accel.get() > FLT_EPSILON && _param_ra_max_speed.get() > FLT_EPSILON) {
_forward_speed_setpoint_with_accel_limit.setSlewRate(_param_ra_max_accel.get() / _param_ra_max_speed.get());
}
if (_param_ra_max_steering_rate.get() > FLT_EPSILON && _param_ra_max_steer_angle.get() > FLT_EPSILON) {
_steering_with_rate_limit.setSlewRate((M_DEG_TO_RAD_F * _param_ra_max_steering_rate.get()) /
_param_ra_max_steer_angle.get());
}
}
void RoverAckermannControl::computeMotorCommands(const float vehicle_forward_speed, const float vehicle_yaw)
{
// 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 ackermann setpoint
_rover_ackermann_setpoint_sub.update(&_rover_ackermann_setpoint);
// Speed control
float forward_speed_normalized{0.f};
if (PX4_ISFINITE(_rover_ackermann_setpoint.forward_speed_setpoint)) {
forward_speed_normalized = calcNormalizedSpeedSetpoint(_rover_ackermann_setpoint.forward_speed_setpoint,
vehicle_forward_speed, dt, false);
} else if (PX4_ISFINITE(_rover_ackermann_setpoint.forward_speed_setpoint_normalized)) { // Use normalized setpoint
forward_speed_normalized = calcNormalizedSpeedSetpoint(_rover_ackermann_setpoint.forward_speed_setpoint_normalized,
vehicle_forward_speed, dt, true);
}
// Steering control
float steering_normalized{0.f};
if (PX4_ISFINITE(_rover_ackermann_setpoint.steering_setpoint)) {
steering_normalized = math::interpolate<float>(_rover_ackermann_setpoint.steering_setpoint,
-_param_ra_max_steer_angle.get(),
_param_ra_max_steer_angle.get(), -1.f, 1.f); // Normalize steering setpoint
} else { // Use normalized setpoint
steering_normalized = PX4_ISFINITE(_rover_ackermann_setpoint.steering_setpoint_normalized) ? math::constrain(
_rover_ackermann_setpoint.steering_setpoint_normalized, -1.f, 1.f) : 0.f;
}
if (_param_ra_max_steering_rate.get() > FLT_EPSILON
&& _param_ra_max_steer_angle.get() > FLT_EPSILON) { // Apply slew rate
_steering_with_rate_limit.update(steering_normalized, dt);
} else {
_steering_with_rate_limit.setForcedValue(steering_normalized);
}
// Publish rover Ackermann status (logging)
_rover_ackermann_status.timestamp = _timestamp;
_rover_ackermann_status.measured_forward_speed = vehicle_forward_speed;
_rover_ackermann_status.steering_setpoint_normalized = steering_normalized;
_rover_ackermann_status.adjusted_steering_setpoint_normalized = _steering_with_rate_limit.getState();
_rover_ackermann_status.pid_throttle_integral = _pid_throttle.integral;
_rover_ackermann_status_pub.publish(_rover_ackermann_status);
// Publish to motor
actuator_motors_s actuator_motors{};
actuator_motors.reversible_flags = _param_r_rev.get();
actuator_motors.control[0] = forward_speed_normalized;
actuator_motors.timestamp = _timestamp;
_actuator_motors_pub.publish(actuator_motors);
// Publish to servo
actuator_servos_s actuator_servos{};
actuator_servos.control[0] = _steering_with_rate_limit.getState();
actuator_servos.timestamp = _timestamp;
_actuator_servos_pub.publish(actuator_servos);
}
float RoverAckermannControl::calcNormalizedSpeedSetpoint(const float forward_speed_setpoint,
const float vehicle_forward_speed, 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 = _param_ra_max_thr_speed.get() > FLT_EPSILON ? _param_ra_max_thr_speed.get() : 0.f;
}
// Apply acceleration and deceleration limit
if (fabsf(forward_speed_setpoint) >= fabsf(_forward_speed_setpoint_with_accel_limit.getState())) {
if (_param_ra_max_accel.get() > FLT_EPSILON && slew_rate_normalization > FLT_EPSILON) {
_forward_speed_setpoint_with_accel_limit.setSlewRate(_param_ra_max_accel.get() / slew_rate_normalization);
_forward_speed_setpoint_with_accel_limit.update(forward_speed_setpoint, dt);
} else {
_forward_speed_setpoint_with_accel_limit.setForcedValue(forward_speed_setpoint);
}
} else if (_param_ra_max_decel.get() > FLT_EPSILON && slew_rate_normalization > FLT_EPSILON) {
_forward_speed_setpoint_with_accel_limit.setSlewRate(_param_ra_max_decel.get() / slew_rate_normalization);
_forward_speed_setpoint_with_accel_limit.update(forward_speed_setpoint, dt);
} else {
_forward_speed_setpoint_with_accel_limit.setForcedValue(forward_speed_setpoint);
}
// Calculate normalized forward speed setpoint
float forward_speed_normalized{0.f};
if (normalized) {
forward_speed_normalized = _forward_speed_setpoint_with_accel_limit.getState();
} else { // Closed loop speed control
_rover_ackermann_status.adjusted_forward_speed_setpoint = _forward_speed_setpoint_with_accel_limit.getState();
if (_param_ra_max_thr_speed.get() > FLT_EPSILON) { // Feedforward
forward_speed_normalized = math::interpolate<float>(_forward_speed_setpoint_with_accel_limit.getState(),
-_param_ra_max_thr_speed.get(), _param_ra_max_thr_speed.get(),
-1.f, 1.f);
}
forward_speed_normalized += pid_calculate(&_pid_throttle, _forward_speed_setpoint_with_accel_limit.getState(),
vehicle_forward_speed, 0, dt); // Feedback
}
return math::constrain(forward_speed_normalized, -1.f, 1.f);
}
void RoverAckermannControl::resetControllers()
{
pid_reset_integral(&_pid_throttle);
}
@@ -0,0 +1,130 @@
/****************************************************************************
*
* 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_ackermann_setpoint.h>
#include <uORB/topics/rover_ackermann_status.h>
#include <uORB/topics/actuator_motors.h>
#include <uORB/topics/actuator_servos.h>
// Standard libraries
#include <lib/pid/pid.h>
#include <matrix/matrix/math.hpp>
#include <matrix/math.hpp>
#include <mathlib/mathlib.h>
#include <math.h>
#include <lib/slew_rate/SlewRate.hpp>
using namespace matrix;
using namespace time_literals;
/**
* @brief Class for ackermann rover guidance.
*/
class RoverAckermannControl : public ModuleParams
{
public:
/**
* @brief Constructor for RoverAckermannControl.
* @param parent The parent ModuleParams object.
*/
RoverAckermannControl(ModuleParams *parent);
~RoverAckermannControl() = default;
/**
* @brief Compute motor commands based on setpoints
*/
void computeMotorCommands(float vehicle_forward_speed, float vehicle_yaw);
/**
* @brief Reset PID controllers and slew rates
*/
void resetControllers();
protected:
/**
* @brief Update the parameters of the module.
*/
void updateParams() override;
private:
/**
* @brief Compute normalized forward speed setpoint by applying slew rates
* to the forward speed setpoint and doing closed loop speed control if enabled.
* @param forward_speed_setpoint Forward speed setpoint [m/s].
* @param vehicle_forward_speed Actual forward speed [m/s].
* @param dt Time since last update [s].
* @param normalized Indicates if the forward speed setpoint is already normalized.
* @return Normalized forward speed setpoint with applied slew rates [-1, 1].
*/
float calcNormalizedSpeedSetpoint(float forward_speed_setpoint, float vehicle_forward_speed, float dt, bool normalized);
// uORB subscriptions
uORB::Subscription _rover_ackermann_setpoint_sub{ORB_ID(rover_ackermann_setpoint)};
rover_ackermann_setpoint_s _rover_ackermann_setpoint{};
// uORB publications
uORB::PublicationMulti<actuator_motors_s> _actuator_motors_pub{ORB_ID(actuator_motors)};
uORB::Publication<actuator_servos_s> _actuator_servos_pub{ORB_ID(actuator_servos)};
uORB::Publication<rover_ackermann_status_s> _rover_ackermann_status_pub{ORB_ID(rover_ackermann_status)};
rover_ackermann_status_s _rover_ackermann_status{};
// Variables
hrt_abstime _timestamp{0};
// Controllers
PID_t _pid_throttle; // The PID controller for the closed loop speed control
SlewRate<float> _steering_with_rate_limit{0.f};
SlewRate<float> _forward_speed_setpoint_with_accel_limit{0.f};
// Parameters
DEFINE_PARAMETERS(
(ParamFloat<px4::params::RA_SPEED_P>) _param_ra_p_speed,
(ParamFloat<px4::params::RA_SPEED_I>) _param_ra_i_speed,
(ParamFloat<px4::params::RA_MAX_ACCEL>) _param_ra_max_accel,
(ParamFloat<px4::params::RA_MAX_DECEL>) _param_ra_max_decel,
(ParamFloat<px4::params::RA_MAX_SPEED>) _param_ra_max_speed,
(ParamFloat<px4::params::RA_MAX_THR_SPEED>) _param_ra_max_thr_speed,
(ParamFloat<px4::params::RA_MAX_STR_ANG>) _param_ra_max_steer_angle,
(ParamFloat<px4::params::RA_MAX_STR_RATE>) _param_ra_max_steering_rate,
(ParamInt<px4::params::CA_R_REV>) _param_r_rev
)
};
@@ -35,5 +35,4 @@ px4_add_library(RoverAckermannGuidance
RoverAckermannGuidance.cpp
)
target_link_libraries(RoverAckermannGuidance PUBLIC pid)
target_include_directories(RoverAckermannGuidance PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
@@ -42,35 +42,30 @@ RoverAckermannGuidance::RoverAckermannGuidance(ModuleParams *parent) : ModulePar
{
_rover_ackermann_guidance_status_pub.advertise();
updateParams();
pid_init(&_pid_throttle, PID_MODE_DERIVATIV_NONE, 0.001f);
}
void RoverAckermannGuidance::updateParams()
{
ModuleParams::updateParams();
pid_set_parameters(&_pid_throttle,
_param_ra_p_speed.get(), // Proportional gain
_param_ra_i_speed.get(), // Integral gain
0, // Derivative gain
1, // Integral limit
1); // Output limit
}
RoverAckermannGuidance::motor_setpoint RoverAckermannGuidance::computeGuidance(const int nav_state)
void RoverAckermannGuidance::computeGuidance(const float vehicle_forward_speed,
const float vehicle_yaw, const int nav_state, const bool armed)
{
updateSubscriptions();
// Distances to waypoints
_distance_to_prev_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
_prev_wp(0), _prev_wp(1));
_distance_to_curr_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
_curr_wp(0), _curr_wp(1));
_distance_to_next_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
_next_wp(0), _next_wp(1));
const float distance_to_prev_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
_prev_wp(0), _prev_wp(1));
const float distance_to_curr_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
_curr_wp(0), _curr_wp(1));
// const float distance_to_next_wp = get_distance_to_next_waypoint(_curr_pos(0), _curr_pos(1),
// _next_wp(0), _next_wp(1));
// Catch return to launch
if (nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) {
_mission_finished = _distance_to_next_wp < _acceptance_radius;
_mission_finished = distance_to_curr_wp < _param_nav_acc_rad.get();
}
// Guidance logic
@@ -78,39 +73,35 @@ RoverAckermannGuidance::motor_setpoint RoverAckermannGuidance::computeGuidance(c
_desired_steering = 0.f;
_desired_speed = 0.f;
} else if (_distance_to_curr_wp < _acceptance_radius) { // Catch delay command
} else if (_nav_cmd == 93) { // Catch delay command
_desired_speed = 0.f;
} else { // Regular guidance algorithm
_desired_speed = calcDesiredSpeed(_wp_max_desired_vel, _param_ra_miss_vel_min.get(),
_param_ra_miss_vel_gain.get(), _distance_to_prev_wp, _distance_to_curr_wp, _acceptance_radius,
_prev_acceptance_radius, _param_ra_max_accel.get(), _param_ra_max_jerk.get(), nav_state);
_desired_speed = calcDesiredSpeed(_cruising_speed, _param_ra_miss_spd_min.get(),
_param_ra_miss_spd_gain.get(), distance_to_prev_wp, distance_to_curr_wp, _acceptance_radius,
_prev_acceptance_radius, _param_ra_max_decel.get(), _param_ra_max_jerk.get(), nav_state, _waypoint_transition_angle,
_prev_waypoint_transition_angle, _param_ra_max_speed.get());
_desired_steering = calcDesiredSteering(_pure_pursuit, _curr_wp_ned, _prev_wp_ned, _curr_pos_ned,
_param_ra_wheel_base.get(), _desired_speed, _vehicle_yaw, _param_ra_max_steer_angle.get());
_param_ra_wheel_base.get(), _desired_speed, vehicle_yaw, _param_ra_max_steer_angle.get(), armed);
}
// Calculate throttle setpoint
hrt_abstime timestamp_prev = _timestamp;
_timestamp = hrt_absolute_time();
const float dt = math::constrain(_timestamp - timestamp_prev, 1_ms, 5000_ms) * 1e-6f;
const float throttle = calcThrottleSetpoint(_pid_throttle, _desired_speed, _actual_speed, _param_ra_max_speed.get(),
dt);
// Publish ackermann controller status (logging)
_rover_ackermann_guidance_status.timestamp = _timestamp;
_rover_ackermann_guidance_status.desired_speed = _desired_speed;
_rover_ackermann_guidance_status.pid_throttle_integral = _pid_throttle.integral;
hrt_abstime timestamp = hrt_absolute_time();
_rover_ackermann_guidance_status.timestamp = timestamp;
_rover_ackermann_guidance_status_pub.publish(_rover_ackermann_guidance_status);
// Return motor setpoints
motor_setpoint motor_setpoint_temp;
motor_setpoint_temp.steering = math::interpolate<float>(_desired_steering, -_param_ra_max_steer_angle.get(),
_param_ra_max_steer_angle.get(), -1.f, 1.f); // Normalize steering setpoint
motor_setpoint_temp.throttle = throttle;
return motor_setpoint_temp;
// Publish speed and steering setpoints
rover_ackermann_setpoint_s rover_ackermann_setpoint{};
rover_ackermann_setpoint.timestamp = timestamp;
rover_ackermann_setpoint.forward_speed_setpoint = _desired_speed;
rover_ackermann_setpoint.forward_speed_setpoint_normalized = NAN;
rover_ackermann_setpoint.steering_setpoint = _desired_steering;
rover_ackermann_setpoint.steering_setpoint_normalized = NAN;
_rover_ackermann_setpoint_pub.publish(rover_ackermann_setpoint);
}
void RoverAckermannGuidance::updateSubscriptions()
@@ -131,8 +122,6 @@ void RoverAckermannGuidance::updateSubscriptions()
}
_curr_pos_ned = Vector2f(local_position.x, local_position.y);
const Vector3f rover_velocity = {local_position.vx, local_position.vy, local_position.vz};
_actual_speed = rover_velocity.norm();
}
if (_home_position_sub.updated()) {
@@ -145,18 +134,17 @@ void RoverAckermannGuidance::updateSubscriptions()
updateWaypointsAndAcceptanceRadius();
}
if (_vehicle_attitude_sub.updated()) {
vehicle_attitude_s vehicle_attitude{};
_vehicle_attitude_sub.copy(&vehicle_attitude);
matrix::Quatf vehicle_attitude_quaternion = Quatf(vehicle_attitude.q);
_vehicle_yaw = matrix::Eulerf(vehicle_attitude_quaternion).psi();
}
if (_mission_result_sub.updated()) {
mission_result_s mission_result{};
_mission_result_sub.copy(&mission_result);
_mission_finished = mission_result.finished;
}
if (_navigator_mission_item_sub.updated()) {
navigator_mission_item_s navigator_mission_item{};
_navigator_mission_item_sub.copy(&navigator_mission_item);
_nav_cmd = navigator_mission_item.nav_cmd;
}
}
void RoverAckermannGuidance::updateWaypointsAndAcceptanceRadius()
@@ -166,7 +154,7 @@ void RoverAckermannGuidance::updateWaypointsAndAcceptanceRadius()
// Global waypoint coordinates
_prev_wp = _curr_pos.isAllFinite() ? _curr_pos : Vector2d(0, 0); // Fallback if previous waypoint is invalid
_curr_wp = Vector2d(0, 0);
_curr_wp = _curr_pos.isAllFinite() ? _curr_pos : Vector2d(0, 0); // Fallback if current waypoint is invalid
_next_wp = _home_position.isAllFinite() ? _home_position : Vector2d(0, 0); // Enables corner slow down with RTL
if (position_setpoint_triplet.current.valid && PX4_ISFINITE(position_setpoint_triplet.current.lat)
@@ -192,46 +180,47 @@ void RoverAckermannGuidance::updateWaypointsAndAcceptanceRadius()
_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
_prev_waypoint_transition_angle = _waypoint_transition_angle;
_waypoint_transition_angle = acosf(cosin);
// Update acceptance radius
_prev_acceptance_radius = _acceptance_radius;
if (_param_ra_acc_rad_max.get() >= _param_nav_acc_rad.get()) {
_acceptance_radius = updateAcceptanceRadius(_curr_wp_ned, _prev_wp_ned, _next_wp_ned, _param_nav_acc_rad.get(),
_acceptance_radius = updateAcceptanceRadius(_waypoint_transition_angle, _param_nav_acc_rad.get(),
_param_ra_acc_rad_gain.get(), _param_ra_acc_rad_max.get(), _param_ra_wheel_base.get(), _param_ra_max_steer_angle.get());
} else {
_acceptance_radius = _param_nav_acc_rad.get();
}
// Waypoint cruising speed
if (position_setpoint_triplet.current.cruising_speed > 0.f) {
_wp_max_desired_vel = math::constrain(position_setpoint_triplet.current.cruising_speed, 0.f, _param_ra_max_speed.get());
_cruising_speed = math::constrain(position_setpoint_triplet.current.cruising_speed, 0.f, _param_ra_max_speed.get());
} else {
_wp_max_desired_vel = _param_ra_miss_vel_def.get();
_cruising_speed = _param_ra_miss_spd_def.get();
}
}
float RoverAckermannGuidance::updateAcceptanceRadius(const Vector2f &curr_wp_ned, const Vector2f &prev_wp_ned,
const Vector2f &next_wp_ned, const float default_acceptance_radius, const float acceptance_radius_gain,
float RoverAckermannGuidance::updateAcceptanceRadius(const float waypoint_transition_angle,
const float default_acceptance_radius, const float acceptance_radius_gain,
const float acceptance_radius_max, const float wheel_base, const float max_steer_angle)
{
// Setup variables
const Vector2f curr_to_prev_wp_ned = prev_wp_ned - curr_wp_ned;
const Vector2f curr_to_next_wp_ned = next_wp_ned - curr_wp_ned;
float acceptance_radius = default_acceptance_radius;
// Calculate acceptance radius s.t. the rover cuts the corner tangential to the current and next line segment
if (curr_to_next_wp_ned.norm() > FLT_EPSILON && curr_to_prev_wp_ned.norm() > FLT_EPSILON) {
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
const float theta = acosf(cosin) / 2.f;
const float min_turning_radius = wheel_base / sinf(max_steer_angle);
const float acceptance_radius_temp = min_turning_radius / tanf(theta);
const float acceptance_radius_temp_scaled = acceptance_radius_gain *
acceptance_radius_temp; // Scale geometric ideal acceptance radius to account for kinematic and dynamic effects
acceptance_radius = math::constrain<float>(acceptance_radius_temp_scaled, default_acceptance_radius,
acceptance_radius_max);
}
float acceptance_radius = default_acceptance_radius;
const float theta = waypoint_transition_angle / 2.f;
const float min_turning_radius = wheel_base / sinf(max_steer_angle);
const float acceptance_radius_temp = min_turning_radius / tanf(theta);
const float acceptance_radius_temp_scaled = acceptance_radius_gain *
acceptance_radius_temp; // Scale geometric ideal acceptance radius to account for kinematic and dynamic effects
acceptance_radius = math::constrain<float>(acceptance_radius_temp_scaled, default_acceptance_radius,
acceptance_radius_max);
// Publish updated acceptance radius
position_controller_status_s pos_ctrl_status{};
@@ -241,51 +230,56 @@ float RoverAckermannGuidance::updateAcceptanceRadius(const Vector2f &curr_wp_ned
return acceptance_radius;
}
float RoverAckermannGuidance::calcDesiredSpeed(const float miss_vel_def, const float miss_vel_min,
const float miss_vel_gain, const float distance_to_prev_wp, const float distance_to_curr_wp, const float acc_rad,
const float prev_acc_rad, const float max_accel, const float max_jerk, const int nav_state)
float RoverAckermannGuidance::calcDesiredSpeed(const float cruising_speed, const float miss_speed_min,
const float miss_speed_gain, const float distance_to_prev_wp, const float distance_to_curr_wp, const float acc_rad,
const float prev_acc_rad, const float max_decel, const float max_jerk, const int nav_state,
const float waypoint_transition_angle, const float prev_waypoint_transition_angle, const float max_speed)
{
// Catch improper values
if (miss_vel_min < 0.f || miss_vel_min > miss_vel_def || miss_vel_gain < FLT_EPSILON) {
return miss_vel_def;
if (miss_speed_min < 0.f || miss_speed_min > cruising_speed || miss_speed_gain < FLT_EPSILON) {
return cruising_speed;
}
// Cornering slow down effect
if (distance_to_prev_wp <= prev_acc_rad && prev_acc_rad > FLT_EPSILON) {
const float cornering_speed = miss_vel_gain / prev_acc_rad;
return math::constrain(cornering_speed, miss_vel_min, miss_vel_def);
const float cornering_speed = max_speed - miss_speed_gain * math::interpolate(M_PI_F - prev_waypoint_transition_angle,
0.f, M_PI_F, 0.f, 1.f);
return math::constrain(cornering_speed, miss_speed_min, cruising_speed);
} else if (distance_to_curr_wp <= acc_rad && acc_rad > FLT_EPSILON) {
const float cornering_speed = miss_vel_gain / acc_rad;
return math::constrain(cornering_speed, miss_vel_min, miss_vel_def);
const float cornering_speed = max_speed - miss_speed_gain * math::interpolate(M_PI_F - waypoint_transition_angle, 0.f,
M_PI_F, 0.f, 1.f);
return math::constrain(cornering_speed, miss_speed_min, cruising_speed);
}
// Straight line speed
if (max_accel > FLT_EPSILON && max_jerk > FLT_EPSILON && acc_rad > FLT_EPSILON) {
float max_velocity{0.f};
if (max_decel > FLT_EPSILON && max_jerk > FLT_EPSILON && acc_rad > FLT_EPSILON) {
float straight_line_speed{0.f};
if (nav_state == vehicle_status_s::NAVIGATION_STATE_AUTO_RTL) {
max_velocity = math::trajectory::computeMaxSpeedFromDistance(max_jerk,
max_accel, distance_to_curr_wp, 0.f);
straight_line_speed = math::trajectory::computeMaxSpeedFromDistance(max_jerk,
max_decel, distance_to_curr_wp, 0.f);
} else {
const float cornering_speed = miss_vel_gain / acc_rad;
max_velocity = math::trajectory::computeMaxSpeedFromDistance(max_jerk,
max_accel, distance_to_curr_wp - acc_rad, cornering_speed);
float cornering_speed = max_speed - miss_speed_gain * math::interpolate(M_PI_F - waypoint_transition_angle, 0.f, M_PI_F,
0.f, 1.f);
cornering_speed = math::constrain(cornering_speed, miss_speed_min, cruising_speed);
straight_line_speed = math::trajectory::computeMaxSpeedFromDistance(max_jerk,
max_decel, distance_to_curr_wp - acc_rad, cornering_speed);
}
return math::constrain(max_velocity, miss_vel_min, miss_vel_def);
return math::min(straight_line_speed, cruising_speed);
} else {
return miss_vel_def;
return cruising_speed;
}
}
float RoverAckermannGuidance::calcDesiredSteering(PurePursuit &pure_pursuit, const Vector2f &curr_wp_ned,
const Vector2f &prev_wp_ned, const Vector2f &curr_pos_ned, const float wheel_base, const float desired_speed,
const float vehicle_yaw, const float max_steering)
const float vehicle_yaw, const float max_steering, const bool armed)
{
const float desired_heading = pure_pursuit.calcDesiredHeading(curr_wp_ned, prev_wp_ned, curr_pos_ned,
desired_speed);
@@ -297,6 +291,10 @@ float RoverAckermannGuidance::calcDesiredSteering(PurePursuit &pure_pursuit, con
float desired_steering{0.f};
if (!armed) {
return desired_steering;
}
if (math::abs_t(heading_error) <= M_PI_2_F) {
desired_steering = atanf(2 * wheel_base * sinf(heading_error) / lookahead_distance);
@@ -309,22 +307,3 @@ float RoverAckermannGuidance::calcDesiredSteering(PurePursuit &pure_pursuit, con
return math::constrain(desired_steering, -max_steering, max_steering);
}
float RoverAckermannGuidance::calcThrottleSetpoint(PID_t &pid_throttle, const float desired_speed,
const float actual_speed, const float max_speed, const float dt)
{
float throttle = 0.f;
if (desired_speed < FLT_EPSILON) {
pid_reset_integral(&pid_throttle);
} else {
throttle = pid_calculate(&pid_throttle, desired_speed, actual_speed, 0, dt);
}
if (_param_ra_max_speed.get() > 0.f) { // Feed-forward term
throttle += math::interpolate<float>(desired_speed, 0.f, max_speed, 0.f, 1.f);
}
return math::constrain(throttle, 0.f, 1.f);
}
@@ -41,14 +41,15 @@
#include <uORB/Publication.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/topics/rover_ackermann_guidance_status.h>
#include <uORB/topics/rover_ackermann_setpoint.h>
#include <uORB/topics/position_setpoint_triplet.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_attitude.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>
#include <uORB/topics/navigator_mission_item.h>
// Standard library includes
#include <matrix/matrix/math.hpp>
@@ -74,21 +75,13 @@ public:
~RoverAckermannGuidance() = default;
/**
* @brief Struct for steering and throttle setpoints.
* @param steering Steering setpoint.
* @param throttle Throttle setpoint.
*/
struct motor_setpoint {
float steering{0.f};
float throttle{0.f};
};
/**
* @brief Calculate motor setpoints based on the mission plan.
* @brief Compute and publish speed and steering setpoints based on the mission plan.
* @param vehicle_forward_speed Forward speed of the vehicle [m/s]
* @param vehicle_yaw Yaw of the vehicle [rad]
* @param nav_state Vehicle navigation state.
* @return Motor setpoints for throttle and steering.
* @param armed Vehicle arm state.
*/
motor_setpoint computeGuidance(int nav_state);
void computeGuidance(float vehicle_forward_speed, float vehicle_yaw, int nav_state, bool armed);
protected:
/**
@@ -110,9 +103,7 @@ private:
/**
* @brief Publish the acceptance radius for current waypoint based on the angle between a line segment
* from the previous to the current waypoint/current to the next waypoint and maximum steer angle of the vehicle.
* @param curr_wp_ned Current waypoint in NED frame [m].
* @param prev_wp_ned Previous waypoint in NED frame [m].
* @param next_wp_ned Next waypoint in NED frame [m].
* @param waypoint_transition_angle Angle between the prevWP-currWP and currWP-nextWP line segments [rad]
* @param default_acceptance_radius Default acceptance radius for waypoints [m].
* @param acceptance_radius_gain Tuning parameter that scales the geometric optimal acceptance radius for the corner cutting [-].
* @param acceptance_radius_max Maximum value for the acceptance radius [m].
@@ -120,30 +111,34 @@ private:
* @param max_steer_angle Rover maximum steer angle [rad].
* @return Updated acceptance radius [m].
*/
float updateAcceptanceRadius(const Vector2f &curr_wp_ned, const Vector2f &prev_wp_ned,
const Vector2f &next_wp_ned, float default_acceptance_radius, float acceptance_radius_gain,
float updateAcceptanceRadius(const float waypoint_transition_angle, float default_acceptance_radius,
float acceptance_radius_gain,
float acceptance_radius_max, float wheel_base, float max_steer_angle);
/**
* @brief Calculate the desired speed setpoint. During cornering the speed is calculated as the inverse
* of the acceptance radius multiplied with a tuning factor. On straight lines it is based on a velocity trajectory
* of the acceptance radius multiplied with a tuning factor. On straight lines it is based on a speed trajectory
* such that the rover will arrive at the next corner with the desired cornering speed under consideration of the
* maximum acceleration and jerk.
* @param miss_vel_def Default desired velocity for the rover during mission [m/s].
* @param miss_vel_min Minimum desired velocity for the rover during mission [m/s].
* @param miss_vel_gain Tuning parameter for the slow down effect during cornering [-].
* @param cruising_speed Cruising speed [m/s].
* @param miss_speed_min Minimum speed setpoint [m/s].
* @param miss_speed_gain Tuning parameter for the slow down effect during cornering [-].
* @param distance_to_prev_wp Distance to the previous waypoint [m].
* @param distance_to_curr_wp Distance to the current waypoint [m].
* @param acc_rad Acceptance radius of the current waypoint [m].
* @param prev_acc_rad Acceptance radius of the previous waypoint [m].
* @param max_accel Maximum allowed acceleration for the rover [m/s^2].
* @param max_jerk Maximum allowed jerk for the rover [m/s^3].
* @param max_accel Maximum allowed acceleration [m/s^2].
* @param max_jerk Maximum allowed jerk [m/s^3].
* @param nav_state Current nav_state of the rover.
* @return Speed setpoint for the rover [m/s].
* @param waypoint_transition_angle Angle between the prevWP-currWP and currWP-nextWP line segments [rad]
* @param prev_waypoint_transition_angle Previous angle between the prevWP-currWP and currWP-nextWP line segments [rad]
* @param max_speed Maximum speed setpoint [m/s]
* @return Speed setpoint [m/s].
*/
float calcDesiredSpeed(float miss_vel_def, float miss_vel_min, float miss_vel_gain, float distance_to_prev_wp,
float distance_to_curr_wp, float acc_rad, float prev_acc_rad, float max_accel, float max_jerk, int nav_state);
float calcDesiredSpeed(float cruising_speed, float miss_speed_min, float miss_speed_gain, float distance_to_prev_wp,
float distance_to_curr_wp, float acc_rad, float prev_acc_rad, float max_accel, float max_jerk, int nav_state,
float waypoint_transition_angle, float prev_waypoint_transition_angle, float max_speed);
/**
* @brief Calculate desired steering angle. The desired steering is calulated as the steering that is required to
@@ -156,32 +151,23 @@ private:
* @param desired_speed Desired speed for the rover [m/s].
* @param vehicle_yaw Current yaw of the rover [rad].
* @param max_steering Maximum steering angle of the rover [rad].
* @param waypoint_transition_angle Angle between the prevWP-currWP and currWP-nextWP line segments [rad]
* @param armed Vehicle arm status
* @return Steering setpoint for the rover [rad].
*/
float calcDesiredSteering(PurePursuit &pure_pursuit, const Vector2f &curr_wp_ned, const Vector2f &prev_wp_ned,
const Vector2f &curr_pos_ned, float wheel_base, float desired_speed, float vehicle_yaw, float max_steering);
/**
* @brief Calculate the throttle setpoint. Calculated with a PID controller using the difference between
* the desired/actual speed and a feedforward term based on the full throttle speed.
* @param pid_throttle Reference to PID instance.
* @param desired_speed Reference speed for the rover [m/s].
* @param actual_speed Actual speed of the rover [m/s].
* @param max_speed Rover speed at full throttle [m/s].
* @param dt Time interval since last update [s].
* @return Normalized throttle setpoint [0, 1].
*/
float calcThrottleSetpoint(PID_t &pid_throttle, float desired_speed, float actual_speed, float max_speed, float dt);
const Vector2f &curr_pos_ned, float wheel_base, float desired_speed, float vehicle_yaw, float max_steering, bool armed);
// 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 _local_position_sub{ORB_ID(vehicle_local_position)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
uORB::Subscription _mission_result_sub{ORB_ID(mission_result)};
uORB::Subscription _home_position_sub{ORB_ID(home_position)};
uORB::Subscription _navigator_mission_item_sub{ORB_ID(navigator_mission_item)};
// uORB publications
uORB::Publication<rover_ackermann_setpoint_s> _rover_ackermann_setpoint_pub{ORB_ID(rover_ackermann_setpoint)};
uORB::Publication<rover_ackermann_guidance_status_s> _rover_ackermann_guidance_status_pub{ORB_ID(rover_ackermann_guidance_status)};
uORB::Publication<position_controller_status_s> _position_controller_status_pub{ORB_ID(position_controller_status)};
rover_ackermann_guidance_status_s _rover_ackermann_guidance_status{};
@@ -192,13 +178,10 @@ private:
// Rover variables
float _desired_steering{0.f};
float _vehicle_yaw{0.f};
float _desired_speed{0.f};
float _actual_speed{0.f};
Vector2d _curr_pos{};
Vector2f _curr_pos_ned{};
PID_t _pid_throttle;
hrt_abstime _timestamp{0};
bool _mission_finished{false};
// Waypoint variables
Vector2d _home_position{};
@@ -208,13 +191,12 @@ private:
Vector2d _curr_wp{};
Vector2d _prev_wp{};
Vector2d _next_wp{};
float _distance_to_prev_wp{0.f};
float _distance_to_curr_wp{0.f};
float _distance_to_next_wp{0.f};
float _acceptance_radius{0.5f};
float _prev_acceptance_radius{0.5f};
float _wp_max_desired_vel{0.f};
bool _mission_finished{false};
float _cruising_speed{0.f};
float _waypoint_transition_angle{0.f}; // Angle between the prevWP-currWP and currWP-nextWP line segments [rad]
float _prev_waypoint_transition_angle{0.f}; // Previous Angle between the prevWP-currWP and currWP-nextWP line segments [rad]
uint _nav_cmd{0};
// Parameters
DEFINE_PARAMETERS(
@@ -222,14 +204,14 @@ private:
(ParamFloat<px4::params::RA_MAX_STR_ANG>) _param_ra_max_steer_angle,
(ParamFloat<px4::params::RA_ACC_RAD_MAX>) _param_ra_acc_rad_max,
(ParamFloat<px4::params::RA_ACC_RAD_GAIN>) _param_ra_acc_rad_gain,
(ParamFloat<px4::params::RA_MISS_VEL_DEF>) _param_ra_miss_vel_def,
(ParamFloat<px4::params::RA_MISS_VEL_MIN>) _param_ra_miss_vel_min,
(ParamFloat<px4::params::RA_MISS_VEL_GAIN>) _param_ra_miss_vel_gain,
(ParamFloat<px4::params::RA_MISS_SPD_DEF>) _param_ra_miss_spd_def,
(ParamFloat<px4::params::RA_MISS_SPD_MIN>) _param_ra_miss_spd_min,
(ParamFloat<px4::params::RA_MISS_SPD_GAIN>) _param_ra_miss_spd_gain,
(ParamFloat<px4::params::RA_SPEED_P>) _param_ra_p_speed,
(ParamFloat<px4::params::RA_SPEED_I>) _param_ra_i_speed,
(ParamFloat<px4::params::RA_MAX_SPEED>) _param_ra_max_speed,
(ParamFloat<px4::params::RA_MAX_JERK>) _param_ra_max_jerk,
(ParamFloat<px4::params::RA_MAX_ACCEL>) _param_ra_max_accel,
(ParamFloat<px4::params::RA_MAX_DECEL>) _param_ra_max_decel,
(ParamFloat<px4::params::NAV_ACC_RAD>) _param_nav_acc_rad
)
};
+44 -13
View File
@@ -57,9 +57,9 @@ parameters:
decimal: 2
default: 2
RA_MISS_VEL_DEF:
RA_MISS_SPD_DEF:
description:
short: Default rover velocity during a mission
short: Default rover speed during a mission
type: float
unit: m/s
min: 0
@@ -68,11 +68,11 @@ parameters:
decimal: 2
default: 2
RA_MISS_VEL_MIN:
RA_MISS_SPD_MIN:
description:
short: Minimum rover velocity during a mission
short: Minimum rover speed during a mission
long: |
The velocity off the rover is reduced based on the corner it has to take
The speed off the rover is reduced based on the corner it has to take
to smooth the trajectory (Set to -1 to disable)
type: float
unit: m/s
@@ -82,13 +82,13 @@ parameters:
decimal: 2
default: 1
RA_MISS_VEL_GAIN:
RA_MISS_SPD_GAIN:
description:
short: Tuning parameter for the velocity reduction during cornering
short: Tuning parameter for the speed reduction during cornering
long: |
The cornering speed is equal to the inverse of the acceptance radius
of the WP multiplied with this factor.
Lower value -> More velocity reduction during cornering.
Lower value -> More speed reduction during cornering.
type: float
min: 0.05
max: 100
@@ -112,17 +112,32 @@ parameters:
type: float
min: 0
max: 100
increment: 0.01
decimal: 2
increment: 0.001
decimal: 3
default: 1
RA_MAX_SPEED:
description:
short: Maximum allowed speed
long: |
This is the maximum allowed speed setpoint when driving in a mode that uses
closed loop speed control.
type: float
unit: m/s
min: -1
max: 100
increment: 0.01
decimal: 2
default: -1
RA_MAX_THR_SPEED:
description:
short: Speed the rover drives at maximum throttle
long: |
This is used for the feed-forward term of the speed controller.
A value of -1 disables the feed-forward term in which case the
Integrator (RA_SPEED_I) becomes necessary to track speed setpoints.
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: -1
@@ -147,6 +162,22 @@ parameters:
decimal: 2
default: -1
RA_MAX_DECEL:
description:
short: Maximum deceleration for the rover
long: |
This is used for the deceleration slew rate, the feed-forward term
for the speed controller during missions and the corner slow down effect.
Note: For the corner slow down effect RA_MAX_JERK, RA_MISS_VEL_GAIN and
RA_MISS_VEL_MIN also have to be set.
type: float
unit: m/s^2
min: -1
max: 100
increment: 0.01
decimal: 2
default: -1
RA_MAX_JERK:
description:
short: Maximum jerk