collapse

* Posts Recentes

Amplificador - Rockboard HA 1 In-Ear por almamater
[Ontem às 19:13]


O que é isto ? por KammutierSpule
[26 de Março de 2024, 19:35]


Bateria - Portátil por almamater
[25 de Março de 2024, 22:14]


Emulador NES em ESP32 por dropes
[13 de Março de 2024, 21:19]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]


Meu novo robô por josecarlos
[06 de Janeiro de 2024, 16:46]


Laser Engraver - Alguém tem? por almamater
[16 de Dezembro de 2023, 14:23]

Autor Tópico: tcustodio - Tractor de prendas!  (Lida 8391 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
tcustodio - Tractor de prendas!
« em: 21 de Dezembro de 2009, 04:07 »
Objectivos

Fazer o meu primeiro robot móvel, para o Desafio ajuda o Pai Natal. Quero que seja capaz de transportar as prendas com a máxima suavidade nas curvas (não quero cá zigzags nem despistes!) 8)



Desenvolvimento

Código

Baseia-se num princípio de controlo proporcional: Quanto mais à esquerda a linha se desviar, mais o motor esquerdo abranda, para o direito completar a curva. O mesmo se aplica ao motor direito (::)).
Citar
#include <Servo.h>

#define LEFT_MIN 94
#define LEFT_MAX 124
#define RIGHT_MIN 89
#define RIGHT_MAX 59

Servo left_servo, right_servo;
unsigned short int left_mod, right_mod;
boolean lastseen_left, lastseen_right;
byte lista_validade[36] = {
  B00001000, //   8
  B00000100, //   4
  B00001100, //  12
  B00000010, //   2
  B00000110, //   6
  B00001110, //  14
  B00000001, //   1
  B00000011, //   3
  B00000111, //   7
  B00001111, //  15
  B00010000, //  16
  B00010001, //  17
  B00010011, //  19
  B00010111, //  23
  B00011111, //  31
  B00100000, //  32
  B00110000, //  48
  B00110001, //  49
  B00110011, //  51
  B00110111, //  55
  B00111111, //  63
  B01000000, //  64
  B01100000, //  96
  B01110000, // 112
  B01110001, // 113
  B01110011, // 115
  B01110111, // 119
  B01111111, // 127
  B10000000, // 128
  B11000000, // 192
  B11100000, // 224
  B11110000, // 240
  B11110001, // 241
  B11110011, // 243
  B11110111, // 247
  B11111111, // 255
};

boolean verif_linha(byte leitura){
  boolean validade = false;
  for(short int k = 0; k < 37; k++){
    if(leitura == lista_validade[k]){
      validade = true;
      break;
    }
  }
  return(validade);
}

void setup(){
  left_servo.attach(9);
  right_servo.attach(10);
  DDRD = 0;      // portas 7..0 como inputs, aqui ligarei os 8 sensores.
  PORTD = 0xFF;  // pull-ups das portas 7..0 ligados. 0xFF = 255 = B11111111
  for(short int i = 0; i < 31; i++){  // os motores vão arrancar gradualmente, com muita suavidade...
    left_servo.write(LEFT_MIN + i);
    right_servo.write(RIGHT_MIN - i);
    delay(10);
  }
}

void loop(){
  if(verif_linha(PIND)){
    left_mod = (PIND >> 4);
    right_mod = (0x0F & PIND);
    (left_mod > right_mod ? lastseen_left = true : lastseen_left = false);
    (right_mod > left_mod ? lastseen_right = true : lastseen_right = false);
    left_servo.write(LEFT_MAX - left_mod);
    right_servo.write(RIGHT_MAX + right_mod);
    delay(10);
  }
  else{
    if(lastseen_left){                    // se a linha tiver sido vista pla última vez à esquerda
      do{
        left_servo.write(LEFT_MAX - 15);  // virar à esquerda
        right_servo.write(RIGHT_MAX);
        delay(10);
      }while(!verif_linha(PIND));         // até obter uma "linha" válida
    }
    else if(lastseen_right){
      do{
        left_servo.write(LEFT_MAX);
        right_servo.write(RIGHT_MAX + 15);
        delay(10);
      }while(!verif_linha(PIND));
    }else{
      do{                                 // se a "linha" não é válida (cruzamentos),
        left_servo.write(LEFT_MAX);       // e se a linha não foi vista nem à esquerda nem à direita,
        right_servo.write(RIGHT_MAX);     // então está no meio!!! PREGO A FUNDO!!!
        delay(10);
      }while(!PIND);
    }
  }
}
Ao declarar os pinos 7..0 como inputs com pull-ups ligados, o estado por omissão deles é 1.
Tendo em conta que ligarei o colector de cada fototransístor a uma destas portas e que os emissores estarão ligados ao ground, caso o sensor apanhe o reflexo (está fora da linha) o fototransístor "afundará" o "1" da porta para 0. Assim posso aceder ao estado das 8 I/Os instantaneamente lendo o registo PIND. A linha estará representada por 1s no byte que obtenho.

Suponhamos o seguinte caso: "A linha está a meio do sensor."
de PIND leria 0b00011000; os zeros representam a área branca e os uns representam a linha preta.
A linha está a meio, os dois motores rodam alegremente a igual velocidade, e seguimos felizes.

"A linha está a desviar-se para a esquerda."
Um possível valor que leriamos de PIND seria 0b01100000. Sabemos que tendo a linha à esquerda, temos que abrandar o motor esquerdo para ir curvando o carro até centrar a linha. Mas como é que aquele valor da leitura pode influenciar a velocidade do motor?
Solução:Um dos algoritmos que idealizei, seria tratar os 4 bits mais à esquerda de PIND como um valor separado dos outros 4 bits. Simples, basta um Shift à direita de 4 unidades: left_mod = (PIND >> 4); o que acontece é que os bits andam prá direita 4 unidades, assim:

  0b01100000 >> 1 = 0b00110000
  0b01100000 >> 2 = 0b00011000
  0b01100000 >> 3 = 0b00001100
  0b01100000 >> 4 = 0b00000110

E assim tenho um valor que varia entre 0 e 15 (4 bits, 16 valores discretos) que me diz o quanto devo abrandar o motor esquerdo.

"A linha está à direita"
Possível valor de PIND = 0b00001100. Ok, só temos que concentrarmo-nos nos 4 bits mais à direita. Usando operadores Bitwise, guardar os 4 bits mais à direita fazer-se-ía assim: right_mod = (0x0F & PIND). O que acontece quando estamos a fazer um AND de 0x0F (0b1111) com PIND, é que mesmo que PIND tenha 1s à esquerda, só os 4 da direita serão tidos em conta, pois um AND de 1 com 0 dá sempre 0, vejamos
PIND0b10101100
0x0F&_0b00001111
right_mod 0b00001100

Pronto, já temos os modificadores... mas continuamos com um problema... Se a linha estiver a meio (PIND = 0b00011000) então left_mod = 0b0001 e right_mod = 0b1000. O primeiro vale 1, o segundo vale 8... isso não é bom, é como se ali tivéssemos um eixo de simetria. Precisamos de inverter a ordem dos bits e isso dá um bocadinho de trabalho a pensar.

Mas afinal não precisamos mudar nada...



se os sensores são ligados um a um aos seus pinos, eu posso muito bem trocar a ordem em que os ligo! e assim já temos as coisas como deve ser.

Manipular os Motores

Depois de procurar e testar em que valores é que os motores atingem velocidade máxima, cheguei aos seguintes resultados:

Vel. mínimaVel. máxima
Motor esquerdo
94
124
Motor direito
89
59

Isto dá 30 passos entre as velocidades mínima e a máxima para cada direcção. Agora que já sabemos calcular os modificadores de direcção é só "escalar" os valores;
Multiplicaria os modificadores por 2, para ficar no intervalo que me interessa: [0..15] * 2 = [0..30]
Mas há um problema que me vai impedir de fazer isto, é que o robot transporta a prenda num reboque, por isso, se uma das rodas parar completamente, o reboque vai bater numa das rodas do servo, e vai ser arrastado. Ora, eu quero que o transporte da prenda seja suave e sem sobressaltos...

Então não podemos parar completamente as rodas: decidi não multiplicar o modificador e deixá-lo como está, assim o mínimo que as rodas podem andar é a metade da velocidade. (curvas à camionista :D)

Então e os cruzamentos?

Este é um assunto que me tem intrigado desde o início, mas que resolvi deixar pró fim.
Segundo o meu algoritmo actual, o motor X abranda proporcionalmente à "quantidade" de linha preta no seu lado, e o factor que entra em jogo nesse cálculo é a representação decimal dos 4 bits da esquerda ou da direita de PIND (em relação ao motor esquerdo ou direito, respectivamente).

isso significa que se PIND valer 0b01100110, cada motor vai abrandar em 6 unidades. (0b0110 = 6)
Então, mas seguindo a lógica dos sensores de reflexão, 0b01100110 mostra que estão ali duas linhas! :o
O meu código iria ignorar o facto de estar a ver duas linhas (um possível cruzamento) e simplesmente abrandaria para o lado da linha mais afastada que visse. Como resolver este problema??
Bem, como eu não estou a imaginar como seria o código para detectar se um dado byte tem só UM grupo de 1's (uma só linha) suponho que a maneira mais fácil seja simplesmente fazer um array com os valores válidos e na hora de seguir a linha verificar se a leitura de PIND está de acorde com a validade que quero implementar.

Um byte pode armazenar 256 valores discretos diferentes, mas só quero os valores que só têm uma sequência de 1's. E aqui estão os 36 valores possíveis:

Possíveis representações da linha na superfície:

0b00000001   1
0b00000010   2
0b00000011   3
0b00000100   4
0b00000110   6
0b00000111   7
0b00001000   8
0b00001100  12
0b00001110  14
0b00001111  15
0b00010000  16
0b00011000  24
0b00011100  28
0b00011110  30
0b00011111  31
0b00100000  32
0b00110000  48
0b00111000  56
0b00111100  60
0b00111110  62
0b00111111  63
0b01000000  64
0b01100000  96
0b01110000 112
0b01111000 120
0b01111100 124
0b01111110 126
0b01111111 127
0b10000000 128
0b11000000 192
0b11100000 224
0b11110000 240
0b11111000 248
0b11111100 252
0b11111110 254
0b11111111 255
     Representação lógica nos sensores (4 bits da direita invertidos):

0b00001000   8
0b00000100   4
0b00001100  12
0b00000010   2
0b00000110   6
0b00001110  14
0b00000001   1
0b00000011   3
0b00000111   7
0b00001111  15
0b00010000  16
0b00010001  17
0b00010011  19
0b00010111  23
0b00011111  31
0b00100000  32
0b00110000  48
0b00110001  49
0b00110011  51
0b00110111  55
0b00111111  63
0b01000000  64
0b01100000  96
0b01110000 112
0b01110001 113
0b01110011 115
0b01110111 119
0b01111111 127
0b10000000 128
0b11000000 192
0b11100000 224
0b11110000 240
0b11110001 241
0b11110011 243
0b11110111 247
0b11111111 255

Ou descubro uma função capaz de me gerar estes valores, ou simplesmente preencho um vector de 36 posições com estes valores todos, é a solução mais rápida :P
Neste caso usarei uma função booleana para verificar se a linha é válida, algo tipo isto:

Citar
boolean verif_linha(byte leitura){
  boolean validade = false;
  for(int k = 0; k < 37; k++){
    if(leitura == lista_validade[k]){
      validade = true;
      break;
    }
  }
  return(validade);
}


Material Utilizado




Final

/* Por preencher */



Vídeo

/* Por preencher */


« Última modificação: 23 de Dezembro de 2009, 02:27 por tcustodio »
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline tr3s

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 811
  • char x=1, y=5; x^=y^=x^=y;
Re: tcustodio - Tractor de prendas!
« Responder #1 em: 21 de Dezembro de 2009, 12:24 »
Muito bem explicado Tiago, mas só para evitar projectos semelhantes sugiro que só divulgues o código no fim do evento  ;)

Embora o teu raciocino esta certo, no que diz respeito a uma roda girar menos que a outra vais deparar-te com um problema.  Se a linha desaparecer relativamente rápido, isto é, se a curva for apertada não vais conseguir fazê-la. Isto porque da maneira que  estás a fazer vais ter um raio mínimo bastante grande para a curva (claro que também depende da distância entre as rodas). Tens de contar com o facto de que a linha pode "desaparecer".

Continua com o bom trabalho  ;)

P.S.: "o 'estado por defeito'"? ou será mais: "o 'estado, por omissão'"
É que defeito é quando algo está defeituoso, não funciona como deve de ser ou apresenta alguma anomalia :P
Tr3s
Daniel Gonçalves

Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
Re: tcustodio - Tractor de prendas!
« Responder #2 em: 21 de Dezembro de 2009, 17:10 »
Tens razão, tr3s, é "o estado, por omissão"! É o que dá andar a fazer directas nisto ;D

O meu código ainda não contempla a hipótese de perder a linha de vista, pois segundo o meu código, se ele não apanhar a linha (PIND = 0b00000000) os motores vão girar ali ao máximo, sempre em frente x)
Mas imagina que há um ângulo de 90º na linha: PIND = 0b11110000. O motor esquerdo parará, o direito andará ao máximo, ele fará a curva para a esquerda. Só falta contemplar no código que ele deve rodar pró lado onde a linha foi pla última vez vista. Falta-me também ignorar intersecções :D é que aqueles cruzamentos logo no início e a meio vão baralhar o meu algoritmo!

Quanto a ter divulgado já o código, acho que é difícil copiar isto sem ter percebido o que é que ele faz, seria mesmo um plágio, e ainda por cima isto é código de baixo nível! Um dos vossos critérios é que apresentemos os nossos métodos para saber "quem foi o primeiro a implementar X sistema", e ainda por cima tenho isto mais que justificado... tiro o código, porque ainda não está completo.
« Última modificação: 21 de Dezembro de 2009, 20:24 por tcustodio »
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline metRo_

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 3.753
Re: tcustodio - Tractor de prendas!
« Responder #3 em: 21 de Dezembro de 2009, 19:24 »
A tua ideia para usares a linha está muito boa.
Aquele esquema para as alimentações é o final?

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.372
    • Tiago Rodrigues
Re: tcustodio - Tractor de prendas!
« Responder #4 em: 21 de Dezembro de 2009, 19:53 »
Parabéns pela tua abordagem, pareceu-me bastante interessante.

Concordo com o metRo_, estás a esquecer-te ali de umas resistencias, já fizeste as contas à tensão e corrente dos leds dos sensores?

Quanto à história da regra contra plágios, é uma segurança para quem quer postar as coisas que vai fazendo, é obvio que há mts maneiras de implementar ideias parecidas, e há mts ideias diferentes que podem ter implementações semelhantes. Não se preocupem com o que têm menos valor, estamos aqui para nos divertirmos, fazer robots, e ganhar uns premiozinhos.

Força com o projecto, estou a gostar!

Offline microbyte

  • Mini Robot
  • *
  • Mensagens: 1.322
    • http://ricardo-dias.com/
Re: tcustodio - Tractor de prendas!
« Responder #5 em: 21 de Dezembro de 2009, 20:13 »
Bem :D Também acho que deves pôr ali umas resistências...
Mesmo que não limites a voltagem, deves limitar a corrente...

O código que fiz também se baseia nesse princípio, do diminuir velocidade de um dos lados... Mas o código foi feito de uma vez e ainda não está testado. Tenho andado atarefado com a "estrutura" do robô. Primeiro tenho de acabar as rodas, depois concluír o resto para ligar os circuitos e fazer uns testes para ver depois o que tenho de alterar no código. ;)

Estás num bom caminho, e é bom saber que o meu blog já está a ajudar o pessoal ;)

Parabéns e boas renas... :D

Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
Re: tcustodio - Tractor de prendas!
« Responder #6 em: 21 de Dezembro de 2009, 20:59 »
Mas o código foi feito de uma vez e ainda não está testado. Tenho andado atarefado com a "estrutura" do robô.
Também ainda não testei o meu, já tenho a estrutura toda completa, foi o que me deu mais gozo fazer :D

Quanto à tensão e corrente dos leds, ainda não me decidi se vou alimentar o atmega com 4.8v (4xAA de 1.2V) ou se vou usar um 7805 e usar 4 pilhas de 1.5v. O 7805 trabalha bem com 6v?
A tensão dos leds vai de 1.2v até 1.5v, portanto posso alimentar 4 leds duma só vez com 4.8v, 5v, etc.
Que valor acham que deva usar nas resistências? O LED Calculator diz-me que devo usar uma resistência de 1ohm...

A pista não tem ângulos rectos, mas vou ter que modificar o código, porque no Sketch "Knob" da biblioteca Servo reparei que há muitos "ângulos" mortos, com isto quero dizer que a velocidade máxima é 179, 89 é parado, mas para abrandar o motor, só quando a velocidade tá a 150 ou 140 (tou a estimar, nunca verifiquei) é que o motor de facto começa àbrandar. Se assim for, tenho que alterar a maneira como os motores abrandam.

Agora, a próxima modificação ao código vai ter que ser ensinar o robot a fazer as curvas à camionista. Porquê? Porque vou usar um reboque pa transportar as prendas!!! Com o código actual, apanhar um ângulo recto na linha implica que uma das rodas dos servos bata no reboque, e eu não quero isso :(
« Última modificação: 22 de Dezembro de 2009, 20:18 por tcustodio »
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.372
    • Tiago Rodrigues
Re: tcustodio - Tractor de prendas!
« Responder #7 em: 21 de Dezembro de 2009, 21:41 »
Sim, até com um bocadinho menos k os 5V ele aguenta-se...

Deves de ter confundido as unidades... devem de rondar os 100Ohms visto que eles são de 50mA e estás a alimentar com 5V.

Exacto, as curvas nos servos não são lineares visto que o servo só começa a reduzir a velocidade quando se esta a aproximar do ponto de mudança, neste caso os 90º... a partir de certa distancia deste ele fica sempre a full speed. Podes ver que valores te interessam e usar a função map(x,x,x,x) do arduino.


Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
Re: tcustodio - Tractor de prendas!
« Responder #8 em: 21 de Dezembro de 2009, 22:40 »
Pensei agora numa cena: uso 4 pilhas AA de 1.5v: ao fim de 3 pilhas no suporte, soldo um fio para alimentar o arduino e os leds (4.5v), ao fim das 4 pilhas alimento os servos (6v). Que tal?

Ah, na datasheet do TCRT5000 há referência às características dos leds:
INPUT (EMITTER)
TEST CONDITION
SYMBOL
TYP.
MAX.
UNIT
Forward voltage
IF = 60 mA
VF
1.25
1.5
V
Aqui diz que a tensão típica é de 1.25v caso a corrente seja de 60mA... se eu usasse 4 em série, tinha um consumo de 5v e 60mA, certo? Uma resistência não me alteraria também a tensão? Então se usasse uma resistência era só por precaução, certo? Estas grandezas e medidas ainda não estão bem instauradas em mim  :-X

Se adoptar aquele método de alimentar o arduino e os leds com 3 pilhas, já vou ter que alimentar os leds doutra maneira.
No LED Calculator obtenho este esquema:

Sugestões?
« Última modificação: 21 de Dezembro de 2009, 22:41 por tcustodio »
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.372
    • Tiago Rodrigues
Re: tcustodio - Tractor de prendas!
« Responder #9 em: 22 de Dezembro de 2009, 12:21 »
O arduino tem um regulador de tensão, pelo que aconselho que o alimentos antes com os 6V no pin Vin e Gnd ou através da ficha de alimentação externa (jack) que este dispõe.

O arduino tb disponibiliza os 5V estáveis nos pinos 5V e Gnd pelo que podes utilizar estes 5V para alimentar os teus leds.

Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
Re: tcustodio - Tractor de prendas!
« Responder #10 em: 22 de Dezembro de 2009, 13:25 »
então assim farei :D obrigado, TigPT
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline tcustodio

  • Mini Robot
  • *
  • Mensagens: 344
  • "beware of programmers who carry a soldering iron"
    • Youtube
Re: tcustodio - Tractor de prendas!
« Responder #11 em: 22 de Dezembro de 2009, 20:36 »
Já desvendei o caso dos "ângulos mortos" dos servos...

Reparei que entre 0 e 59, o servo rodava à velocidade máxima, o que dá 30 níveis de velocidade até parar.
O esperado era que no sentido oposto se comportasse do mesmo modo, atingindo a velocidade máxima no nível 119, mas não foi isso que aconteceu. O servo só começou a rodar quando passei o nível 94, e atingiu a velocidade máxima no nível 124 (novamente, 30 níveis de velocidade).

Espero que isto venha a ser útil para todos
Tiago Custódio,
- Não me dêem álcool se estiver um piano por perto.

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.372
    • Tiago Rodrigues
Re: tcustodio - Tractor de prendas!
« Responder #12 em: 24 de Dezembro de 2009, 12:42 »
Obrigado por partilhares, certamente que será!