Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incremental Nonlinear Dynamic Inversion (INDI) position controller for the Crazyflie #568

Merged
merged 14 commits into from
Apr 20, 2020
Merged
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ PROJ_OBJ += crtp_commander_generic.o crtp_localization_service.o
PROJ_OBJ += attitude_pid_controller.o sensfusion6.o stabilizer.o
PROJ_OBJ += position_estimator_altitude.o position_controller_pid.o
PROJ_OBJ += estimator.o estimator_complementary.o
PROJ_OBJ += controller.o controller_pid.o controller_mellinger.o controller_indi.o
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add the position_controller_indi.o to line 169 instead, so that all position controllers are in the Makefile together?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

PROJ_OBJ += controller.o controller_pid.o controller_mellinger.o controller_indi.o position_controller_indi.o
PROJ_OBJ += power_distribution_$(POWER_DISTRIBUTION).o
PROJ_OBJ += estimator_kalman.o kalman_core.o kalman_supervisor.o

Expand Down
15 changes: 8 additions & 7 deletions src/modules/interface/controller_indi.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "param.h"
#include "position_controller.h"
#include "attitude_controller.h"
#include "position_controller_indi.h"

#define ATTITUDE_UPDATE_DT (float)(1.0f/ATTITUDE_RATE)

Expand All @@ -43,13 +44,13 @@
#define STABILIZATION_INDI_FILT_CUTOFF_R STABILIZATION_INDI_FILT_CUTOFF

// these parameters are used in the filtering of the angular acceleration
#define STABILIZATION_INDI_G1_P 0.0034724f
#define STABILIZATION_INDI_G1_Q 0.0052575f
#define STABILIZATION_INDI_G1_R -0.0015942f
#define STABILIZATION_INDI_G2_R -0.11281f
#define STABILIZATION_INDI_REF_ERR_P 3.57f
#define STABILIZATION_INDI_REF_ERR_Q 3.57f
#define STABILIZATION_INDI_REF_ERR_R 3.57f
Comment on lines -46 to -52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a difference. How did you tune this? I guess this are the values for the INDI attitude controller right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attitude INDI was quite uncontrollable (very slow and not very stable), that's why we have decided to re-estimate some of the parameters.
The gains (STABILIZATION_INDI_REF_ERR_P/Q/R) were tuned manually. The control effectiveness parameters (G1, G2) of the INDI were tuned according to the paper "Adaptive Incremental Nonlinear Dynamic Inversion for Attitude Control of Micro Air Vehicles" (Section A. Parameter Estimation). I have uploaded two Matlab scripts (param_estimation.m, import_logdata.m) and the flight data (data_1901.csv) I used to estimate these parameters. They are located in the /tools/param_est folder.

#define STABILIZATION_INDI_G1_P 0.0066146f
#define STABILIZATION_INDI_G1_Q 0.0052125f
#define STABILIZATION_INDI_G1_R -0.001497f
#define STABILIZATION_INDI_G2_R 0.000043475f
#define STABILIZATION_INDI_REF_ERR_P 24.0f
#define STABILIZATION_INDI_REF_ERR_Q 24.0f
#define STABILIZATION_INDI_REF_ERR_R 24.0f
#define STABILIZATION_INDI_REF_RATE_P 14.0f
#define STABILIZATION_INDI_REF_RATE_Q 14.0f
#define STABILIZATION_INDI_REF_RATE_R 14.0f
Expand Down
86 changes: 86 additions & 0 deletions src/modules/interface/position_controller_indi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* || ____ _ __
* +------+ / __ )(_) /_______________ _____ ___
* | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
* +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
* || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
*
* Crazyflie control firmware
*
* Copyright (C) 2011-2016 Bitcraze AB
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, in version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* position_controller_indi.h - INDI Controller Interface
*/

#ifndef __POSITION_CONTROLLER_INDI_H__
#define __POSITION_CONTROLLER_INDI_H__

// Thrust command (in motor units)
#define MIN_THRUST 0
#define MAX_THRUST 60000

// Cutoff frequency used in the filtering
#define POSITION_INDI_FILT_CUTOFF 8.0f

#include "controller_indi.h"
#include "stabilizer_types.h"
#include "filter.h"
#include "math3d.h"
#include "log.h"
#include "param.h"

struct Vectr {
float x;
float y;
float z;
};

struct Angles {
float phi;
float theta;
float psi;
};

struct IndiOuterVariables {

Butterworth2LowPass ddxi[3];
Butterworth2LowPass ang[3];
Butterworth2LowPass thr[3];

float filt_cutoff;
float act_dyn_posINDI;

struct Vectr linear_accel_ref;
struct Vectr linear_accel_err;
struct Vectr linear_accel_s; // acceleration sensed
struct Vectr linear_accel_f; // acceleration filtered
struct Vectr linear_accel_ft; // acceleration filtered transformed to NED
struct Angles attitude_s; // attitude senssed (here estimated)
struct Angles attitude_f; // attitude filtered
struct Angles attitude_c; // attitude commanded to the inner loop
float phi_tilde; // roll angle increment
float theta_tilde; // pitch angle increment
float T_tilde; // thrust increment
float T_inner; // thrust to inner INDI
float T_inner_f;
float T_incremented;
};

void positionControllerINDIInit(void);
void positionControllerINDI(const sensorData_t *sensors,
const state_t *state,
vector_t *refOuterINDI);

#endif //__POSITION_CONTROLLER_INDI_H__
64 changes: 49 additions & 15 deletions src/modules/src/controller_indi.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,20 @@ static float bound_control_input = 32000.0f;
static attitude_t attitudeDesired;
static attitude_t rateDesired;
static float actuatorThrust;
static float roll_kp = 6.0f;
static float pitch_kp = 6.0f;
static float yaw_kp = 6.0f;
static float roll_kp = 5.0f;
static float pitch_kp = 5.0f;
static float yaw_kp = 5.0f;

static float attYawError;

static float r_roll;
static float r_pitch;
static float r_yaw;
static float accelz;

static vector_t refOuterINDI; // Reference values from outer loop INDI
static float outerLoopActive; // if 1, outer loop INDI is activated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OuterloopActive is a boolean right? You should define this as a boolean and initialize it as = false. (don't forget to change this at the parameters as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it as you suggested, however I am not sure if there is a boolean type for parameters (that's why I defined it as PARAM_UINT8).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, there is no boolean for parameters, but uint8 is just fine. Thanks for changing it


static struct IndiVariables indi = {
.g1 = {STABILIZATION_INDI_G1_P, STABILIZATION_INDI_G1_Q, STABILIZATION_INDI_G1_R},
.g2 = STABILIZATION_INDI_G2_R,
Expand Down Expand Up @@ -119,6 +124,7 @@ void controllerINDIInit(void)

attitudeControllerInit(ATTITUDE_UPDATE_DT);
positionControllerInit();
positionControllerINDIInit();
}

bool controllerINDITest(void)
Expand Down Expand Up @@ -149,7 +155,7 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,
}
}

if (RATE_DO_EXECUTE(POSITION_RATE, tick)) {
if (RATE_DO_EXECUTE(POSITION_RATE, tick) && !outerLoopActive) {
Copy link
Contributor

@knmcguire knmcguire Mar 31, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I'm not exactly sure about this. So you made an outerloop boolean to give people the choice to use either PID or INDI for the position control? Just trying to to think if this is actually that necessary like this, as this enables people to change controllers on the fly, which results in both controllers being made and that might be overkilled. Could this also be handled with a define or a make flag? So that at build people can decide if they want position INDI or PID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason why I introduced this variable is to differentiate between two main use cases I needed:

  • Attitude INDI only
  • Position and Attitude INDI

I can remove position PID from attitude INDI completely and at first structure the INDI controllers (into different files) as you suggested at the bottom. Thus, there will remain only two options:

  • Attitude INDI only
  • Position and Attitude INDI

which are controlled by the outerLoopActive.

Later, (if needed) one can add position PID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah good point. Just leave as is for now. I don't think it is a big issue on a second thought and I actually would like to try out indi/pid with my position controller script that I just pushed to the python library.

positionController(&actuatorThrust, &attitudeDesired, setpoint, state);
}

Expand All @@ -158,13 +164,33 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,
*/
if (RATE_DO_EXECUTE(ATTITUDE_RATE, tick)) {

// Call outer loop INDI (position controller)
if (outerLoopActive) {
positionControllerINDI(sensors, state, &refOuterINDI);
}

// Switch between manual and automatic position control
if (setpoint->mode.z == modeDisable) {
actuatorThrust = setpoint->thrust;
if (outerLoopActive) {
// INDI position controller active, INDI attitude controller becomes inner loop
actuatorThrust = refOuterINDI.z;
}
else {
// INDI position controller not active, INDI attitude controller is main loop
actuatorThrust = setpoint->thrust;
}
}
if (setpoint->mode.x == modeDisable || setpoint->mode.y == modeDisable) {
attitudeDesired.roll = setpoint->attitude.roll;
attitudeDesired.pitch = setpoint->attitude.pitch;
if (outerLoopActive) {
// INDI position controller active, INDI attitude controller becomes inner loop
attitudeDesired.roll = refOuterINDI.x;
attitudeDesired.pitch = refOuterINDI.y;
}
else {
// INDI position controller not active, INDI attitude controller is main loop
attitudeDesired.roll = setpoint->attitude.roll;
attitudeDesired.pitch = setpoint->attitude.pitch;
}
}

// attitudeControllerCorrectAttitudePID(state->attitude.roll, state->attitude.pitch, state->attitude.yaw,
Expand All @@ -173,7 +199,16 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,

rateDesired.roll = roll_kp*(attitudeDesired.roll - state->attitude.roll);
rateDesired.pitch = pitch_kp*(attitudeDesired.pitch - state->attitude.pitch);
rateDesired.yaw = yaw_kp*(attitudeDesired.yaw - state->attitude.yaw);
//rateDesired.yaw = yaw_kp*(attitudeDesired.yaw - state->attitude.yaw);
attYawError = attitudeDesired.yaw - state->attitude.yaw;
if (attYawError > 180.0f) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In PID controller file

attitudeDesired.yaw = capAngle(attitudeDesired.yaw);
, a static function capangle is used. Maybe do it in the same way?

Anyway, if we get more controllers we probably need to think about making a separate file with controller helper functions, but that is something for the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

attYawError = attYawError - 360.0f;
}
else if (attYawError < -180.0f) {
attYawError = attYawError + 360.0f;
}
rateDesired.yaw = yaw_kp*attYawError;


// For roll and pitch, if velocity mode, overwrite rateDesired with the setpoint
// value. Also reset the PID to avoid error buildup, which can lead to unstable
Expand Down Expand Up @@ -228,14 +263,11 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,
float attitude_error_q = radians(rateDesired.pitch) - stateAttitudeRatePitch;
float attitude_error_r = radians(rateDesired.yaw) - stateAttitudeRateYaw;

indi.angular_accel_ref.p = indi.reference_acceleration.err_p * attitude_error_p
- indi.reference_acceleration.rate_p * body_rates.p;
indi.angular_accel_ref.p = indi.reference_acceleration.err_p * attitude_error_p;

indi.angular_accel_ref.q = indi.reference_acceleration.err_q * attitude_error_q
- indi.reference_acceleration.rate_q * body_rates.q;
indi.angular_accel_ref.q = indi.reference_acceleration.err_q * attitude_error_q;

indi.angular_accel_ref.r = indi.reference_acceleration.err_r * attitude_error_r
- indi.reference_acceleration.rate_r * body_rates.r;
indi.angular_accel_ref.r = indi.reference_acceleration.err_r * attitude_error_r;

/*
* 5. Update the For each axis: delta_command = 1/control_effectiveness * (angular_acceleration_reference – angular_acceleration)
Expand All @@ -247,7 +279,7 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,
//(they have significant inertia, see the paper mentioned in the header for more explanation)
indi.du.p = 1.0f / indi.g1.p * (indi.angular_accel_ref.p - indi.rate_d[0]);
indi.du.q = 1.0f / indi.g1.q * (indi.angular_accel_ref.q - indi.rate_d[1]);
indi.du.r = 1.0f / (indi.g1.r + indi.g2) * (indi.angular_accel_ref.r - indi.rate_d[2] + indi.g2 * indi.du.r);
indi.du.r = 1.0f / (indi.g1.r - indi.g2) * (indi.angular_accel_ref.r - indi.rate_d[2] - indi.g2 * indi.du.r);


/*
Expand Down Expand Up @@ -299,6 +331,7 @@ void controllerINDI(control_t *control, setpoint_t *setpoint,
control->roll = indi.u_in.p;
control->pitch = indi.u_in.q;
control->yaw = indi.u_in.r;

}

PARAM_GROUP_START(ctrlINDI)
Expand All @@ -322,6 +355,7 @@ PARAM_ADD(PARAM_FLOAT, act_dyn_q, &indi.act_dyn.q)
PARAM_ADD(PARAM_FLOAT, act_dyn_r, &indi.act_dyn.r)
PARAM_ADD(PARAM_FLOAT, filt_cutoff, &indi.filt_cutoff)
PARAM_ADD(PARAM_FLOAT, filt_cutoff_r, &indi.filt_cutoff_r)
PARAM_ADD(PARAM_FLOAT, outerLoopActive, &outerLoopActive)
PARAM_GROUP_STOP(ctrlINDI)

LOG_GROUP_START(ctrlINDI)
Expand Down
Loading