Olá pessoal,

Este é meu primeiro post aqui, espero que esteja tudo dentro das regras do forum.

Estou desenvolvendo um projeto que envolve a construção de pequenos robos móveis. Na verdade a parte crítica do projeto não é exatamente a construção dos robos, mas sua programação. Então pretendo usar vários microcontroladores, várias tecnologias diferentes, mas tudo conectado por uma programação padrão que aceite essas varias tecnologias misturadas no mesmo ambiente de controle.

Minha intenção com este tópico, não é exatamente resolver dúvidas, embora eu tenha muitas, mas poder discutir assuntos relacionados ao projeto com outras pessoas. Até o momento tudo foi feito baseado nas minhas pesquisas e leituras, mas não tenho ninguém com quem conversar e talvez eu esteja cometendo erros básicos sem saber. Caso alguem se interesse em participar ou ajudar, é um projeto aberto, tanto no hardware quanto no software, tudo vai ser divulgado no estilo open-source. Já comecei uma parte da divulgação na minha wiki em:

https://fperrotti.wikispaces.com/Projeto+de+robo+móvel.

No momento o que está me incomodando agora é minha opção pelos motores de passo em vez de usar motores DC como praticamente TODOS os robos móveis de pequeno porte que tenho visto pela internet. Eu pessoalmente acho os motores de passo muito mais práticos e fáceis de trabalhar do que motores DC. A gente diz quantos passos ele tem que se mover e ele vai, sem precisar de monitoramento por encoders, sem algoritmos de controle complicados e eventualmente até sem sistemas de transmissão de movimento como redutores, polias e engrenagens, em alguns casos as rodas podem ser conectadas diretamente ao eixo do motor. Além disso são fáceis de serem encontrados em sucata (tenho dezenas aqui), mas mesmo que precisem ser comprados novos, atualmente estão ficando bem mais baratos por conta da popularização das impressoras 3D. E finalmemte, são motores precisos. Como meu projeto envolve (ou pretende) navegação autônoma, a precisão nos movimentos é muito importante.

Eu considero que são argumentos bem fortes a favor dos motores de passo e não entendo porque não encontro exemplos de robôs usando motores de passo pela internet. Sei que os motores DC conseguem mais potência e velocidade com menos consumo, mas meus robozinhos com motor de passo funcionam bem, devo estar perdendo alguma coisa nesse assunto. Afinal, porque ninguém usa motores de passo pra esse fim?

Obrigado,

Perrotti

Exibições: 11369

Responder esta

Respostas a este tópico

Eu também não consegui fazer download da biblioteca, agora preciso sair, mas depois eu posto o código na forma de post mesmo, não é muito grande.

Abraço

To de volta...
sobre os motores eu importei sim, comprei na pololu mesmo, eles mandaram via correio, ficou meio salgado com impostos e tudo, mas achei que valeu a pena. Mas ultimamente tem aumentado a oferta de motores de passo por causa das impressoras 3D. Se já faz algum tempo desde que vc andou procurando, talvez agora seja bem mais facil de achar algo do jeito que vc precisa.

A questão de empilhar comandos, tem a ver com a execução de rotas, tipo: ande 100 pulsos para frente, depois gire 25 pulsos para a esquerda e depois ande mais 50 pulsos para a frente. Neste exemplo seriam 3 comandos de movimento que poderiam ser enviados de uma vez só caso o robo pudesse empilhar comandos. O robo com o wixel já faz isso, ele pode armazenar até 90 comandos e vai executando um de cada vez na sequencia. Se alguma coisa impede o movimento, então o robo para, deleta todos os comandos pendentes e envia uma msg pro computador dizendo que existe um obstáculo a frente, aí o programa no computador é que decide o que fazer. Eu não acho que o wixel, arduíno ou pic tenham recursos pra tomar esse tipo de decisão, isso tem que ficar a cargo do computador. O meu projeto é direcionado a esse esquema, computador e robo trabalhando juntos e se comunicando o tempo todo.

Outra vantagem de empilhar movimentos é poder juntar movimentos iguais de forma a deixar a aceleração e desaceleração mais "espertas". No wixel isso já está implementado, então é possível uma situação onde o robo recebe um comando pra andar 1 metro, como tem uma telemetria bem intensa, no meio do caminho o computador pode decidir que é possivel alongar o movimento e aí envia um segundo comando para andar por exemplo mais meio metro.

O robô recebe o segundo comando enquanto ainda está executando o primeiro e como são movimentos iguais, em vez de empilhar o segundo comando ele modifica o primeiro juntando os dois. Isso evita que ele desacelere o primeiro movimento para iniciar o segundo.

Sobre o timer, pelo que entendi vc estava configurando o timer exatamente para o tempo dos pulsos. Como esse intervalo varia com a aceleração tem que ficar a todo momento reconfigurando o timer. Também complica quando vc tem duas ou mais coisas que devem ser verificadas em intervalos diferentes, isso te obriga a usar os outros timers.

Eu segui uma abordagem diferente. O timer está configurado pra um intervalo curtíssimo, antes estava pra 250 microssegundos, agora com a implementação do pwm mudei pra 64 us. E dentro da função do timer faço a mesma coisa que vc faz atualmente, conto os microssegundos usando a função micros().

Eu penso no timer como um segundo loop. Tem o loop principal e tem o loop do timer. A diferença é que o loop do timer tem alta prioridade em relação ao loop principal então as contagens de tempo podem ser mais precisas.

Sobre a programação das funções de baixo, médio ou alto nível, agora falando como programador eu aconselho a seguir uma abordagem hierárquica e deixar tudo bem separado. Por exemplo na implementação que eu fiz, os movimentos implementados são considerados de baixo nível. Existe um conjunto pequeno de movimentos básicos e são quantificados por pulsos pros motores de passo.

Nesse nível o robô não precisa saber nada sobre o hardware de movimentação, a não ser que usa dois motores de passo em configuração diferencial. Por consequência também não pode saber quantos centímetros andou ou quantos graus girou.

Um nível acima seria acrescentado as informações do hardware: diâmetro das rodas, distancia entre rodas, passos por volta e o que mais fosse necessário. Nesse nível os comandos poderiam ser quantificados em centímetros ou graus e o robô seria capaz saber por exemplo a distância percorrida até o momento ou sua orientação atual.

Para isso eu implementaria uma classe descendente da classe que implementa os movimentos básicos, assim o código dos dois níveis ficam bem separados e um não interfere com o outro.

Vou abrir outro post pra por a biblioteca, nossa conversa tá ficando meio espremida no canto da tela, vou ver se consigo abrir mais espaço.

Segue o código da biblioteca. Usa duas classes, uma pra gerenciar o motor e outra pra gerenciar o conjunto.

A classe StepperMotor cuida de calcular e enviar a configuração de pinos pra um motor, o esquema é bem simples, como o motor tem quatro passos, a classe tem um contador que pode contar de 0 a 3. A cada novo passo o contador pode ser incrementado, decrementado ou ficar do jeito que está. É isso que o parâmetro do método nextStep() informa, ele pode ser 1, -1 ou 0.

Uma vez calculado o numero do passo, esse numero é convertido para binário e enviado para os pinos do motor pelo método motorPinsOut().

Esta classe não precisa ser acessada diretamente pela aplicação, a outra classe é que cuida disso, mas por algum motivo o compilador do arduino exige que ela seja declarada no include da aplicação.


+----------------------- Arquivo StepperMotor.h -------------------------+

// Esta biblioteca é parte do projeto de desenvolvimento de
// robôs móveis desenvolvido por Francesco A. Perrotti na
// Fatec Americana.
// Pode ser usada para fins comerciais ou pessoais livremente,
// apenas deixe citado o autor.

#ifndef StepperMotor_h
#define StepperMotor_h

#include <Arduino.h>

class StepperMotor {
  public:
    StepperMotor();
    void init(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4);
    byte nextStep(int direction);
    void motorPinsOut(byte pins);
    void turboOn();
    void turboOff();
    byte step_number;
  private:
    int number_of_steps;
    byte turbo;
    int motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4;
};
#endif
+------------------------------------------------------------------------+


+----------------------- Arquivo StepperMotor.cpp -----------------------+

// Esta biblioteca é parte do projeto de desenvolvimento de
// robôs móveis desenvolvido por Francesco A. Perrotti na
// Fatec Americana.
// Pode ser usada para fins comerciais ou pessoais livremente,
// apenas deixe citado o autor.

#ifndef STEPPERMOTOR_cpp
#define STEPPERMOTOR_cpp

#include "Arduino.h"
#include "StepperMotor.h"

StepperMotor::StepperMotor()
{
}

void StepperMotor::init(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4)
{
  this->number_of_steps = number_of_steps;    // total number of steps for this motor

  // A inversão de pinos aqui é para que a pinagem fique compatível
  // com a biblioteca Stepper.h. Assim a subtituição de uma biblioteca
  // por outra não exige mudanças no hardware.

  this->motor_pin_1 = motor_pin_2;
  this->motor_pin_2 = motor_pin_3;
  this->motor_pin_3 = motor_pin_1;
  this->motor_pin_4 = motor_pin_4;

  pinMode(this->motor_pin_1, OUTPUT);
  pinMode(this->motor_pin_2, OUTPUT);
  pinMode(this->motor_pin_3, OUTPUT);
  pinMode(this->motor_pin_4, OUTPUT);

  step_number = 1;      // passo corrente
  turbo= 0;
  motorPinsOut(0);
}

void StepperMotor::motorPinsOut(byte pins)
{
  digitalWrite(motor_pin_1, pins & 1);
  digitalWrite(motor_pin_2, (pins>>1) & 1);
  digitalWrite(motor_pin_3, (pins>>2) & 1);
  digitalWrite(motor_pin_4, (pins>>3) & 1);
}

byte StepperMotor::nextStep(int direction)
{ byte pins;

  step_number = (step_number + 4 + direction) % 4;

  pins= 1 step_number;
  if(turbo && direction) // não liga o turbo se não tem movimento
  {
    if(pins>1)
        pins += pins / 2;
    else
        pins += 8;
  }

  motorPinsOut(pins);
  return pins;
}

void StepperMotor::turboOn(){
  turbo= 1;
}

void StepperMotor::turboOff(){
  turbo= 0;
}

#endif
+------------------------------------------------------------------------+

Saiu uma linha errada na função nextStep().

Onde está:

"pins= 1 step_number;"

Devia ser:

pins= 1 '<'< step_number;

Não consigo por o sinal de menor duas vezes neste editor, ele simplesmente desaparece do texto, agora to imaginando se no restante do código ele não foi engolido também.

Aqui a classe principal. Já tem uma instancia criada, é só inicializar como tá no outro post.
Como disse, ainda é uma versão inicial, tem muita coisa que pode ser otimizada, talvez eliminar algumas variáveis ou melhorar a lógica.

Tem dois métodos de parada que abortam o movimento:
void stopNow(): para imediatamente, se o robô estiver meio rápido ele pode não parar realmente.
void decelStop(): inicia a desaceleração imediatamente e para assim que atingir a velocidade de partida.

+-------------------------Arquivo StepperRobot.h -------------------------------+

// Esta biblioteca é parte do projeto de desenvolvimento de
// robôs móveis desenvolvido por Francesco A. Perrotti na
// Fatec Americana.
// Pode ser usada para fins comerciais ou pessoais livremente,
// apenas deixe citado o autor.

#ifndef StepperRobot_h
#define StepperRobot_h

#include <Arduino.h>
#include <TimerOne.h>
#include "StepperMotor.h"

#define mvAhead     0
#define mvBack      1
#define mvSpinL      2
#define mvSpinR     3
#define mvTurnL     4
#define mvTurnR     5
#define mvBrake     6

class StepperRobot {
public:
    StepperRobot();
    void init(int stepsPerRev, int startSpeed, int cruizeSpeed, int pulsesToCruizeSpeed);
    void initLeftMotor(int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4);
    void initRightMotor(int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4);
    boolean move(int movType, int pulses);
    void stopNow();
    void decelStop();
    boolean isMoving();
    StepperMotor lf;
    StepperMotor rg;
    void turboOn();
    void turboOff();
private:
    int stepsPerRev;
    int pulsesToCruize;
};

extern StepperRobot rob;

#endif
+------------------------------------------------------------------------+

+-------------------------Arquivo StepperRobot.cpp ------------------------+

// Esta biblioteca é parte do projeto de desenvolvimento de
// robôs móveis desenvolvido por Francesco A. Perrotti na
// Fatec Americana.
// Pode ser usada para fins comerciais ou pessoais livremente,
// apenas deixe citado o autor.

#include "StepperRobot.h"

struct _MoveTable {
  char left, right;
};

typedef _MoveTable TMoveTable;

#define nMoves    7

TMoveTable mvTable[nMoves] =
{// left rigth
  {   -1,    1  },  // move ahead
  {    1,   -1  },  // move back
  {    1,    1  },  // spin left
  {   -1,   -1  },  // spin right
  {    0,    1  },  // turn left
  {   -1,    0  },  // turn right
  {    0,    0  },  // brake (bobinas ativadas)
};

StepperRobot rob;

unsigned long nextPulse;
unsigned long pulseTime;
int pulsesToGo;
int moveType;
int acelUntil;
int decelAfter;
unsigned long deltaTime;
unsigned long timeOnStart;
unsigned long timeOnCruize;
float escaler;
float deltaEscaler;

boolean blinkLeft = false;
boolean blinkRight = false;
boolean blink = false;
boolean blinkStatus = false;
byte leftPins, rightPins;
unsigned long nextBlink, blinkTime;

/// ISR Timer Routine

void timerEvent()
{
  if(!pulsesToGo) return;

  if(micros()>=nextPulse)
  {
   rob.lf.nextStep(mvTable[moveType].left);
   rob.rg.nextStep(mvTable[moveType].right);

   pulsesToGo--;

   if(pulsesToGo)
   {
     if(pulsesToGo > acelUntil)
     {
       pulseTime= max(timeOnCruize, pulseTime-deltaTime*escaler);
       escaler-=deltaEscaler;
     }
     else
       if(pulsesToGo <= decelAfter)
       {
         pulseTime= min(timeOnStart, pulseTime+deltaTime*escaler);
         escaler+=deltaEscaler;
       }

     nextPulse= micros()+pulseTime;
   }
   else
     blink= false;
  }
  else
   if(blink && (micros()>=nextBlink))
   {
     blinkStatus = !blinkStatus;
     if(blinkLeft)
     {
       if(blinkStatus)
         rob.lf.motorPinsOut(leftPins);
       else
         rob.lf.motorPinsOut(0);
     }
     if(blinkRight)
     {
       if(blinkStatus)
         rob.rg.motorPinsOut(rightPins);
       else
         rob.rg.motorPinsOut(0);
     }
     nextBlink= micros()+blinkTime;
   }
}


/**
   stepsPerRev: steps per revolution for both motors (must be the same)
   startSpeed: pulses/second
   cruizeSpeed: pulses/second
   pulsesToCruize: how many pulses from start to cruize.
*/

void StepperRobot::init(int stepsPerRev, int startSpeed, int cruizeSpeed, int pulsesToCruizeSpeed)
{
  this->stepsPerRev = stepsPerRev;
  pulsesToCruize = pulsesToCruizeSpeed;
  timeOnStart = 1000000L / startSpeed;    // tempo em microssegundos
  timeOnCruize= 1000000L / cruizeSpeed;

  moveType= 0;
  nextPulse= 0;
  pulsesToGo = 0;

  Timer1.initialize(64);
  Timer1.attachInterrupt( timerEvent );
}

boolean StepperRobot::move(int movType, int pulses)
{
   if (pulsesToGo) return false;
   if((movType<0)||(movType>=nMoves)) return false;

   moveType= movType;

   // calcula parametros para aceleração
   pulseTime= timeOnStart;
   if((moveType==mvSpinL)||(moveType==mvSpinL))
       pulseTime+= timeOnStart / 4;
   deltaTime = (pulseTime-timeOnCruize) / pulsesToCruize;
   decelAfter= min(pulses/2, pulsesToCruize);
   acelUntil= pulses-decelAfter;
   escaler= 1.5;
   deltaEscaler= (escaler-(2-escaler))/pulsesToCruize;

   // teste pwm para travar rodas
   blinkLeft= !mvTable[moveType].left;
   blinkRight= !mvTable[moveType].right;
   blink= blinkLeft || blinkRight;
   if(blink)
   {
      blinkTime= pulseTime / 16;
      nextBlink= micros()+blinkTime;
      blinkStatus= true;
   }

   // inicia o movimento
   leftPins = lf.nextStep(mvTable[moveType].left);
   rightPins = rg.nextStep(mvTable[moveType].right);
   nextPulse= micros()+pulseTime;
   pulsesToGo= pulses;
   return true;
}

void StepperRobot::stopNow(){
   pulsesToGo= 0;
}

StepperRobot::StepperRobot()
{
}

void StepperRobot::decelStop()

{
   pulsesToGo= min(pulsesToGo, pulsesToCruize);
   decelAfter= pulsesToGo;
}

boolean StepperRobot::isMoving()
{
  return pulsesToGo > 0;
}

void StepperRobot::initLeftMotor(int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4)
{
  lf.init(stepsPerRev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4);
}

void StepperRobot::initRightMotor(int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4)
{
  rg.init(stepsPerRev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4);
}

void StepperRobot::turboOn()
{
  lf.turboOn();
  rg.turboOn();
}

void StepperRobot::turboOff()
{
  lf.turboOff();
  rg.turboOff();
}
+------------------------------------------------------------------------+

Francesco, realmente muito obrigado pela biblioteca e explicações de como usá-la.

Pelo que ví numa primeira leitura, é mais ou menos o que fiz aqui, só que você usou recursos avançados para mim, como classes e hierarquia.

Ainda estou aprendendo sobre C/C++ e esse tipo de recurso.
Daí eu implementei minha lógica toda em cima de funções de usuário. Estão até estruturadas, mas como você me alertou agora, talvez eu tenha misturado um pouco as coisas, pois no baixo nível eu já coloquei centímetros andados p.ex., misturando com a contagem de pulsos. Tenho aí uma melhoria a fazer.

Vou estudar sua biblioteca com cuidado antes de falar alguma besteira, mas acho que posso utilizada sim, pois com o L297 não precisa se preocupar em fazer o stepping, é só gerar um clock para cada motor e aí também a utilização de um timer simplifica ainda mais. Vou pensar direito no seu conceito de dois loops - principal e o do timer - acho que não captei direito a idéia.


Por outro lado, minha abordagem quando à navegação do robô (DUWI) e diferente da sua.
Eu pretendo, até agora, incluir toda a lógica de navegação nos arduinos e futuramente agregar algo mais potente para análise de imagens. p. ex., mas ainda não sei muita coisa sobre isso, preciso estudar muito ainda.

Atualmente uso dois Arduinos, mas imagino chegar a três.
A grosso modo, um Nano comando os motores, monitora microswitches instalados em uma espécie de parachoque (dianteiro e traseiro) e comandava um terceiro motor de passo instalado com o eixo na vertical e girando 360º um segundo sensor ultrasônico e um PIR (para correr atrás dos meus cachorros, hehe). Em um UNO, tenho o monitoramento de sensores e a lógica de navegação, onde como falei, tenho - hoje ainda misturados - opções para seguir uma linha ou desviar de um obstáculo detectado, ou seguir uma parede à determinada distância.
O Nano responde imediatamente à um comando recebido do UNO, p. ex. parar, interrompendo o comando em execução. Ele executa os comandos recebidos sem parar os motores, ou seja, se tiver que parar o UNO deve enviar isso antes. Até antes de parar isso para os benditos motores DC, eu estava já comandado tudo meio seco, tipo tem que virar, "trava um motor na paulada" e ajusta a rotação do outro, se necessário.

Pensando em um motor DC, toda essa lógica se complica um pouco, pois não dá para contar pulsos, tem que contar tempo!. Agora imagine uma aceleração, tem que integrar a velocidade no tempo para saber quanto o robô andou. Essa foi essa parte que não estava muito boa. Por sorte, estou voltando aos motores de passo, hehe.

Quanto ao empilhamento de comando, é o que entendí mesmo.

Ainda não havia desistido dele totalmente, para implementar algo parecido com aquela linguagem de uma tartaruga (esquecí o nome) - algo que o robô da Arduino tem. Na minha abordagem, isso seria uma função de alto nível que deveria - ainda não é - facilmente utilizável pelo programa principal (main).

Quanto aos motores, eu comprei os meus grandões à quinze dias atrás. Procurei bastante incluindo Mercado Livre e nada. O melhor no aspecto tensão/corrente/torque que encontrei foi mesmo o que comprei. É esse aqui .

Parece gozação, mas ele tem ponta de eixo dos dois lados. Isso é o ideal para motoredutor DC, mas não se encontra nenhum no mercado. Para dizer a verdade, achei só um, mas foi até difícil obter a cotação para PF e o custo não era muito honesto. Com a falta de acoplamento no eixo do motor, a resolução do encoder se complica bastante estando no eixo da roda. Esse é outro ponto negativo do uso de motoredutor DC nos robôs.

Ainda estou com um probleminha no driver L297/L298 - já achei onde é mas como ficou muito fio de jumper, está difícil acessar o ponto do problema - mas já deu para perceber que o torque agora está até exagerado, hehe. Nem vou precisar usar Full Stepping, em half o torque já é mais que o necessário.

Ainda não remontei o robô, mas estou pressentindo que o alto torque deva gerar um problema de "vibração" pois o comando de um step com duas boinas energizadas é bem violento e muito rápido - tendo em vista o chopper que mantém a corrente no máximo da nominal do motor (eu acho...). Isso vai complicar um pouco usando half stepping, que oscila entre uma e duas bobinas.

Já estou vendo uma melhoria nesse driver que seria abaixar a corrente nos pulsos com duas bobinas para a corrente / torque ficar "igual" à com uma. Isso deve fazer a vibração reduzir, mas ainda preciso testar o robô para ter certeza.

Essa a meu ver é outra questão que não se encontra (fácil) na internet. Ninguém discute excesso de torque em motores de passo para robôs, só baixo torque !!.

Wilmar

Wilmar,

Eu ainda to começando no arduino e pra falar a verdade também não sou muito experiente em C++, conheço bem C, Pascal e Java então estou me virando em C++ com o que sei das outras linguagens.

Mas sobre o uso de classes pra criar hierarquias, é claro que as classes são feitas pra isso, então facilita bastante, mas vc não precisa necessariamente usar orientação a objeto pra estabelecer uma hierarquia no código, pode fazer com C ansi, basta colocar o código em arquivos separados para cada nível e estabelecer a hierarquia através das dependencias. O arquivo com funções de mais alto nivel pode incluir o arquivo com funções de mais baixo nivel, mas nunca o contrário. Por exemplo a biblioteca que fiz, usa dois arquivos de código fonte (sem contar os headers) se eu fosse fazer em C ansi manteria exatamente assim, usando dois arquivos. Para funções de mais alto nível criaria um terceiro e assim por diante. Além disso eu não tenho muita certeza se o uso de classes é a melhor opção quando se programa microcontroladores. Talvez a versão C ansi produza um código binário mais enxuto, com menos bytes, eu não sei, precisaria testar.

A linguagem do arduino é C/C++, não tem nenhuma diferença. Pelo que entendi o programa principal, ou seja o arquivo onde está a aplicação é um script em C/C++ que acrescenta alguma facilidades, só isso. Quando vc compila o programa, o que o "compilador" faz é gerar o código fonte correspondente em C/C++ para o teu script e aí chamar o compilador de verdade que é o compilador C/C++ pro microcontrolador em uso.

Isso quer dizer que as funções que vc cria no arquivo da aplicação, imagino que seja o que vc chama de funções do usuário, podem perfeitamente ser movidas para arquivos separados e depois vc poe um include no teu arquivo principal, não tem segredo.

Sobre o timer, eu estava dizendo o seguinte, se o timer estiver configurado pra intervalos regulares e bem curtos, o loop principal e o timer podem ser encarados como processos que são executados em paralelo, um não interfere (teoricamente) e não depende do outro. É como se fossem duas threads rodando em paralelo, dois loops independentes que são executados repetidamente ao longo do tempo. As funcionalidades onde o tempo não é um fator crítico podem ficar no loop principal, as que precisam de medidas precisas de tempo ficam no loop do timer, é isso. Pra funcionar o intervalo do timer tem que ser menor que o menor intervalo necessário pra tua aplicação.

Sobre os motores, eu nem tentei fazer nada com motores DC, mas fico imaginando o inferno que seria tentar aplicar essa lógica ao tempo em vez de usar pulsos, mas não dá pra simular pulsos com os encoders? Dá mais trabalho mas imagino que seja possível. Em algum lugar eu li que existem encoders com até 2000 pulsos por volta, só não sei qual seria a dificuldade pra posicionar os motores DC com uma resolução tão alta assim.

Olha, foi bom vc citar a vibração dos motores, eu tava percebendo que o meu robozinho vibra bastante no inicio do movimento. Como está no exemplo, tinha deixado com as duas bobinas ativadas porque assim conseguia mais velocidade, agora que me toquei da relação entre as duas coisas. Mudei pro modo com uma bobina e diminuí as velocidades, agora vibra menos, mas ainda vibra um pouco no inicio, antes de chegar na velocidade de cruzeiro. Imagino que seja assim mesmo, porque ele não consegue partir em velocidades maiores pra evitar a vibração. O que eu fiz foi tentar passar mais rápido pelas baixas rotações na aceleração, por isso tem o escaler e o deltaEscaler no código, mas ainda não resolveu.

Falando no código, depois que postei percebi alguns bugs menores que já corrigi aqui, ainda estou trabalhando em algumas implementações, depois posso por o código corrigido e melhorado.

Até onde eu já andei pesquisando, usa-se sim hierarquias e classes para microcontroladores, e isso provalemente possa economizar em bytes compilados se usar um edior/compilador melhor que o do Arduino.

Eu acabei, como disse, estruturando o meu código no que você chama de C ANSI.
Do que conheço de programação (eu comecei na faculdade com FORTRAN 1130 !!) nunca usei classes e hierarquias. Ultimamente evolui bastante em VBA para Excel e Project principalmente, mas também não cheguei nisso.

Depois de ler um pouco, fiz como menciona, separando em diferentes arquivos. Mas com as poucas facilidades da IDE do Arduino, acabei juntando tudo em um único arquivo. Agora me caiu a ficha de que talvez não fosse a IDE do Arduino e sim a mistura que eu já fazia entre código de baixo e médio nível, p. ex. pensando em centímetros na rotina de stepping!. Vou melhorar isso agora.


Agora entendí sobre os loops principal e do timer!. Valeu pela explicação.

Sobre os encoder´s, acha-se sim os de tipo industrial no mercado, com altíssima resolução.
Só que o preço é à partir de uns R$400/peça, e, o pior, o acoplamento deles é por eixo. Em motoredutores com apenas uma ponta de eixo, fica complicado acoplar a roda e um encoder desse tipo, sem falar no espaço disponível em um pequeno robô.

Já pensando em um tipo de encoder "não profissional", tipo um com um codewheel impresso ou perfurado e leitura por IR, o que complica muito é que no eixo da roda, pela baixa rotação é necessário uma quantidade de pulsos por rotação que não se acha nada pronto, a impressão mesmo em laser é "difícil" e perfurado (na mão) então, fica muito irregular. O mesmo para Hall, é praticamente impossível dispor vários pequenos imãs com um passo preciso, além da dificuldade de como fixá-los. É lógico que tudo isso para uma situação caseiro, onde não conto com nenhum recurso mecânico sofisticado.

Eu comentei anteriormente que não se acha nada razoável no mercado nessa área né.
Um ponto que, pelo meu conhecimento profissional, eu deveria ter antecipado e que se mostrou um problema intransponível, além dos problemas com a irregularidade dos pulsos com a construção caseira é a relação pulsos/rotação do encoder e a frequência de atualização dos PID´s que você vai precisar para controlar adequadamente a rotação das rodas.

Mas para ter movimentos ágeis é necessários algo como uns 35 mS de atualização dos PID´s de controle de rotação das rodas. O PID foi inventado na era analógica, na verdade antes dela, pois foi criado na era pneumática. Assume-se que você tenha uma nova medição da variável que quer controlar a cada atualização do PID, senão a lógica do próprio PID cria problemas, porque você calcula uma ação de correção com dados antigos e de fato aquilo já não é mais verdade, pois o processo de motor - redutor - roda é rápido. Daí concluí-se que a frequência do encoder deve ser maior que a atualização do PID. Fazendo-se algumas continhas simples, percebe-se que é necessário, tipo no mínimo 100 faixas / furos no codewheel (para ficar meia-boca) pra isso. como nessa fixação, o diâmetro externo do codewheel deve ser menor que o diâmetro da roda, vamos dizer de 60 a 100mm, a coisa fica minúscula!.

Desculpe estar entrando em detalhes que não vão te ajudar.
Estou registrando aqui, para que, se alguém acompanhar nossa discussão, possa aproveitar esse aprendizado, pois de novo, isso não se acha fácil na internet.

Quanto a "vibração", eu bem lá no início, até pensei nisso, pelo torque ser pulsante e tals.
Quando implementei as primeiras versões do meu robôzinho, ví que isso não era problema.
Agora que me toquei (na "bancada") que, com um motor de maior torque nominal e com o melhor aproveitamento do torque do motor com o chopper, mesmo em médias rotações (umas 20 a 30 rpm no meu caso), a martelada nos steps é muito maior e na intuição, acredito que a velocidade de giro do rotor do motor também o seja. Em half stepping, com a alternância de acionamento de uma e duas bobinas, no sentimento e em rotações maiores que essa, parece que a velocidade de giro com duas bobinas é maior do que com uma!. Teoricamente é isso, mas na prática, isso parece ser mais significativo agora do que antes, com motores mais fracos. Como os motores que você está utilizando são bem mais fortes que os meus "antigos", talvez seja isso que você percebeu!.

Não se preocupe com atualizar o código por minha causa, o que passou já dá para abrir a cabeça e pensar por um bom tempo, e eu ainda estou acabando o meu novo driver.

Já estou meio exausto dessa fase de hardware. De quando decidí migrar para os motores DC até agora, uns cinco meses, não avancei nenhum milímetro (nem o robô!) no código, só adaptações. Já nem lembro ao certo onde parei, hehe.

Vou mencionar aqui, para tentar ajudar outras pessoas, o real motivo de embarcar na minha maratona com obstáculos no desenvolvimentos dos encoder´s: de cara eu não pensei em encoder´s, ficaria para uma segunda etapa, a das melhorias. Eu comprei dois motoredutores meio superdimensionados imaginando que com uns ajustes nas diferentes velocidades, eu conseguisse fazer o bichinho andar reto. Isso não ficou perfeito, mas dava para quebrar o galho, na verdade bagunçava um pouco justamente nas acelerações e desacelerações, outro motivo para minha rotina não ter ficado boa. Eu só esquecí de uma coisa!. Uso uma LIPO 3S para alimentar o robô. Totalmente carregada ela tem 12,4 volts. No mínimo de carga chega a 11,1 volts. Pois bem, um motor DC é altamente sensível a tensão de alimentação certo. Como toda a lógica eu estava modificando para tempo (ao invés de pulsos), toda hora que eu ia calibrar um tempo, para p. ex. rotacionar 90º eu percebia que ele já não rotacionava corretamente 45º. Porque ? Porque ?? Haaaa, porque a tensão da LIPO tinha caído!!! Daí, encoder já!!!
Não dava para pensar em regular a tensão, porque, primeiro a corrente em situações de alta solicitação passava de dois ampéres, podendo chegar a mais de quatro com os motores travados, e eu havia comprado os motores com tensão nominal de 12V, claro, tentasse regular para uns nove talvez, estaria perdendo capacidade nos motores!.

É isso por agora. Vamos trocando idéias Francesco ...

Wilmar

Sobre a vibração dos motores eu tenho uma teoria, mas não é algo que eu consiga resolver com software, teria que ser com hardware mesmo. Acho que a vibração acontece quando o tempo do pulso é maior do que o tempo que o motor leva pra mover o rotor de uma fase para outra. A gente usa motores de 4 fases, o rotor do motor se move de fase em fase a cada passo, a qualquer momento o rotor está "apontado" pra uma das fases. A gente energiza a fase seguinte (ou anterior) e ele se move pra lá. O problema é quando o rotor chega na fase seguinte e ela ainda está energizada, aí ele para no tranco e provoca a vibração. O ideal seria que no momento em que o rotor chega na fase seguinte, a próxima já estivesse energizada, assim ele não daria essa travada e o movimento segue suave. Então eu to sempre tentando achar a velocidade onde o tempo do pulso e do movimento do rotor se equilibram.

O problema é que não existe (eu acho) uma velocidade de equilibrio fixa e constante, isso varia muito com vários fatores, eu sabia do peso, quanto mais pesado o robô, menor é essa velocidade, também varia com a inércia, ela pode ser maior quando o robo já está em movimento, mas até vc comentar o assunto, não tinha me ocorrido que também depende da voltagem aplicada às bobinas.

Pelo que entendi, a corrente dá torque ao motor e a voltagem dá velocidade, por isso meus motorzinhos de 2.7 volts/1A por fase são lentos mas poderosos. Acho que um drive ideal poderia fornecer bastante corrente com baixa voltagem no inicio do movimento, nesse momento o que o robô precisa é de torque, não de velocidade e aos poucos, conforme o robô vai ganhando inércia ir baixando a corrente e aumentando a voltagem, mas mantendo a mesma potencia. Se de alguma forma o software pudesse ir variando o tempo dos pulsos na mesma proporção, acredito que assim o movimento poderia ser suave em qualquer rotação.

Posso estar falando besteira, como disse não sou da área da eletrônica, vc falou do L297 que mantêm a corrente dentro do especificado. Eu já tinha visto, ali na Pololu mesmo e depois em vários outros lugares, drivers com reguladores de corrente, normalmente são usados nas impressoras 3D. Só que os drivers que vi, exigem uma voltagem mínima bem alta pra funcionar e acho que o que eles fazem é manter a corrente constante. Seria possível fazer um regulador de potencia em vez de um regulador de corrente? Algo que variasse a corrente (ou o limite de corrente) de acordo com a voltagem fornecida pra manter a mesma potencia? Acho que assim seria possível conseguir uma aceleração suave, supondo que as bobinas não torrem na partida.

Sobre o programa, como vc disse pode ter mais gente acompanhando a conversa, se não agora em algum momento futuro, então depois vou postar a versão corrigida, assim que terminar mais algumas implementações. Além disso não me sinto bem sabendo que tem uma cria minha com defeito por aí :)

E sobre o teu projeto, não se acanhe em comentar o que quiser, mesmo que não tenha a ver com o meu projeto, se bem que eu pouco posso ajudar na eletrônica, mas to aprendendo muito com nossa conversa.

Abraço

Francesco,

Eu comecei a responder sua colocação e ela ficou enorme e no final, hummm, sem final, sem nenhuma conclusão lógica. Vou manter por enquanto só os fatos e amadurecer um pouco mais as elocubrações até para poder colocá-las em texto de forma intelingível.

Os fatos são:


Como eu finalizei a montagem do novo driver, pensei em testar o meu robô numa montagem meia-boca com os motores maiores (NEMA 23), antes de responder as suas colocações, porque realmente estava achando meio exagerado a vibração desses motores na "bancada".

Pois bem, realmente, com o chopper (controle de corrente para os motores) ajustado no valor nominal dos motores, o robô mais que vibra, ele quase pula!. A única coisa que ganhou massa foram os motores (100%), o resto está igual, exceto por pequenos componentes que não foram montados, mas não têm massa significativa. Nessa condição, a vibração (e o ruído do chassi e outros componentes) é tanta que o robô chega a perder tração (e não andar reto)!. Considere que minhas rodas são meia-boca, mas mesmo assim, nem a maior massa dos novos motores mantém as rodas "grudadas" no piso. Cheguei a testar em piso com cerâmicas um pouco rústicas (não lisas) e mesmo assim as rodas perdem tração. Péssimo isso né!.

Bom, aí tentei ajustar o chopper para valores menores e muito menores, mas o resultado não foi o que esperava.
Mesmo com um torque líquido bem baixo, quase igual aos motores anteriores (fraquinhos), a vibração ainda é bem maior, embora o robô passe a andar reto. O ruído reduz bem, mas a vibração nem tanto, ficando ainda muito alta do ideal.

Assim que conseguir envelopar minhas idéias sobre isso, posto aqui para tentarmos achar uma maneira de minimizar essa vibração.

Wilmar

Wilmar,

Terminei uma versão um pouco mais elaborada da biblioteca. Pra facilitar o download e a documentação criei um espaço pra ela na minha wiki. Com o tempo vou acrescentando novas versões e uma documentação melhor:

https://fperrotti.wikispaces.com/Stepper+Lib+Arduino

Esta versão já consegue empilhar comandos, implementei um limite de 32 mas dá por muito mais, cada comando usa só 3 bytes no buffer.

Depois vou fazer umas experiências de software pra ver se consigo reduzir a vibração, tive umas ideias e talvez funcione, depois te falo o resultado.

Francesco

RSS

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço