tom.rudnick
Member
Hey,
I'm currently working on programming my own flightcontroller and everything worked fine so far. A few days ago I did the first flying tests and now I wanted to do write some data to a SD Card in order to do a FFT and check if their are any vibrations (Got some oscillations and I want to know if I need to soft mount the gyro).
I use the following Hardware:
- Teensy3.6
- Frsky r-xsr receiver (SBUS) connected to PIN 7
- MPU6050 (i2c) connected to PIN 18, 19
- ESCs are controlled by oneshot125 connected to 20,21,22,23 using FTM0
- SD Card is a SanDisk Ultra micro SD 32GB using the Builtin SD Card module
Now let's come to the problem. When capturing data and writing it to the SD Card sometimes a variable of the calculated GyroValue overflows (not the Raw Gyro value) and that of course leads to a high PID output and lastly to a crash.
Again, this only happens when I capture data. After some hours of debugging I found out if I disable the SBUS receiver and set the receiver values to constant values this error doesn't occur at all.
Here's the piece of code that does reproduce the error:
I'm 99.9% PID.h is not the Problem but maybe I miss something so here it is:
PID.CPP
The loop of the flightcontroller is of course changed in order to capture 5 mins of data. A piece of data looks like this:
If you now comment out the readSBUS() function everything works as expected and you don't get any overflows.
Does anybody know why this happens and how to resolve this issue? Or am I just dump and did a mistake?
Thanks in advance, feel free to correct my bad English
I'm currently working on programming my own flightcontroller and everything worked fine so far. A few days ago I did the first flying tests and now I wanted to do write some data to a SD Card in order to do a FFT and check if their are any vibrations (Got some oscillations and I want to know if I need to soft mount the gyro).
I use the following Hardware:
- Teensy3.6
- Frsky r-xsr receiver (SBUS) connected to PIN 7
- MPU6050 (i2c) connected to PIN 18, 19
- ESCs are controlled by oneshot125 connected to 20,21,22,23 using FTM0
- SD Card is a SanDisk Ultra micro SD 32GB using the Builtin SD Card module
Now let's come to the problem. When capturing data and writing it to the SD Card sometimes a variable of the calculated GyroValue overflows (not the Raw Gyro value) and that of course leads to a high PID output and lastly to a crash.
Again, this only happens when I capture data. After some hours of debugging I found out if I disable the SBUS receiver and set the receiver values to constant values this error doesn't occur at all.
Here's the piece of code that does reproduce the error:
Code:
#include "pid.h"
#include <i2c_t3.h>
#include <SD.h>
#include <SPI.h>
constexpr uint8_t throttle = 0;
constexpr uint8_t pitch = 2;
constexpr uint8_t roll = 1;
constexpr uint8_t yaw = 3;
constexpr uint8_t switch1 = 4;
constexpr uint8_t switch2 = 5;
constexpr uint8_t GyroScale = 0x1B;
constexpr uint8_t MPU_ADDR = 0x68;
constexpr float idle_throttle_percentage = 4.5;
constexpr uint16_t idle_throttle_value = 125.f + 125.f / 100.f * idle_throttle_percentage;
enum QuadState { notArmed, armed, failsafe };
bool blockArming = false;
uint8_t quadStatus = QuadState::notArmed;
float gyro_x_raw, gyro_y_raw, gyro_z_raw; // variables for gyro raw data
float gyro_pitch, gyro_roll, gyro_yaw;
float gyro_x_bias, gyro_y_bias, gyro_z_bias;
constexpr int biasCalculations = 1000;
uint8_t sbus_buffer[25];
uint8_t bufferIndex = 0;
uint16_t sbus_channels[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
uint16_t rc_values[8] = { 0,0,0,0,0,0,0,0 };
double setpoint[4] = { 0,0,0,0 };
double rate = 3.2;
double expo = 0.3;
uint8_t esc_1 = 125;
uint8_t esc_2 = 125;
uint8_t esc_3 = 125;
uint8_t esc_4 = 125;
constexpr double pitch_p = 0.05, pitch_i = 0.2, pitch_d = 0.00001;
constexpr double roll_p = 0.05, roll_i = 0.2, roll_d = 0.00001;
constexpr double yaw_p = 0.05, yaw_i = 0.01, yaw_d = 0;
float pitch_pid, roll_pid, yaw_pid;
PID pitch_pid_c = PID();
PID roll_pid_c = PID();
PID yaw_pid_c = PID();
uint32_t timeAfterTakeoff;
bool isQuadLiftedTimer = false;
bool set_I_toZero = false;
bool isQuadLifted = false;
uint32_t takeOffTime = 1000; //in ms
uint32_t nowTime, previousTime;
double dTime;
double deltaTimeS;
float pitch_dError, pitch_errorSum, pitch_error;
float roll_dError, roll_errorSum, roll_error;
float yaw_dError, yaw_errorSum, yaw_error;
float previous_pitch_error, previous_roll_error, previous_yaw_error;
/* MOTOR Konfiguration und Pin Belegung
*
* 23 20
* 4 1
* \ /
* \ _ /
* / \
* / \
* 3 2
* 22 21
*
*
* 1 = esc_1
* 2 = esc_2
* 3 = esc_3
* 4 = esc_4
*
*
*/
bool highpitch = false;
long beforeTime, afterTime;
constexpr bool blackboxActive = true;
bool blackboxSDworked = false;
constexpr int blackboxupdaterate = 1000; //in hz
constexpr float blackboxupdatetime = (1.0f / blackboxupdaterate) * pow(10, 6); // 1 / 1000 * 10^6
long blackboxCurrentTime = 0;
long blackboxLastTime = 0;
File blackboxFile;
bool allreadyStopped = false;
void setup() {
//if(blackboxActive)
prepareBlackbox();
delay(2000);
Serial.begin(115200);
Serial3.begin(100000);
delay(2000);
setupMotors();
intializeGyro();
calcBiasValues();
calcBiasValues();
//delay(1000);
pinMode(24, OUTPUT);
pinMode(25, OUTPUT);
//timeT = millis();
pitch_pid_c.init(pitch_p, pitch_i, pitch_d);
roll_pid_c.init(roll_p, roll_i, roll_d);
yaw_pid_c.init(yaw_p, yaw_i, yaw_d);
nowTime = millis();
previousTime = nowTime;
}
void loop() {
if(300000 >= millis())
{
readGyroData();
calcGyroValues();
readSBUS();
convertSBusToServoSignal();
/*rc_values[throttle] = 1000;
rc_values[pitch] = 1500;
rc_values[roll] = 1500;
rc_values[yaw] = 1500;*/
calculateSetpoint();
pidController();
writeMotorValues();
//Serial.println(setpoint[pitch]);
writeBlackbox();
}
else
{
if(!allreadyStopped)
{
blackboxFile.close();
Serial.println("stop");
allreadyStopped = true;
}
}
}
void pidController()
{
nowTime = micros();
dTime = nowTime - previousTime;
previousTime = nowTime;
deltaTimeS = dTime / 1000000.0;
if (setpoint[throttle] < idle_throttle_value)
{
setpoint[throttle] = idle_throttle_value;
}
pitch_pid = pitch_pid_c.pidCalc(setpoint[pitch], gyro_pitch, deltaTimeS);
roll_pid = roll_pid_c.pidCalc(setpoint[roll], gyro_roll, deltaTimeS);
yaw_pid = yaw_pid_c.pidCalc(setpoint[yaw], gyro_yaw, deltaTimeS);
uint32_t esc_1_tmp = setpoint[throttle] + pitch_pid + roll_pid - yaw_pid;
uint32_t esc_2_tmp = setpoint[throttle] - pitch_pid + roll_pid + yaw_pid;
uint32_t esc_3_tmp = setpoint[throttle] - pitch_pid - roll_pid - yaw_pid;
uint32_t esc_4_tmp = setpoint[throttle] + pitch_pid - roll_pid + yaw_pid;
esc_1 = minMax(esc_1_tmp, idle_throttle_value, 250);
esc_2 = minMax(esc_2_tmp, idle_throttle_value, 250);
esc_3 = minMax(esc_3_tmp, idle_throttle_value, 250);
esc_4 = minMax(esc_4_tmp, idle_throttle_value, 250);
}
void writeMotorValues()
{
uint16_t esc_1_analog = map(esc_1, 125, 250, 256, 512);
uint16_t esc_2_analog = map(esc_2, 125, 250, 256, 512);
uint16_t esc_3_analog = map(esc_3, 125, 250, 256, 512);
uint16_t esc_4_analog = map(esc_4, 125, 250, 256, 512);
analogWrite(20, esc_1_analog);
analogWrite(21, esc_2_analog);
analogWrite(22, esc_3_analog);
analogWrite(23, esc_4_analog);
}
void turnOffAllMotors()
{
esc_1 = 125;
esc_2 = 125;
esc_3 = 125;
esc_4 = 125;
writeMotorValues();
}
void setupMotors()
{
analogWriteFrequency(22, 500); //Da nur Pins von FTM0 genutzt werden, ändert sich für die anderen
//Pins die Frequenz gleich mit
analogWriteResolution(12); // 2^12 1us = 2,048 also 125 * 2,048 = 256
turnOffAllMotors(); //Safety Reasons
}
float convertInputToDegrees(uint16_t input)
{
double f_factor = (double)1 / (double)20000;
//todo super rate needs to be added
double degreeValue = f_factor * rate * (input - 1500) * abs(f_factor * pow((input - 1500), 3)) + expo * (input - 1500);
return degreeValue;
}
void calculateSetpoint()
{
setpoint[throttle] = map(rc_values[throttle], 1000, 2000, 125, 250);
setpoint[pitch] = convertInputToDegrees(rc_values[pitch]);
setpoint[roll] = convertInputToDegrees(rc_values[roll]);
setpoint[yaw] = convertInputToDegrees(rc_values[yaw]);
}
void convertSBusToServoSignal()
{
rc_values[throttle] = map(sbus_channels[throttle], 172, 1811, 1000, 2000);
rc_values[pitch] = map(sbus_channels[pitch], 172, 1811, 1000, 2000);
rc_values[roll] = map(sbus_channels[roll], 172, 1811, 1000, 2000);
rc_values[yaw] = map(sbus_channels[yaw], 172, 1811, 1000, 2000);
rc_values[switch1] = map(sbus_channels[switch1], 172, 1811, 1000, 2000);
rc_values[switch2] = map(sbus_channels[switch2], 172, 1811, 1000, 2000);
}
void readGyroData()
{
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x43); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
Wire.requestFrom(MPU_ADDR, 3 * 2, true); // request a total of 7*2=14 registers
int16_t gyro_x_raw_i = Wire.read() << 8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
int16_t gyro_y_raw_i = Wire.read() << 8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
int16_t gyro_z_raw_i = Wire.read() << 8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)
gyro_x_raw = float(gyro_x_raw_i);
gyro_y_raw = float(gyro_y_raw_i);
gyro_z_raw = float(gyro_z_raw_i);
}
void readSBUS()
{
if (Serial3.available() >= 25)
{
bufferIndex = 0;
while (Serial3.available() > 0) {
uint8_t data = Serial3.read();
sbus_buffer[bufferIndex] = data;
bufferIndex++;
}
if (bufferIndex == 25)
{
if (sbus_buffer[0] == 0x0f && sbus_buffer[24] == 0x00)
{
sbus_channels[0] = ((sbus_buffer[1] | sbus_buffer[2] << 8) & 0x07FF);
sbus_channels[1] = ((sbus_buffer[2] >> 3 | sbus_buffer[3] << 5) & 0x07FF);
sbus_channels[2] = ((sbus_buffer[3] >> 6 | sbus_buffer[4] << 2 | sbus_buffer[5] << 10) & 0x07FF);
sbus_channels[3] = ((sbus_buffer[5] >> 1 | sbus_buffer[6] << 7) & 0x07FF);
sbus_channels[4] = ((sbus_buffer[6] >> 4 | sbus_buffer[7] << 4) & 0x07FF);
sbus_channels[5] = ((sbus_buffer[7] >> 7 | sbus_buffer[8] << 1 | sbus_buffer[9] << 9) & 0x07FF);
}
}
}
}
void calcGyroValues()
{
gyro_pitch = -1.0 * ((gyro_x_raw - gyro_x_bias) / 16.4);
gyro_roll = -1.0 * ((gyro_y_raw - gyro_y_bias) / 16.4);
gyro_yaw = (gyro_z_raw - gyro_z_bias) / 16.4;
}
void calcBiasValues()
{
int32_t gyro_x_tmp = 0;
int32_t gyro_y_tmp = 0;
int32_t gyro_z_tmp = 0;
for (int i = 0; i < biasCalculations; i++)
{
readGyroData();
gyro_x_tmp += gyro_x_raw;
gyro_y_tmp += gyro_y_raw;
gyro_z_tmp += gyro_z_raw;
}
gyro_x_bias = gyro_x_tmp / biasCalculations;
gyro_y_bias = gyro_y_tmp / biasCalculations;
gyro_z_bias = gyro_z_tmp / biasCalculations;
}
void intializeGyro()
{
Wire.begin(I2C_MASTER, MPU_ADDR, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x6B);
Wire.write(0x01);
Wire.endTransmission(true);
Wire.beginTransmission(MPU_ADDR);
Wire.write(GyroScale);
Wire.write(0x18);
Wire.endTransmission(true);
/*Wire.beginTransmission(MPU_ADDR);
Wire.write(0x1A); // Digital Low Pass Filter
Wire.write(6);
Wire.endTransmission(true);*/
}
float minMax(float value, float min, float max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
bool prepareBlackbox()
{
if(!SD.begin(BUILTIN_SDCARD))
return false;
blackboxFile = SD.open("log.txt", FILE_WRITE);
if(blackboxFile)
{
blackboxFile.println("deltaTime;Gpr;Grr;Gyr;Gp;Gr;Gy;esc1;esc2;esc3;esc4;pidp;pidr;pidy;throttle;pitch;roll;yaw");
return true;
}
else
return false;
blackboxCurrentTime = 0;
}
void writeBlackbox()
{
blackboxCurrentTime = micros();
blackboxFile.print(blackboxCurrentTime - blackboxLastTime); blackboxFile.print(";");
Serial.println(blackboxCurrentTime-blackboxLastTime);
blackboxFile.print(gyro_x_raw); blackboxFile.print(";");
blackboxFile.print(gyro_y_raw); blackboxFile.print(";");
blackboxFile.print(gyro_z_raw); blackboxFile.print(";");
blackboxFile.print(gyro_pitch); blackboxFile.print(";");
blackboxFile.print(gyro_roll); blackboxFile.print(";");
blackboxFile.print(gyro_yaw); blackboxFile.print(";");
blackboxFile.print(esc_1); blackboxFile.print(";");
blackboxFile.print(esc_2); blackboxFile.print(";");
blackboxFile.print(esc_3); blackboxFile.print(";");
blackboxFile.print(esc_4); blackboxFile.print(";");
blackboxFile.print(pitch_pid); blackboxFile.print(";");
blackboxFile.print(roll_pid); blackboxFile.print(";");
blackboxFile.print(yaw_pid); blackboxFile.print(";");
blackboxFile.print(setpoint[throttle]); blackboxFile.print(";");
blackboxFile.print(setpoint[pitch]); blackboxFile.print(";");
blackboxFile.print(setpoint[roll]); blackboxFile.print(";");
blackboxFile.print(setpoint[yaw]); blackboxFile.println(";");
blackboxFile.flush();
blackboxLastTime = blackboxCurrentTime;
}
I'm 99.9% PID.h is not the Problem but maybe I miss something so here it is:
Code:
// pid.h
#ifndef _PID_h
#define _PID_h
class PID {
public:
PID();
PID(float _kp, float _ki, float _kd);
void init(float _kp, float _ki, float _kd);
float pidCalc(float setpoint, float position, double dt);
void setKP(float _kp);
void setKI(float _ki);
void setKD(float _kd);
void resetErrorSum();
float getErrorSum();
private:
float dError, errorSum, error;
float previousError = 0;
float kp, ki, kd;
};
#endif
PID.CPP
Code:
//
//
//
#include "pid.h"
PID::PID()
{
}
PID::PID(float _kp, float _ki, float _kd)
{
kp = _kp;
ki = _ki;
kd = _kd;
}
void PID::init(float _kp, float _ki, float _kd)
{
kp = _kp;
ki = _ki;
kd = _kd;
}
float PID::pidCalc(float setpoint, float position, double dt)
{
error = (int)position - (int)setpoint;
errorSum += error * dt;
dError = kd * (error - previousError) / dt;
previousError = error;
//
return kp * error + ki * errorSum + dError;
}
void PID::setKP(float _kp)
{
kp = _kp;
}
void PID::setKI(float _ki)
{
ki = _ki;
}
void PID::setKD(float _kd)
{
kd = _kd;
}
void PID::resetErrorSum()
{
errorSum = 0;
}
float PID::getErrorSum()
{
return errorSum;
}
The loop of the flightcontroller is of course changed in order to capture 5 mins of data. A piece of data looks like this:
If you now comment out the readSBUS() function everything works as expected and you don't get any overflows.
Does anybody know why this happens and how to resolve this issue? Or am I just dump and did a mistake?
Thanks in advance, feel free to correct my bad English