Возможный шум от двигателей
Я пытаюсь управлять самобалансирующимся автомобилем, но кажется, что MPU6050 загрязняется, когда мои двигатели работают на полной скорости. Я управляю своими двигателями с помощью модуля L298N и с помощью ШИМ-сигнала (пины 5, 6, 9 и 10 от ардуино). Я читал о развязке с помощью конденсаторов, но точно не знаю, как мне это сделать.
Это мой код. Это немного сумбурно (извините за это, просто я все еще не понимаю, что происходит), а также изображение моего проекта:
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
#define MIN_ABS_SPEED 20
MPU6050 mpu;
#define DEBUG
//#определить OFFSET_1
//#определить СТОП
// переменные управления/состояния MPU
bool dmpReady = false; // устанавливаем true, если инициализация DMP прошла успешно
uint8_t mpuIntStatus; // содержит фактический байт состояния прерывания от MPU
uint8_t devStatus; // возвращаем статус после каждой операции с устройством (0 = успех, !0 = ошибка)
uint16_t packetSize; // ожидаемый размер пакета DMP (по умолчанию 42 байта)
uint16_t fifoCount; // подсчет всех байтов в настоящее время в FIFO
uint8_t fifoBuffer[64]; // Буфер хранения FIFO
// переменные ориентации/движения
Quaternion q; // [w, x, y, z] контейнер кватернионов
VectorFloat gravity; // [x, y, z] вектор гравитации
float euler[3]; // [пси, тета, фи] Контейнер угла Эйлера
//ПИД
// удвоить originalSetpoint = 175,8;
#ifdef OFFSET_1
double originalSetpoint = 225;
#else
double originalSetpoint = 184;
#endif
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output, output_left, output_right, error;
int moveState=0; //0 = баланс; 1 = спина; 2 = вперед
double Kp = 40;
double Kd = 0.05;
double Ki = 5;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
double motorSpeedFactorLeft = 1;
double motorSpeedFactorRight = 1;
double motorSpeedFactorForward = 1;
double motorSpeedFactorBackward = 1;
//КОНТРОЛЛЕР ДВИГАТЕЛЯ
int ENA = 5;
int IN1 = 6;
int IN2 = 7;
int IN3 = 8;
int IN4 = 9;
int ENB = 10;
//LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);
//таймеры
long time1Hz = 0;
long time5Hz = 0;
volatile bool mpuInterrupt = false; // указывает, перешел ли вывод прерывания MPU в высокий уровень
void dmpDataReady()
{
mpuInterrupt = true;
}
void setup()
{
// подключаемся к шине I2C (библиотека I2Cdev не делает этого автоматически)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // Установите тактовую частоту SCL на 400 кГц
//Wire.setClock(200000); // Установите тактовую частоту SCL на 400 кГц
//TWBR = 24; // Тактовая частота I2C 400 кГц (200 кГц, если процессор 8 МГц)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
//Fastwire::setup(200, true);
#endif
Serial.begin(115200);
mpu.initialize();
devStatus = mpu.dmpInitialize();
// укажите здесь собственные смещения гироскопа, масштабированные для минимальной чувствительности
#ifdef OFFSET_1
mpu.setXGyroOffset(0);
mpu.setYGyroOffset(0);
mpu.setZGyroOffset(0);
mpu.setZAccelOffset(16372);
mpu.setYAccelOffset(5);
mpu.setXAccelOffset(2);
#else
mpu.setXGyroOffset(-52);
mpu.setYGyroOffset(-12);
mpu.setZGyroOffset(53);
mpu.setZAccelOffset(2420);
mpu.setYAccelOffset(2881);
mpu.setXAccelOffset(-784);
#endif
// убедиться, что это сработало (возвращает 0, если это так)
if (devStatus == 0)
{
//mpu.CalibrateAccel(6);
//mpu.CalibrateGyro(6);
// включаем DMP, теперь, когда он готов
mpu.setDMPEnabled(true);
// включить обнаружение прерываний Arduino
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// устанавливаем наш флаг готовности DMP, чтобы основная функция loop() знала, что ее можно использовать
dmpReady = true;
// получаем ожидаемый размер пакета DMP для последующего сравнения
packetSize = mpu.dmpGetFIFOPacketSize();
pid.SetMode(AUTOMATIC);
pid.SetSampleTime(10);
pid.SetOutputLimits(-255, 255);
}
else
{
// ОШИБКА!
// 1 = первоначальная загрузка памяти не удалась
// 2 = не удалось обновить конфигурацию DMP
// (если он сломается, обычно код будет 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
}
void loop()
{
#ifdef STOP
return;
#endif
// если программирование не удалось, не пытайтесь ничего сделать
if (!dmpReady) return;
// ждем прерывания MPU или доступных дополнительных пакетов
while (!mpuInterrupt && fifoCount < packetSize)
{
pid.Compute();
output = 255;
output_left = output * motorSpeedFactorLeft;
output_right = output * motorSpeedFactorRight;
if(input > (setpoint))
{
output_left = output_left*motorSpeedFactorForward ;
output_right = output_right* motorSpeedFactorForward;
Move_Forward();
}
else if(input < (setpoint))
{
output_left = output_left*motorSpeedFactorBackward ;
output_right = output_right* motorSpeedFactorBackward;
Move_Backward();
}
else
{
Stop();
}
}
// сброс флага прерывания и получение байта INT_STATUS
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// получаем текущий счетчик FIFO
fifoCount = mpu.getFIFOCount();
// проверка на переполнение (это никогда не должно происходить, если только наш код не слишком неэффективен)
if ((mpuIntStatus & 0x10) || fifoCount == 1024)
{
// сбрасываем, чтобы мы могли продолжить работу без ошибок
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// в противном случае проверьте прерывание готовности данных DMP (это должно происходить часто)
}
else if (mpuIntStatus & 0x02)
{
// ожидание правильной доступной длины данных, должно быть ОЧЕНЬ короткое ожидание
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// читаем пакет из FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// отслеживаем количество FIFO здесь, если есть > 1 пакет в наличии
// (это позволяет нам сразу читать больше, не дожидаясь прерывания)
fifoCount -= packetSize;
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(euler, &q, &gravity);
input = (euler[1] * 180 / M_PI + 180);
error = input - setpoint;
// Serial.print("\tOutput: ");
// Serial.print(output);
Serial.print("\tError: ");
Serial.println(error);
}
}
void Move_Forward() // Код для вращения колеса вперед
{
//******ПРАВИЛЬНО******//
analogWrite(5, -1*output_right);
analogWrite(6, 0);
//******ОСТАВИЛ******//
analogWrite(9, -1*output_left);
analogWrite(10, 0);
digitalWrite(11, HIGH);
digitalWrite(12, LOW);
}
void Move_Backward() // Код для вращения колеса вперед
{
//******ПРАВИЛЬНО******//
analogWrite(5, LOW);
analogWrite(6, 1*output_right);
//******ОСТАВИЛ******//
analogWrite(9, 0);
analogWrite(10, 1*output_left);
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
}
void Stop() // Код для вращения колеса вперед
{
analogWrite(11, LOW);
analogWrite(12, LOW);
analogWrite(5, LOW);
analogWrite(6, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
}
@PartnerTech, 👍1
Обсуждение1 ответ
Лучший ответ:
Ну, должен сказать, что вначале я не думал, что договоренность, о которой упомянул @Majenko, сработает. Сначала я попытался подключить конденсатор кабелями, но он все равно дает мне шум. Как только я припаял его непосредственно к двигателям, шум значительно уменьшился.
Спасибо всем. Это изображение аранжировки, которую я сделал:
- Как подключить вывод INT MPU 6050?
- Как очистить буфер FIFO на MPU6050?
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- MPU6050 не работает
- Понимание того, почему следует избегать «String» и альтернативных решений
- Как установить связь между веб-камерой и Arduino UNO и получать прямую трансляцию на мобильном телефоне с помощью ESP8266?
- Объяснение кода MPU6050
- Как подключить MPU9250 к NodeMCU с помощью SPI или I2C Slave?
Продолжайте читать о развязке. Узнайте, что делает конденсатор. Вы на правильном пути, вам просто нужно продолжать смотреть на это., @Delta_G
[это] (https://miro.medium.com/max/480/0*Xyfe9kthHM1t2FrV) является типичным расположением., @Majenko
@Majenko: провода колпачка изолированы? Если нет, то предполагалось, что корпус двигателя может быть закорочен на провода питания?, @Seamus
Нет, провода не изолированы. Два конденсатора *припаяны* к корпусу двигателя. Это часть договоренности., @Majenko
Где ты взял комплект? Можете ли вы опубликовать ссылку? Выглядит довольно круто. Был бы забавный проект., @Gabriel Staples
@Seamus Часть корпуса двигателя, где находятся (синие) корпуса конденсаторов, пластиковая. Конденсатор не является КЗ по постоянному току: пайка (справа на фото) к корпусу сделана намеренно., @Andrew Morton