Error fixes and base flight logic

This commit is contained in:
2024-04-03 20:38:44 +07:00
parent 47f91eeae5
commit 0cb666c860
13 changed files with 113 additions and 61 deletions

View File

@@ -21,6 +21,7 @@ FlightController::FlightController(
_status = DeviceStatus::Idle; _status = DeviceStatus::Idle;
_checkBatteryCharge(); _checkBatteryCharge();
stopAllRotors(); stopAllRotors();
_initPidControllers();
ESP_LOGI(_tag, "Flight controller initialized"); ESP_LOGI(_tag, "Flight controller initialized");
} }
@@ -35,9 +36,7 @@ FlightController::~FlightController() {
void FlightController::tick() { void FlightController::tick() {
bool hasUpdates = _sensors->tick(); bool hasUpdates = _sensors->tick();
if (hasUpdates) {
_checkBatteryCharge(); _checkBatteryCharge();
}
// TODO: don't apply pid values to motors while DeviceStatus != IsFlying // TODO: don't apply pid values to motors while DeviceStatus != IsFlying
switch (_status) { switch (_status) {
case DeviceStatus::Idle: case DeviceStatus::Idle:
@@ -50,15 +49,23 @@ void FlightController::tick() {
break; break;
case DeviceStatus::IsBoarding: case DeviceStatus::IsBoarding:
/* TODO: implement boarding */ /* TODO: implement boarding */
if (hasUpdates) {
_boarding(); _boarding();
}
break; break;
case DeviceStatus::IsFlying: case DeviceStatus::IsFlying:
/* TODO: implement flying */ /* TODO: implement flying */
if (hasUpdates) {
_tailRotorController->tick();
}
break; break;
} }
if (hasUpdates and (_status == DeviceStatus::IsFlying or _status == DeviceStatus::IsBoarding)) {
_tailRotorController->input = round(_sensors->mpuData().pitch * 10) / 10;
_tailRotorController->update();
_heightController->input = _sensors->flightHeight();
auto baseDuty = (uint32_t) round(_heightController->getResultNow());
_rotor1->setDuty(baseDuty);
_rotor2->setDuty(baseDuty);
}
} }
ReportedDeviceState FlightController::currentDeviceState() const { ReportedDeviceState FlightController::currentDeviceState() const {
@@ -106,15 +113,15 @@ void FlightController::_takeoff() {
// TODO: implement takeoff // TODO: implement takeoff
_status = DeviceStatus::IsFlying; _status = DeviceStatus::IsFlying;
_tailRotorController->enable(); _tailRotorController->enable();
_heightController->integral = 0.f;
_heightController->setpoint = _defaultFlightHeight;
} }
void FlightController::_boarding() { void FlightController::_boarding() {
if (_sensors->flightHeight() <= _defaultStopMotorHeight) { if (_sensors->flightHeight() <= _defaultStopMotorHeight) {
// TODO: stop motors and setpoints stopAllRotors();
_status = DeviceStatus::Idle;
_tailRotorController->disable();
} else { } else {
// TODO: implement boarding _heightController->setpoint -= _boarding_speed;
} }
} }
@@ -167,6 +174,7 @@ void FlightController::setRotor3Duty(int16_t rotor3Duty) {
} }
void FlightController::stopAllRotors() { void FlightController::stopAllRotors() {
_tailRotorController->disable();
setRotor1Duty(0); setRotor1Duty(0);
setRotor2Duty(0); setRotor2Duty(0);
setRotor3Duty(0); setRotor3Duty(0);
@@ -198,3 +206,9 @@ PidSettings FlightController::pidSettings() const {
settings.pitchController = {.p = _tailRotorController->Kp, .i = _tailRotorController->Ki, .d = _tailRotorController->Kd}; settings.pitchController = {.p = _tailRotorController->Kp, .i = _tailRotorController->Ki, .d = _tailRotorController->Kd};
return settings; return settings;
} }
void FlightController::_initPidControllers() {
_tailRotorController->setpoint = 180.f;
_heightController->setLimits(0, _rotor1->maxDuty());
}

View File

@@ -76,9 +76,15 @@ public:
private: private:
void _checkBatteryCharge(); void _checkBatteryCharge();
[[deprecated]] void _takeoff(); void _initPidControllers();
[[deprecated]] void _boarding(); void _updateSetpoints();
void _updatePidControllers();
void _takeoff();
void _boarding();
Sensors *_sensors = nullptr; Sensors *_sensors = nullptr;
SavedPidRegulator *_heightController = nullptr; SavedPidRegulator *_heightController = nullptr;
@@ -87,12 +93,17 @@ private:
BrushedMotor *_rotor1 = nullptr; BrushedMotor *_rotor1 = nullptr;
BrushedMotor *_rotor2 = nullptr; BrushedMotor *_rotor2 = nullptr;
DeviceStatus _status; DeviceStatus _status;
//uint32_t _flightTimer;
//float _verticalVelocityOffset = 0.f; // [cm/10 ms]
//float _pitchVelocityOffset = 0.f; // [degrees/10 ms]
static constexpr const char *_tag = "FlightController"; static constexpr const char *_tag = "FlightController";
static constexpr uint8_t _batteryCriticalCharge = 15; // [%] static constexpr uint8_t _batteryCriticalCharge = 15; // [%]
static constexpr float _defaultFlightHeight = 30.f; // [cm] static constexpr float _defaultFlightHeight = 55.f; // [cm]
static constexpr float _defaultHorizontAngle = 90.f; // [degrees] static constexpr float _defaultHorizontAngle = 180.f; // [degrees]
static constexpr float _defaultStopMotorHeight = 5; // [cm] static constexpr float _boarding_speed = 0.1; // [cm/10 ms]
static constexpr float _defaultStopMotorHeight = 15; // [cm]
bool _unlimitedBattery = false; // for debug purposes bool _unlimitedBattery = false; // for debug purposes
}; };

View File

@@ -100,14 +100,14 @@ void FlightDispatcher::_onNewMessageReceived(char *package) {
case SH("p2"): case SH("p2"):
if (pair.type() == gson::Type::Object and pair.includes(SH("p")) if (pair.type() == gson::Type::Object and pair.includes(SH("p"))
and pair.includes(SH("i")) and pair.includes(SH("d"))) { and pair.includes(SH("i")) and pair.includes(SH("d"))) {
_flightController->setYawControllerParams( _flightController->setPitchControllerParams(
pair[SH("p")].toFloat(), pair[SH("i")].toFloat(), pair[SH("d")].toFloat()); pair[SH("p")].toFloat(), pair[SH("i")].toFloat(), pair[SH("d")].toFloat());
} }
break; break;
case SH("p3"): case SH("p3"):
if (pair.type() == gson::Type::Object and pair.includes(SH("p")) if (pair.type() == gson::Type::Object and pair.includes(SH("p"))
and pair.includes(SH("i")) and pair.includes(SH("d"))) { and pair.includes(SH("i")) and pair.includes(SH("d"))) {
_flightController->setPitchControllerParams( _flightController->setYawControllerParams(
pair[SH("p")].toFloat(), pair[SH("i")].toFloat(), pair[SH("d")].toFloat()); pair[SH("p")].toFloat(), pair[SH("i")].toFloat(), pair[SH("d")].toFloat());
} }
break; break;
@@ -146,21 +146,21 @@ void FlightDispatcher::_pidSettingsOpened() {
pkg.beginObj(); pkg.beginObj();
pkg.beginObj("p1"); pkg.beginObj("p1");
pkg["p"] = settings.flightController.p; pkg.addFloat("p", settings.flightController.p, 5);
pkg["i"] = settings.flightController.i; pkg.addFloat("i", settings.flightController.i, 5);
pkg["d"] = settings.flightController.d; pkg.addFloat("d", settings.flightController.d, 5);
pkg.endObj(); pkg.endObj();
pkg.beginObj("p2"); pkg.beginObj("p2");
pkg["p"] = settings.yawController.p; pkg.addFloat("p", settings.pitchController.p, 5);
pkg["i"] = settings.yawController.i; pkg.addFloat("i", settings.pitchController.i, 5);
pkg["d"] = settings.yawController.d; pkg.addFloat("d", settings.pitchController.d, 5);
pkg.endObj(); pkg.endObj();
pkg.beginObj("p3"); pkg.beginObj("p3");
pkg["p"] = settings.pitchController.p; pkg.addFloat("p", settings.yawController.p, 5);
pkg["i"] = settings.pitchController.i; pkg.addFloat("i", settings.yawController.i, 5);
pkg["d"] = settings.pitchController.d; pkg.addFloat("d", settings.yawController.d, 5);
pkg.endObj(); pkg.endObj();
pkg.endObj(); pkg.endObj();

View File

@@ -14,8 +14,8 @@ public:
assert(rotor != nullptr); assert(rotor != nullptr);
_rotor = rotor; _rotor = rotor;
_isEnabled = isEnabled; _isEnabled = isEnabled;
this->_dt = dt; setLimits(-_rotor->maxDuty(), _rotor->maxDuty());
setLimits(0, _rotor->maxDuty()); setDirection(REVERSE);
_pid_timer = millis(); _pid_timer = millis();
ESP_LOGI(_tag, "PID %s initialized with params (%f, %f, %f)", name, Kp, Ki, Kd); ESP_LOGI(_tag, "PID %s initialized with params (%f, %f, %f)", name, Kp, Ki, Kd);
} }
@@ -23,6 +23,7 @@ public:
void enable() { void enable() {
_isEnabled = true; _isEnabled = true;
_rotor->setDuty(0); _rotor->setDuty(0);
integral = 0;
} }
void disable() { void disable() {
@@ -30,10 +31,12 @@ public:
_rotor->setDuty(0); _rotor->setDuty(0);
} }
void tick() { void update() {
if (millis() - _pid_timer >= _dt and _isEnabled) { if (_isEnabled) {
_rotor->setDuty((uint16_t) getResultTimer()); //ESP_LOGI(_tag, "Setpoint: %f", setpoint);
_pid_timer = millis(); //ESP_LOGI(_tag, "Input: %f", input);
//ESP_LOGI(_tag, "Output: %d", (int16_t) round(getResultNow()));
setRotorDuty((int16_t) round(getResultNow()));
} }
} }
@@ -43,7 +46,7 @@ public:
} }
private: private:
uint16_t _dt; //uint16_t _dt;
BrushedMotor *_rotor = nullptr; BrushedMotor *_rotor = nullptr;
uint32_t _pid_timer = 0; uint32_t _pid_timer = 0;
bool _isEnabled = false; bool _isEnabled = false;

View File

@@ -23,6 +23,10 @@ void BrushedMotor::setDuty(uint32_t duty) const {
ledcWrite(_channel, duty); ledcWrite(_channel, duty);
} }
void BrushedMotor::decrement(uint32_t k) const {
ledcWrite(_channel, ledcRead(_channel) - k);
}
void BrushedMotor::setExtendedDuty(int32_t duty) { void BrushedMotor::setExtendedDuty(int32_t duty) {
if (duty > 0) { if (duty > 0) {
forward(); forward();

View File

@@ -27,6 +27,8 @@ public:
void coast(); void coast();
void decrement(uint32_t k) const;
private: private:
uint8_t _channel; uint8_t _channel;
uint32_t _frequency; uint32_t _frequency;

View File

@@ -16,6 +16,7 @@ BluetoothDispatcher::BluetoothDispatcher(BluetoothSerial *controller, const char
} }
bool BluetoothDispatcher::initialize(int readTimeoutMS) { bool BluetoothDispatcher::initialize(int readTimeoutMS) {
bool success = _controller->begin(_device_name, false);
_controller->enableSSP(); _controller->enableSSP();
_controller->onConfirmRequest([this](uint16_t pin) { _controller->onConfirmRequest([this](uint16_t pin) {
_onConfirmRequest(pin); _onConfirmRequest(pin);
@@ -27,8 +28,7 @@ bool BluetoothDispatcher::initialize(int readTimeoutMS) {
deviceConnectedCallback = [this](BTAddress device) { deviceConnectedCallback = [this](BTAddress device) {
_onDeviceConnected(device); _onDeviceConnected(device);
}; };
_controller->setTimeout(readTimeoutMS); //_controller->setTimeout(readTimeoutMS);
bool success = _controller->begin(_device_name, false);
if (!success) { if (!success) {
ESP_LOGI(_tag, "Failed to initialize Bluetooth controller!"); ESP_LOGI(_tag, "Failed to initialize Bluetooth controller!");
assert(false); assert(false);
@@ -81,6 +81,7 @@ void BluetoothDispatcher::_onAuthComplete(boolean success) const {
void BluetoothDispatcher::_onDeviceConnected(BTAddress device) { void BluetoothDispatcher::_onDeviceConnected(BTAddress device) {
ESP_LOGI(_tag, "New device connected: %s", device.toString(true).c_str()); ESP_LOGI(_tag, "New device connected: %s", device.toString(true).c_str());
_buffer.clear(); _buffer.clear();
_controller->flush();
if (_deviceConnectedCallback) { if (_deviceConnectedCallback) {
_deviceConnectedCallback(device); _deviceConnectedCallback(device);
} }
@@ -92,12 +93,9 @@ void BluetoothDispatcher::onNewDeviceConnected(DeviceConnectedCb deviceConnected
} }
void BluetoothDispatcher::sendPackage(const char *package) { void BluetoothDispatcher::sendPackage(const char *package) {
if (!_controller->connected()) if (!_controller->hasClient())
return; return;
auto res = _controller->println(package); _controller->println(package);
if (res == 0) {
delay(10);
}
} }

View File

@@ -1,6 +1,8 @@
// This is a personal academic project. Dear PVS-Studio, please check it. // This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: https://pvs-studio.com // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: https://pvs-studio.com
#define TX_QUEUE_SIZE 512
#include "BluetoothSerial.h" #include "BluetoothSerial.h"
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "esp_log.h" #include "esp_log.h"

View File

@@ -7,6 +7,7 @@
#include "esp_log.h" #include "esp_log.h"
static volatile bool _isDMPDataReady = false; static volatile bool _isDMPDataReady = false;
static void IRAM_ATTR _dmpInterruption() { static void IRAM_ATTR _dmpInterruption() {
_isDMPDataReady = true; _isDMPDataReady = true;
} }
@@ -14,7 +15,7 @@ static void IRAM_ATTR _dmpInterruption() {
typedef std::function<void()> OnCalibrationFinishedCb; typedef std::function<void()> OnCalibrationFinishedCb;
class MPU { class MPU {
public: public:
MPU(MPU6050_6Axis_MotionApps20 *mpu) { MPU(MPU6050_6Axis_MotionApps20 *mpu) {
assert(mpu != nullptr); assert(mpu != nullptr);
_mpu = mpu; _mpu = mpu;
@@ -39,7 +40,7 @@ class MPU {
return false; return false;
} }
_mpu->setDLPFMode(MPU6050_DLPF_BW_10); // 10 Hz bandwidth _mpu->setDLPFMode(MPU6050_DLPF_BW_10); // 10 Hz bandwidth
//mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_500); // set sensivity, not recomended //mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_500); // set sensivity, not recommended
//mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_8); //mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_8);
if (_mpu->dmpInitialize()) { if (_mpu->dmpInitialize()) {
@@ -99,6 +100,11 @@ class MPU {
_prOffset[0] /= _calibrationsIterCounter; _prOffset[0] /= _calibrationsIterCounter;
_prOffset[1] /= _calibrationsIterCounter; _prOffset[1] /= _calibrationsIterCounter;
_prOffset[0] = DEG_TO_RAD * (180 - RAD_TO_DEG * _prOffset[0]);
_prOffset[1] = DEG_TO_RAD * (180 - RAD_TO_DEG * _prOffset[1]);
ESP_LOGI(_TAG, "Calibration offsets: roll: %f, pitch: %f", RAD_TO_DEG * _prOffset[1],
RAD_TO_DEG * _prOffset[0]);
_isCalibration = false; _isCalibration = false;
_updateOffsets(); _updateOffsets();
_calibrationsIterCounter = 0; _calibrationsIterCounter = 0;
@@ -113,9 +119,11 @@ class MPU {
_gxOffset += _gx; _gxOffset += _gx;
_gyOffset += _gy; _gyOffset += _gy;
_gzOffset += _gz; _gzOffset += _gz;
_prOffset[0] += _ypr[1]; _prOffset[0] += pitch() * DEG_TO_RAD;
_prOffset[1] += _ypr[2]; _prOffset[1] += roll() * DEG_TO_RAD;
_calibrationsIterCounter++; _calibrationsIterCounter++;
ESP_LOGI(_TAG, "Angles on calibration: roll: %f, pitch: %f", roll(),
pitch());
} }
} }
@@ -126,21 +134,24 @@ class MPU {
float accZInertial() const { float accZInertial() const {
return _AccZInertial; return _AccZInertial;
} }
float yaw() const { float yaw() const {
return degrees(_ypr[0]); return degrees(_ypr[0]) + 180.f;
} }
float pitch() const { float pitch() const {
if (_isCalibration) { if (_isCalibration) {
return degrees(_ypr[1]); return 360.f - (degrees(_ypr[1]) + 180);
} else { } else {
return degrees(_ypr[1] - _prOffset[0]); return 360.f - (degrees(_ypr[1]) + 180) + degrees(_prOffset[0]);
} }
} }
float roll() const { float roll() const {
if (_isCalibration) { if (_isCalibration) {
return degrees(_ypr[2]); return 360.f - (degrees(_ypr[2]) + 180);
} else { } else {
return degrees(_ypr[2] - _prOffset[1]); return 360.f - (degrees(_ypr[2] + _prOffset[1]) + 180) + degrees(_prOffset[1]);
} }
} }
@@ -151,6 +162,7 @@ class MPU {
return _ax - _axOffset; return _ax - _axOffset;
} }
} }
float ay() const { float ay() const {
if (_isCalibration) { if (_isCalibration) {
return _ay; return _ay;
@@ -158,6 +170,7 @@ class MPU {
return _ay - _ayOffset; return _ay - _ayOffset;
} }
} }
float az() const { float az() const {
if (_isCalibration) { if (_isCalibration) {
return _az; return _az;
@@ -177,6 +190,7 @@ class MPU {
return _gx - _gxOffset; return _gx - _gxOffset;
} }
} }
float gy() const { float gy() const {
if (_isCalibration) { if (_isCalibration) {
return _gy; return _gy;
@@ -184,6 +198,7 @@ class MPU {
return _gy - _gyOffset; return _gy - _gyOffset;
} }
} }
float gz() const { float gz() const {
if (_isCalibration) { if (_isCalibration) {
return _gz; return _gz;
@@ -200,9 +215,9 @@ class MPU {
_calibrationFinishedCb = callback; _calibrationFinishedCb = callback;
} }
private: private:
MPU6050_6Axis_MotionApps20 *_mpu = nullptr; MPU6050_6Axis_MotionApps20 *_mpu = nullptr;
float _ypr[3] = { 0 }; float _ypr[3] = {0};
float _ax = 0, _ay = 0, _az = 0; float _ax = 0, _ay = 0, _az = 0;
float _gx = 0, _gy = 0, _gz = 0; float _gx = 0, _gy = 0, _gz = 0;
float _gravity = 0; float _gravity = 0;
@@ -210,9 +225,9 @@ class MPU {
float _axOffset = 0, _ayOffset = 0, _azOffset = 0; float _axOffset = 0, _ayOffset = 0, _azOffset = 0;
float _gxOffset = 0, _gyOffset = 0, _gzOffset = 0; float _gxOffset = 0, _gyOffset = 0, _gzOffset = 0;
float _prOffset[2] = { 0 }; // yaw isn't used float _prOffset[2] = {0}; // yaw isn't used
uint8_t _fifoBuffer[45] = { 0 }; uint8_t _fifoBuffer[45] = {0};
Preferences _preferences; Preferences _preferences;
const char *_TAG = "MPU6050 module"; const char *_TAG = "MPU6050 module";
@@ -239,8 +254,9 @@ class MPU {
_gxOffset = _preferences.getFloat("gx", 0.f); _gxOffset = _preferences.getFloat("gx", 0.f);
_gyOffset = _preferences.getFloat("gy", 0.f); _gyOffset = _preferences.getFloat("gy", 0.f);
_gzOffset = _preferences.getFloat("gz", 0.f); _gzOffset = _preferences.getFloat("gz", 0.f);
_ypr[1] = _preferences.getFloat("pitch", 0.f); _prOffset[0] = _preferences.getFloat("pitch", 0.f);
_ypr[2] = _preferences.getFloat("roll", 0.f); _prOffset[1] = _preferences.getFloat("roll", 0.f);
ESP_LOGI(_TAG, "Offsets loaded: pitch: %f, roll %f", _prOffset[0], _prOffset[1]);
} }
void _updateOffsets() { void _updateOffsets() {
@@ -252,5 +268,6 @@ class MPU {
_preferences.putFloat("gz", _gzOffset); _preferences.putFloat("gz", _gzOffset);
_preferences.putFloat("pitch", _prOffset[0]); _preferences.putFloat("pitch", _prOffset[0]);
_preferences.putFloat("roll", _prOffset[1]); _preferences.putFloat("roll", _prOffset[1]);
ESP_LOGI(_TAG, "Offsets saved.");
} }
}; };

View File

@@ -20,7 +20,7 @@ RangingSensor::RangingSensor(TwoWire &i2c) {
} }
void RangingSensor::startFlight() { void RangingSensor::startFlight() {
_start_distance = _distance; //_start_distance = _distance;
} }
void RangingSensor::tick() { void RangingSensor::tick() {

View File

@@ -21,8 +21,8 @@ public:
private: private:
TwoWire *_i2c; TwoWire *_i2c;
VL53L0X _sensor; VL53L0X _sensor;
uint32_t _distance; // [cm] float _distance = 0; // [cm]
uint32_t _start_distance = 0; // [cm] float _start_distance = 0; // [cm]
static constexpr const char *_tag = "RangingSensor"; static constexpr const char *_tag = "RangingSensor";
constexpr static const int _MID_ARITM_WINDOW = 5; constexpr static const int _MID_ARITM_WINDOW = 5;

View File

@@ -54,6 +54,7 @@ void Sensors::measureBaseAltitudeSync(void) {
void Sensors::measureBaseAltitudeAsync(void) { void Sensors::measureBaseAltitudeAsync(void) {
if (_heightSensor == HeightSensor::RangeMeterOnly) { if (_heightSensor == HeightSensor::RangeMeterOnly) {
_ranging_sensor->startFlight();
if (_onMeasuaringAltitudeFinished) { if (_onMeasuaringAltitudeFinished) {
_onMeasuaringAltitudeFinished(); _onMeasuaringAltitudeFinished();
} }

View File

@@ -11,7 +11,7 @@ static Application *app = nullptr;
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
app = new Application(new FlightDispatcher( app = new Application(new FlightDispatcher(
new BluetoothDispatcher(new BluetoothSerial()), new BluetoothDispatcher(new BluetoothSerial(), "Redmi Note 8 Pro"),
new FlightController( new FlightController(
new Sensors(new Barometer(new GyverBME280(i2c)), new Sensors(new Barometer(new GyverBME280(i2c)),
new RangingSensor(i2c), new RangingSensor(i2c),
@@ -25,7 +25,7 @@ void setup() {
new BrushedMotor(26, 27, 30000, 1, 10), new BrushedMotor(26, 27, 30000, 1, 10),
new PIDController(0.6f, 3.5f, 0.03f, new PIDController(0.6f, 3.5f, 0.03f,
new BrushedMotor(25, 33, 30000, 3, 10), new BrushedMotor(25, 33, 30000, 3, 10),
"RollControl"), // pitch "PitchControl"), // pitch
true))); true)));
} }