Skip to content

Commit

Permalink
cherry-picking from voxl impl
Browse files Browse the repository at this point in the history
  • Loading branch information
mnissov committed Feb 6, 2025
1 parent e560ac2 commit 0c6027d
Show file tree
Hide file tree
Showing 30 changed files with 493 additions and 1,970 deletions.
1 change: 0 additions & 1 deletion boards/mro/pixracerpro/default.px4board
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ CONFIG_MODULES_MC_AUTOTUNE_ATTITUDE_CONTROL=y
CONFIG_MODULES_MC_HOVER_THRUST_ESTIMATOR=y
CONFIG_MODULES_MC_POS_CONTROL=y
CONFIG_MODULES_MC_RATE_CONTROL=y
CONFIG_MODULES_MC_SAFE_CONTROL=y
CONFIG_MODULES_NAVIGATOR=y
CONFIG_MODULES_RC_UPDATE=y
CONFIG_MODULES_ROVER_POS_CONTROL=y
Expand Down
7 changes: 5 additions & 2 deletions msg/CbfDebug.msg
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
uint64 timestamp # time since system start (microseconds)

float32 h
uint8 qp_fail

float32[3] virtual_obstacle
float32 h
float32 h1
float32 h2

float32[3] input
float32[3] output
float32[2] slack
1 change: 1 addition & 0 deletions src/modules/logger/logged_topics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ void LoggedTopics::add_default_topics()
add_topic("vehicle_status");
add_optional_topic("vtol_vehicle_status", 200);
add_topic("wind", 1000);
add_topic("cbf_debug", 10);

// multi topics
add_optional_topic_multi("actuator_outputs", 100, 3);
Expand Down
2 changes: 1 addition & 1 deletion src/modules/mavlink/mavlink
2 changes: 1 addition & 1 deletion src/modules/mavlink/mavlink_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,7 @@ Mavlink::configure_streams_to_default(const char *configure_single_stream)
configure_stream_local("MOUNT_ORIENTATION", 10.0f);
configure_stream_local("OBSTACLE_DISTANCE", 10.0f);
configure_stream_local("ODOMETRY", 30.0f);

configure_stream_local("CBF_DEBUG", 100.0f);
configure_stream_local("ADSB_VEHICLE", unlimited_rate);
configure_stream_local("ATTITUDE_QUATERNION", 50.0f);
configure_stream_local("ATTITUDE_TARGET", 10.0f);
Expand Down
2 changes: 1 addition & 1 deletion src/modules/mavlink/mavlink_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ static const StreamListItem streams_list[] = {
create_stream_list_item<MavlinkStreamCurrentMode>(),
#endif // CURRENT_MODE_HPP
#if defined(CBF_DEBUG_HPP)
create_stream_list_item<MavlinkStreamCbfDebug>(),
create_stream_list_item<MavlinkStreamCbfDebug>()
#endif // CBF_DEBUG_HPP
};

Expand Down
11 changes: 8 additions & 3 deletions src/modules/mavlink/streams/CBF_DEBUG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,18 @@ class MavlinkStreamCbfDebug : public MavlinkStream

msg.time_usec = cbf_debug.timestamp;

msg.qp_fail = cbf_debug.qp_fail;
msg.h = cbf_debug.h;
msg.input[0] = cbf_debug.output[0];
msg.input[1] = cbf_debug.output[1];
msg.input[2] = cbf_debug.output[2];
msg.h1 = cbf_debug.h1;
msg.h2 = cbf_debug.h2;
msg.input[0] = cbf_debug.input[0];
msg.input[1] = cbf_debug.input[1];
msg.input[2] = cbf_debug.input[2];
msg.output[0] = cbf_debug.output[0];
msg.output[1] = cbf_debug.output[1];
msg.output[2] = cbf_debug.output[2];
msg.slack[0] = cbf_debug.slack[0];
msg.slack[1] = cbf_debug.slack[1];

mavlink_msg_cbf_debug_send_struct(_mavlink->get_channel(), &msg);

Expand Down
1 change: 1 addition & 0 deletions src/modules/mc_pos_control/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ px4_add_module(
controllib
geo
SlewRate
qpoases
)
16 changes: 16 additions & 0 deletions src/modules/mc_pos_control/MulticopterPositionControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,16 @@ void MulticopterPositionControl::parameters_update(bool force)
_takeoff.setSpoolupTime(_param_com_spoolup_time.get());
_takeoff.setTakeoffRampTime(_param_mpc_tko_ramp_t.get());
_takeoff.generateInitialRampValue(_param_mpc_z_vel_p_acc.get());

// CBF parameters
_control.getCbf().setEpsilon(_param_cbf_epsilon.get());
_control.getCbf().setPole0(_param_cbf_pole0.get());
_control.getCbf().setKappa(_param_cbf_kappa.get());
_control.getCbf().setGamma(_param_cbf_gamma.get());
_control.getCbf().setAlpha(_param_cbf_alpha.get());
_control.getCbf().setFovAlpha(_param_cbf_fov_alpha.get());
_control.getCbf().setFovSlack(_param_cbf_fov_slack.get());
_control.getCbf().setEnabled((bool)_param_cbf_enabled.get());
}
}

Expand Down Expand Up @@ -524,6 +534,12 @@ void MulticopterPositionControl::Run()
_control.update(dt);
}

// Publish internal safety filter calculations
cbf_debug_s cbf_debug_msg{};
cbf_debug_msg.timestamp = hrt_absolute_time();
_control.getCbf().getDebug(cbf_debug_msg);
_cbf_debug_pub.publish(cbf_debug_msg);

// Publish internal position control setpoints
// on top of the input/feed-forward setpoints these containt the PID corrections
// This message is used by other modules (such as Landdetector) to determine vehicle intention.
Expand Down
14 changes: 13 additions & 1 deletion src/modules/mc_pos_control/MulticopterPositionControl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#include <uORB/topics/vehicle_land_detected.h>
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_local_position_setpoint.h>
#include <uORB/topics/cbf_debug.h>

using namespace time_literals;

Expand Down Expand Up @@ -96,6 +97,7 @@ class MulticopterPositionControl : public ModuleBase<MulticopterPositionControl>
uORB::PublicationData<takeoff_status_s> _takeoff_status_pub{ORB_ID(takeoff_status)};
uORB::Publication<vehicle_attitude_setpoint_s> _vehicle_attitude_setpoint_pub{ORB_ID(vehicle_attitude_setpoint)};
uORB::Publication<vehicle_local_position_setpoint_s> _local_pos_sp_pub{ORB_ID(vehicle_local_position_setpoint)}; /**< vehicle local position setpoint publication */
uORB::Publication<cbf_debug_s> _cbf_debug_pub{ORB_ID(cbf_debug)};

uORB::SubscriptionCallbackWorkItem _local_pos_sub{this, ORB_ID(vehicle_local_position)}; /**< vehicle local position */

Expand Down Expand Up @@ -182,7 +184,17 @@ class MulticopterPositionControl : public ModuleBase<MulticopterPositionControl>

(ParamFloat<px4::params::MPC_XY_ERR_MAX>) _param_mpc_xy_err_max,
(ParamFloat<px4::params::MPC_YAWRAUTO_MAX>) _param_mpc_yawrauto_max,
(ParamFloat<px4::params::MPC_YAWRAUTO_ACC>) _param_mpc_yawrauto_acc
(ParamFloat<px4::params::MPC_YAWRAUTO_ACC>) _param_mpc_yawrauto_acc,

// CBF parameters
(ParamFloat<px4::params::CBF_EPSILON>) _param_cbf_epsilon, /**< TODO describe */
(ParamFloat<px4::params::CBF_POLE0>) _param_cbf_pole0,
(ParamFloat<px4::params::CBF_KAPPA>) _param_cbf_kappa,
(ParamFloat<px4::params::CBF_GAMMA>) _param_cbf_gamma,
(ParamFloat<px4::params::CBF_ALPHA>) _param_cbf_alpha,
(ParamFloat<px4::params::CBF_FOV_ALPHA>) _param_cbf_fov_alpha,
(ParamFloat<px4::params::CBF_FOV_SLACK>) _param_cbf_fov_slack,
(ParamBool<px4::params::CBF_ENABLED>) _param_cbf_enabled
);

control::BlockDerivative _vel_x_deriv; /**< velocity derivative in x */
Expand Down
235 changes: 235 additions & 0 deletions src/modules/mc_pos_control/PositionControl/CBFSafetyFilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/**
* @file CBFSafetyFilter.cpp
*/
#include <CBFSafetyFilter.hpp>

#include <px4_platform_common/module.h>
#include <math.h>
#include <string.h>

CBFSafetyFilter::CBFSafetyFilter() {
qp = QProblem(NV, NC);
qp.setPrintLevel(PL_NONE);
}

void CBFSafetyFilter::updateObstacles() {
tof_obstacles_chunk_s tof_obstacles_chunk;
if (_tof_obstacles_chunk_sub.update(&tof_obstacles_chunk))
{
// TODO make this part of the message
size_t max_chunk_size = 20;

// if we get a new pointcloud with fewer points than the previous, remove the overflow
for (size_t i = _obstacles.size() ; i >= tof_obstacles_chunk.num_points_total ; i--)
{
_obstacles.remove(i);
}

for (
size_t i_obs = tof_obstacles_chunk.chunk_id * max_chunk_size, i_chnk = 0 ;
i_chnk < tof_obstacles_chunk.num_points_chunk ;
i_obs++, i_chnk++
) {
// if the obstacle array is already large enough, replace
if (i_obs < _obstacles.size())
{
_obstacles[i_obs] = Vector3f(
tof_obstacles_chunk.points_x[i_chnk],
tof_obstacles_chunk.points_y[i_chnk],
tof_obstacles_chunk.points_z[i_chnk]
);
}
// else, pushback
else
{
_obstacles.push_back(Vector3f(
tof_obstacles_chunk.points_x[i_chnk],
tof_obstacles_chunk.points_y[i_chnk],
tof_obstacles_chunk.points_z[i_chnk]
));
}
}
}
}

void CBFSafetyFilter::updateAttitude() {
if (_vehicle_attitude_sub.updated())
{
vehicle_attitude_s vehicle_attitude;
if (_vehicle_attitude_sub.copy(&vehicle_attitude))
_attitude = Quatf(vehicle_attitude.q);
}
}

void CBFSafetyFilter::filter(Vector3f& acceleration_setpoint, const Vector3f& velocity, uint64_t timestamp) {
if (!_enabled) return;

// TODO reset obstacle with timer
// pass through if no obstacles are recorded
updateAttitude();
updateObstacles();
const size_t n = _obstacles.size();
if (n == 0) return;

// compute local state
Dcmf R_WB(_attitude);
Dcmf R_BW = R_WB.transpose();
Eulerf euler_current(_attitude);
Eulerf euler_WV(0.f, 0.f, euler_current.psi());
Dcmf R_WV(euler_WV);
Dcmf R_VW = R_WV.transpose();
Dcmf R_BV = R_BW * R_WV;

_body_acceleration_setpoint = R_BW * acceleration_setpoint;
_body_velocity = R_BW * velocity;
_vehicle_velocity = R_VW * velocity;


// composite collision CBF
// nu1_i
for(size_t i = 0; i < n; i++) {
float nu_i0 = _obstacles[i].norm_squared() - (_epsilon * _epsilon);
float Lf_nu_i0 = -2.f * _obstacles[i].dot(_body_velocity);
_nu1[i] = Lf_nu_i0 - _pole0 * nu_i0;
}

// h(x)
float exp_sum = 0.f;
for(size_t i = 0; i < n; i++) {
exp_sum += expf(-_kappa * saturate(_nu1[i] / _gamma));
}
float h = -(_gamma / _kappa) * logf(exp_sum);

// L_{f}h(x)
float Lf_h = 0.f;
for(size_t i = 0; i < n; i++) {
float Lf_nu_i1 = 2.f * (_body_velocity + _pole0 * _obstacles[i]).dot(_body_velocity);
float lambda_i = expf(-_kappa * saturate(_nu1[i] / _gamma)) * saturateDerivative(_nu1[i] / _gamma);
Lf_h += lambda_i * Lf_nu_i1;
}
Lf_h /= exp_sum;

// L_{g}h(x)
Vector3f Lg_h(0.f, 0.f, 0.f);
for(size_t i = 0; i < n; i++) {
Vector3f Lg_nu_i1 = -2.f * _obstacles[i];
float lambda_i = expf(-_kappa * saturate(_nu1[i] / _gamma)) * saturateDerivative(_nu1[i] / _gamma);
Lg_h += lambda_i * Lg_nu_i1;
}
Lg_h /= exp_sum;

// L_{g}h(x) * u, u = k_n(x) = a
float Lg_h_u = Lg_h.dot(_body_acceleration_setpoint);


// horizontal FoV CBF
Vector3f e1(sinf(_fov_h), cosf(_fov_h), 0.f);
Vector3f e2(sinf(_fov_h), -cosf(_fov_h), 0.f);
float h1 = (e1).dot(_vehicle_velocity);
float h2 = (e2).dot(_vehicle_velocity);
float Lf_h1 = 0.f;
float Lf_h2 = 0.f;
Vector3f Lg_h1 = R_BV * e1;
Vector3f Lg_h2 = R_BV * e2;

// analytical QP solution from: https://arxiv.org/abs/2206.03568
// float eta = 0.f;
// float Lg_h_mag2 = Lg_h.norm_squared();
// if (Lg_h_mag2 > 1e-5f) {
// eta = -(Lf_h + Lg_h_u + _alpha*h) / Lg_h_mag2;
// }
// Vector3f local_correction = (eta > 0.f ? eta : 0.f) * Lg_h;
// dbg.x = local_correction(0);
// dbg.y = local_correction(1);
// dbg.z = local_correction(2);
// local_accel_setpoint += local_correction;
// acceleration_setpoint = R_IB * local_accel_setpoint;

_debug_msg.h = h;
_debug_msg.h1 = h1;
_debug_msg.h2 = h2;
// _debug_msg.virtual_obstacle = ; // TODO: marvin
_debug_msg.input[0] = acceleration_setpoint(0);
_debug_msg.input[1] = acceleration_setpoint(1);
_debug_msg.input[2] = acceleration_setpoint(2);

// solve QP
// quadratic cost x^T*H*x
real_t H[NV*NV] = {1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 3.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0};
// linear cost matrix g*x
real_t g[NV] = { 0.0, 0.0, 0.0, (real_t)_fov_slack, (real_t)_fov_slack };
// constraint matrix A
real_t A[NC*NV] = {(real_t)Lg_h(0), (real_t)Lg_h(1), (real_t)Lg_h(2), (real_t)0.0, (real_t)0.0,
(real_t)0.0, (real_t)0.0, (real_t)0.0, (real_t)1.0, (real_t)0.0,
(real_t)Lg_h1(0), (real_t)Lg_h1(1), (real_t)Lg_h1(2), (real_t)1.0, (real_t)0.0,
(real_t)0.0, (real_t)0.0, (real_t)0.0, (real_t)0.0, (real_t)1.0,
(real_t)Lg_h2(0), (real_t)Lg_h2(1), (real_t)Lg_h2(2), 0.0, (real_t)1.0};
// bounds on Ax
real_t lbA[NC] = { (real_t)(-Lf_h - kappaFunction(h, _alpha) - Lg_h_u), 0.0, (real_t)(-Lf_h1 - _fov_alpha * h1), 0.0, (real_t)(-Lf_h2 - _fov_alpha * h2) };
real_t* ubA = NULL;
// bounds on x
real_t* lb = NULL;
real_t* ub = NULL;
int_t nWSR = 50;

qp = QProblem(NV, NC);
qp.setPrintLevel(PL_NONE);
returnValue qp_status = qp.init(H, g, A, lb, ub, lbA, ubA, nWSR);

switch(qp_status) {
case SUCCESSFUL_RETURN: {
qp.getPrimalSolution(_xOpt);
Vector3f acceleration_correction(_xOpt[0], _xOpt[1], _xOpt[2]);
_body_acceleration_setpoint += acceleration_correction;
acceleration_setpoint = R_WB * _body_acceleration_setpoint;
_debug_msg.qp_fail = 0;
break;
}
case RET_MAX_NWSR_REACHED:
PX4_ERR("QP could not be solved within the given number of working set recalculations");
_debug_msg.qp_fail = 1;
break;
default:
PX4_ERR("QP failed: returned %d", qp_status);
_debug_msg.qp_fail = 1;
break;
}

clampAccSetpoint(acceleration_setpoint);

_debug_msg.output[0] = acceleration_setpoint(0);
_debug_msg.output[1] = acceleration_setpoint(1);
_debug_msg.output[2] = acceleration_setpoint(2);
_debug_msg.slack[0] = _xOpt[3];
_debug_msg.slack[1] = _xOpt[4];
}

void CBFSafetyFilter::clampAccSetpoint(Vector3f& acceleration_setpoint) {
acceleration_setpoint(0) = math::constrain(acceleration_setpoint(0), -max_acc_xy, max_acc_xy);
acceleration_setpoint(1) = math::constrain(acceleration_setpoint(1), -max_acc_xy, max_acc_xy);
acceleration_setpoint(2) = math::constrain(acceleration_setpoint(2), -max_acc_z, max_acc_z);
}

float CBFSafetyFilter::saturate(float x) {
return tanh(x);
}

float CBFSafetyFilter::saturateDerivative(float x) {
float th = tanh(x);
return 1.f - (th * th);
}

float CBFSafetyFilter::kappaFunction(float h, float alpha) {
float a = alpha;
float b = 1.f/alpha;
if (h>=0.f) {
return alpha * h;
}
else {
return a * b * ( h / (b + abs(h)) );
}
}
Loading

0 comments on commit 0c6027d

Please sign in to comment.