Índice:

Robô de autoequilíbrio do Magicbit: 6 etapas
Robô de autoequilíbrio do Magicbit: 6 etapas

Vídeo: Robô de autoequilíbrio do Magicbit: 6 etapas

Vídeo: Robô de autoequilíbrio do Magicbit: 6 etapas
Vídeo: Self balancing robot, early stages, testing MPU6050 2024, Novembro
Anonim

Este tutorial mostra como fazer um robô com equilíbrio automático usando a placa de desenvolvimento Magicbit. Estamos usando o magicbit como placa de desenvolvimento neste projeto que é baseado no ESP32. Portanto, qualquer placa de desenvolvimento ESP32 pode ser usada neste projeto.

Suprimentos:

  • magicbit
  • Driver de motor L298 de ponte H dupla
  • Regulador Linear (7805)
  • Bateria Lipo 7.4V 700mah
  • Unidade de medição inercial (IMU) (6 graus de liberdade)
  • motores de engrenagem 3V-6V DC

Etapa 1: História

História
História
História
História

Olá pessoal, hoje neste tutorial vamos aprender sobre coisas um pouco complexas. Trata-se de um robô com auto-equilíbrio usando Magicbit com Arduino IDE. Então vamos começar.

Em primeiro lugar, vamos dar uma olhada no que é um robô que se equilibra automaticamente. O robô de auto-equilíbrio é um robô de duas rodas. O recurso especial é que o robô pode se equilibrar sem usar nenhum suporte externo. Quando a energia está ligada, o robô se levanta e se equilibra continuamente usando movimentos de oscilação. Então agora todos vocês têm uma ideia aproximada sobre o robô de autoequilíbrio.

Etapa 2: Teoria e Metodologia

Teoria e Metodologia
Teoria e Metodologia

Para equilibrar o robô, primeiro obtemos dados de algum sensor para medir o ângulo do robô no plano vertical. Para isso usamos o MPU6050. Depois de obter os dados do sensor, calculamos a inclinação para o plano vertical. Se o robô estiver em uma posição reta e equilibrada, o ângulo de inclinação será zero. Caso contrário, o ângulo de inclinação é um valor positivo ou negativo. Se o robô estiver inclinado para a frente, então o robô deve se mover para a direção frontal. Além disso, se o robô estiver inclinado para o lado reverso, o robô deve se mover para a direção reversa. Se este ângulo de inclinação for alto, a velocidade de resposta deve ser alta. Vice-versa, o ângulo de inclinação é baixo, então a velocidade de reação deve ser baixa. Para controlar esse processo, usamos o teorema específico denominado PID. PID é um sistema de controle que costumava controlar muitos processos. PID significa 3 processos.

  • P- proporcional
  • I- integral
  • D- derivado

Cada sistema tem entrada e saída. Da mesma forma, este sistema de controle também tem alguma entrada. Neste sistema de controle, esse é o desvio do estado estável. Chamamos isso de erro. Em nosso robô, o erro é o ângulo de inclinação do plano vertical. Se o robô estiver equilibrado, o ângulo de inclinação será zero. Portanto, o valor do erro será zero. Portanto, a saída do sistema PID é zero. Este sistema inclui três processos matemáticos separados.

O primeiro é multiplicar o erro do ganho numérico. Esse ganho é geralmente denominado Kp

P = erro * Kp

O segundo é gerar a integral do erro no domínio do tempo e multiplicá-la a partir de algum ganho. Este ganho é chamado de Ki

I = Integral (erro) * Ki

O terceiro é derivado do erro no domínio do tempo e multiplique-o por algum ganho. Este ganho é chamado de Kd

D = (d (erro) / dt) * kd

Depois de adicionar as operações acima, obtemos nosso resultado final

SAÍDA = P + I + D

Por causa da parte P, o robô pode obter uma posição estável que é proporcional ao desvio. A parte I calcula a área de erro vs gráfico de tempo. Portanto, ele tenta fazer com que o robô fique em uma posição estável sempre com precisão. A parte D mede a inclinação no gráfico de tempo versus erro. Se o erro for crescente, esse valor é positivo. Se o erro está diminuindo, esse valor é negativo. Por causa disso, quando o robô se move para uma posição estável, a velocidade de reação diminui e isso ajudará a remover overshoots desnecessários. Você pode aprender mais sobre a teoria PID neste link mostrado abaixo.

www.arrow.com/en/research-and-events/articles/pid-controller-basics-and-tutorial-pid-implementation-in-arduino

A saída da função PID está limitada à faixa de 0-255 (resolução PWM de 8 bits) e que alimentará os motores como sinal PWM.

Etapa 3: configuração do hardware

Configuração de hardware
Configuração de hardware

Agora, esta é a parte de configuração do hardware. O design do robô depende de você. Quando você projetou o corpo do robô, você deve considerá-lo simétrico em relação ao eixo vertical que se encontra no eixo do motor. A bateria localizada abaixo. Portanto, o robô é fácil de equilibrar. Em nosso projeto, fixamos a placa Magicbit verticalmente ao corpo. Usamos dois motoredutores de 12V. Mas você pode usar qualquer tipo de motoredutor. isso depende das dimensões do seu robô.

Quando falamos sobre o circuito, ele é alimentado por uma bateria Lipo de 7,4 V. Magicbit usou 5V para energizar. Para tanto, usamos o regulador 7805 para regular a tensão da bateria para 5V. Em versões posteriores do Magicbit, esse regulador não é necessário. Porque alimenta até 12V. Fornecemos diretamente 7,4 V para o driver do motor.

Conecte todos os componentes de acordo com o diagrama abaixo.

Etapa 4: configuração do software

No código, usamos a biblioteca PID para calcular a saída PID.

Vá para o link a seguir para fazer o download.

www.arduinolibraries.info/libraries/pid

Baixe a versão mais recente dele.

Para obter melhores leituras do sensor, usamos a biblioteca DMP. DMP significa processo de movimento digital. Este é um recurso embutido do MPU6050. Este chip tem unidade de processo de movimento integrada. Portanto, é preciso ler e analisar. Depois ele gera saídas precisas e silenciosas para o microcontrolador (neste caso Magicbit (ESP32)). Mas há muitos trabalhos no lado do microcontrolador para fazer essas leituras e calcular o ângulo. Para isso, usamos a biblioteca MPU6050 DMP. Faça o download clicando no link a seguir.

github.com/ElectronicCats/mpu6050

Para instalar as bibliotecas, no menu do Arduino, vá para ferramentas-> incluir biblioteca-> biblioteca add.zip e selecione o arquivo de biblioteca que você baixou.

No código, você deve alterar o ângulo do ponto de ajuste corretamente. Os valores da constante PID são diferentes de robô para robô. Então, para ajustar isso, primeiro defina os valores de Ki e Kd como zero e aumente Kp até obter uma velocidade de reação melhor. Mais causas Kp para mais overshoots. Em seguida, aumente o valor de Kd. Aumente-o sempre em muito pouca quantidade. Esse valor geralmente é baixo do que outros valores. Agora aumente o Ki até obter uma estabilidade muito boa.

Selecione a porta COM e o tipo de placa corretos. carregue o código. Agora você pode brincar com seu robô DIY.

Etapa 5: esquemas

Esquemas
Esquemas

Etapa 6: Código

#incluir

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = false; // define verdadeiro se a inicialização DMP foi bem-sucedida uint8_t mpuIntStatus; // mantém o byte de status de interrupção real do MPU uint8_t devStatus; // retorna o status após cada operação do dispositivo (0 = sucesso,! 0 = erro) uint16_t packetSize; // tamanho do pacote DMP esperado (o padrão é 42 bytes) uint16_t fifoCount; // contagem de todos os bytes atualmente em FIFO uint8_t fifoBuffer [64]; // buffer de armazenamento FIFO Quaternion q; // [w, x, y, z] contêiner de quatérnio VectorFloat gravity; // [x, y, z] vetor de gravidade float ypr [3]; // [guinada, inclinação, rotação] recipiente de guinada / inclinação / rotação e vetor de gravidade double originalSetpoint = 172.5; setpoint duplo = originalSetpoint; double movingAngleOffset = 0,1; dupla entrada, saída; int moveState = 0; double Kp = 23; // definir P primeiro double Kd = 0.8; // este valor geralmente pequeno double Ki = 300; // este valor deve ser alto para melhor estabilidade PID pid (& input, & output, & setpoint, Kp, Ki, Kd, DIRECT); // inicializar pid int motL1 = 26; // 4 pinos para acionamento do motor int motL2 = 2; int motR1 = 27; int motR2 = 4; bool volátil mpuInterrupt = false; // indica se o pino de interrupção MPU ficou alto void dmpDataReady () {mpuInterrupt = true; } void setup () {ledcSetup (0, 20000, 8); // configuração pwm ledcSetup (1, 20000, 8); ledcSetup (2, 20000, 8); ledcSetup (3, 20000, 8); ledcAttachPin (motL1, 0); // pinmode dos motores ledcAttachPin (motL2, 1); ledcAttachPin (motR1, 2); ledcAttachPin (motR2, 3); // junta-se ao barramento I2C (a biblioteca I2Cdev não faz isso automaticamente) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin (); Wire.setClock (400000); // Clock I2C de 400 kHz. Comente esta linha se estiver tendo dificuldades de compilação #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire:: setup (400, true); #endif Serial.println (F ("Inicializando dispositivos I2C…")); pinMode (14, INPUT); // inicializa a comunicação serial // (115200 escolhido porque é necessário para a saída do Teapot Demo, mas // realmente depende de você, dependendo do seu projeto) Serial.begin (9600); while (! Serial); // espera pela enumeração de Leonardo, outros continuam imediatamente // inicializa o dispositivo Serial.println (F ("Inicializando dispositivos I2C …")); mpu.initialize (); // verificar a conexão Serial.println (F ("Testing device connections…")); Serial.println (mpu.testConnection ()? F ("MPU6050 conexão bem-sucedida"): F ("MPU6050 conexão falhou")); // carregue e configure o DMP Serial.println (F ("Inicializando DMP…")); devStatus = mpu.dmpInitialize (); // forneça seus próprios offsets giroscópios aqui, dimensionados para sensibilidade mínima mpu.setXGyroOffset (220); mpu.setYGyroOffset (76); mpu.setZGyroOffset (-85); mpu.setZAccelOffset (1788); // 1688 padrão de fábrica para meu chip de teste // certifique-se de que funcionou (retorna 0 em caso afirmativo) if (devStatus == 0) {// liga o DMP, agora que está pronto Serial.println (F ("Ativando DMP… ")); mpu.setDMPEnabled (true); // habilita a detecção de interrupção do Arduino Serial.println (F ("Habilitando a detecção de interrupção (interrupção externa do Arduino 0) …")); attachInterrupt (14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus (); // define nosso sinalizador DMP Ready para que a função loop () principal saiba que está tudo bem em usá-lo Serial.println (F ("DMP pronto! Aguardando a primeira interrupção …")); dmpReady = true; // obtém o tamanho do pacote DMP esperado para comparação posterior packetSize = mpu.dmpGetFIFOPacketSize (); // configurar PID pid. SetMode (AUTOMATIC); pid. SetSampleTime (10); pid. SetOutputLimits (-255, 255); } else {// ERROR! // 1 = falha no carregamento inicial da memória // 2 = falha nas atualizações de configuração do DMP // (se houver falha, normalmente o código será 1) Serial.print (F ("Falha na inicialização do DMP (código")); Serial. imprimir (devStatus); Serial.println (F (")")); }} void loop () {// se a programação falhou, não tente fazer nada if (! dmpReady) return; // aguarde a interrupção do MPU ou pacote (s) extra (s) disponíveis enquanto (! mpuInterrupt && fifoCount <packetSize) {pid. Compute (); // este período de tempo é usado para carregar dados, então você pode usar isso para outros cálculos motorSpeed (saída); } // redefine o sinalizador de interrupção e obtém o byte INT_STATUS mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus (); // obtém a contagem FIFO atual fifoCount = mpu.getFIFOCount (); // verifique se há estouro (isso nunca deve acontecer, a menos que nosso código seja muito ineficiente) if ((mpuIntStatus & 0x10) || fifoCount == 1024) {// redefina para que possamos continuar de forma limpa mpu.resetFIFO (); Serial.println (F ("estouro de FIFO!")); // caso contrário, verifique a interrupção dos dados DMP (isso deve acontecer com frequência)} else if (mpuIntStatus & 0x02) {// aguarde o comprimento correto dos dados disponíveis, deve ser uma espera MUITO curta (fifoCount 1 pacote disponível // (este permite-nos ler imediatamente mais sem esperar por uma interrupção) fifoCount - = packetSize; mpu.dmpGetQuaternion (& q, fifoBuffer); mpu.dmpGetGravity (& gravity, & q); mpu.dmpGetYawPitchRoll (ypr, & q, & gravity); #if LOG_INPUT Serial. print ("ypr / t"); Serial.print (ypr [0] * 180 / M_PI); // ângulos de euler Serial.print ("\ t"); Serial.print (ypr [1] * 180 / M_PI); Serial.print ("\ t"); Serial.println (ypr [2] * 180 / M_PI); #endif input = ypr [1] * 180 / M_PI + 180;}} void motorSpeed (int PWM) {float L1, L2, R1, R2; if (PWM> = 0) {// direção direta L2 = 0; L1 = abs (PWM); R2 = 0; R1 = abs (PWM); if (L1> = 255) { L1 = R1 = 255;}} else {// direção para trás L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); if (L2> = 255) {L2 = R2 = 255; }} // acionamento do motor ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1 * 0,97); // 0,97 é o fato de velocidade ou, porque o motor direito tem alta velocidade que o motor esquerdo, então a reduzimos até que as velocidades do motor sejam iguais a ledcWrite (3, R2 * 0,97);

}

Recomendado: