collapse

* Links de Robótica

* Posts Recentes

Pendentes de compras colectivas por Hugu
[Hoje às 00:51]


Preços e fabricantes de pcb por vasco
[Ontem às 23:18]


Palavras Cruzadas por LVirtual
[Ontem às 20:54]


[Projecto] Bomba Airsoft por jm_araujo
[Ontem às 19:01]


Apresentação por Tech_JA
[23 de Setembro de 2017, 09:19]


Medir Agua que está no Poço por filjoa
[21 de Setembro de 2017, 20:58]


URGENTE - display de 7 segmentos com backpack por helderjsd
[20 de Setembro de 2017, 12:30]


Isaac Asimov - I, Robot por senso
[18 de Setembro de 2017, 03:41]


ic SL440 da Plessey? por senso
[16 de Setembro de 2017, 13:11]


Compra Colectiva RS-Amidata por brunus
[15 de Setembro de 2017, 22:31]

Autor Tópico: Breebot  (Lida 2284 vezes)

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

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.089
    • Tróniquices
Breebot
« em: 07 de Novembro de 2009, 23:57 »
O breebot é apenas uma plataforma básica com 2 rodas e um core breaduino que tenho andado a desenvolver muito lentamente nas horas vagas, apenas para experimentar.

Acho que é o tipo de robot mais barato que se consegue fazer, pois só tem uma matriz de contactos, 2 servos de rotação contínua e um breaduino. Mais tarde ponho umas fotos.

Para já abri este tópico para vos deixar uma classe simples para facilitar o controlo de um servo de rotação contínua, baseada na biblioteca MegaServo.

A utilização é muito simples, basta copiar o código para o vosso sketch e declarar as variáveis que se deseje dando-lhes o pino em que o servo está; depois é quase só usar o método SetSpeed com um parametro entre -45 e 45. Este método desliga o servo quando colocamos a velocidade a zero, para ele ficar mesmo parado.

Código: [Seleccione]
ContinuousRotationServo  servoDir(10);    // servo no pino 10
ContinuousRotationServo  servoEsq(11);    // outro servo, no pino 11
...
servoEsq.SetSpeed(45);    // para a frente, a toda a velocidade
servoDir.SetSpeed(-45);   // para a trás, a toda a velocidade

Mais abaixo está uma classe (com um sketch de teste) para lidar com um LED como um sensor de luz. O conceito de usar um LED como sensor de luz é simples e uma 1ª implementação também pode ser muito simples, só que tem alguns problemas que tentei resolver, daí o tamanho do código.
Em 1º lugar a classe é genérica e aceita até 4 LEDs como sensores. O que isto quer dizer é que basta criar uma variável dando-lhe os pinos em que o LED está, por exemplo

Código: [Seleccione]
LedSensor  led(8, 9);      // Led nos pinos 8 e 9, ánodo no 8 e cátodo com resistência no 9

e a partir daqui basta chamar o método (função) GetIntensity() para obter um valor entre 0 (escuro) e 511 (claro), por exemplo:

Código: [Seleccione]
LedSensor  led(8, 9);

void loop (void)  {
    byte  cls[] = {0x1b, '[', '2', 'J'};
    Serial.write(cls, 4);
    Serial.print("Light: ");
    Serial.print((int)led.GetIntensity());
    delay(50);
}

Este método GetIntensity() tem uma particularidade que é não bloquear. O algoritmo para medir a intensidade da luz implica fazer um ciclo à espera que um dos pinos do LED fique a 0; isto para um teste simples não tem problema, agora quando queremos fazer outras coisas ao mesmo tempo já é uma chatice, pois obriga-nos a bloquear no ciclo e este bloqueio tem um tempo muito variável, que pode ir desde alguns microsegundos (muita luz) até mais de 50 milisegundos (escuridão). Para ultrapassar este problema utilizei umas interruções que o AVR tem para detecção de mudança de estado num pino.

O circuito é um LED com o ánodo ligado directamente ao pino do Arduino e o cátodo ligado ao outro pino do Arduino mas através de uma resistência de 180 Ohm (pode ser outra de valor aproximado). O LED que experimentei e que se portou melhor foi o verde de 5mm de diâmetro.

Experimentem o sketch que está lá abaixo e deiam-me o vosso feedback. Actualmente uso-o para pôr o robot a mexer quando lhe aponto uma lanterna.





Código: [Seleccione]
// Continuous rotation servo class.
// By Njay in 2009 (EmbeddedDreams.com)
// Licensed under CC BY NC SA (http://creativecommons.org/licenses/by-nc-sa/2.5/pt/
#include <Servo.h>


/**
 * Wraps a MegaServo object to control a continuous rotation servo.
 * The servo should be calibrated to be stopped when Megaservo::write()
 * is set to 90.
 * When speed is set to 0 to stop the servo, the servo is detached to
 * make sure it will be completly stopped.
 */
class ContinuousRotationServo
{
  public:
    ContinuousRotationServo (unsigned char pin)  {
        mSpeed = 0;
        SetPin(pin);
    }

    ContinuousRotationServo (void)  {
        mSpeed = 0;
    }

    inline void SetPin (unsigned char pin)  {
        mPin = pin;
    }

    // Set the speed. 0 to be stopped, > 0 to rotate in one direction
    // and < 0 to rotate on the other.
    void SetSpeed (signed char speed)  {
        if (speed == 0)  {
            if (mSpeed != 0)  {
                //Serial.println("dettach");
                mServo.detach();
            }
        }
        else  {
            if (mSpeed == 0)  {
                //Serial.println("attach");
                mServo.attach(mPin);
            }
            //Serial.println("set speed");
            mServo.write(90 + speed);
        }
        mSpeed = speed;
    }

    inline void Detach (void)  {
        mServo.detach();
    }

  private:
    Servo          mServo;
    unsigned char  mPin;
    signed char    mSpeed;    // current speed
};



Código: [Seleccione]
// LED as a light sensor.
// By Njay in 2009 (embeddeddreams.com)
// Licensed under CC BY NC SA (http://creativecommons.org/licenses/by-nc-sa/2.5/pt/)

#define bool  byte
#define on  1
#define off 0

typedef unsigned char  byte;        // I like to call things by their real name :)
typedef unsigned int   word;
typedef unsigned long  dword;


/**
 * Handles an LED as a light sensor.
 * This class handles conversions in the background so that you can get
 * the current light being sensed without blocking your loop.
 * It also handles up to 4 LEDs on Arduino's digital ports, and all you
 * have to do is to create an object of this class passing it the pins
 * where the LED is connected.
 * This class uses port's D and B Interrupt on pin change.
 * Tested with green 5mm LED and cathode in series with a 180 Ohm resistor.
 */
class LedSensor
{
  private:
    enum State {
        eIdle = 0,
        eDischarging,
        eLightOn,
    };

    enum Consts {
        cMaxSensors = 4,
        cDischargeTimeUnit = 128,
    };

  public:
    LedSensor (byte anodePin, byte cathodePin)  {
        byte  i;
        for (i = 0; (i < cMaxSensors) && sLeds[i]; i++);

        if (i >= cMaxSensors)  {
            // too many LED sensors
            return;
        }

        mAnodePin = anodePin;
        mCathodePin = cathodePin;

        mPortIdx = cathodePin / 8;
        mCathodePinMask = 1 << (cathodePin % 8);

        mRawLight = 0;
        mLedOn = off;

        PCICR |= (1 << PCIE0) | (1 << PCIE2);    // enable port B&D pin change interrupts
        StartConversion();

        noInterrupts();
        *(volatile LedSensor**)(&sLeds[i]) = this;    // atomic write of 2 8-bit values
        interrupts();
    }

    void Light (bool state)  {
        mLedOn = state;
        if (mState != eIdle)  return;
        pinMode(mAnodePin, OUTPUT);
        pinMode(mCathodePin, OUTPUT);
        digitalWrite(mAnodePin, state? HIGH : LOW);
        digitalWrite(mCathodePin, LOW);
    }

    void ReverseBias (void)  {
        pinMode(mAnodePin, OUTPUT);
        pinMode(mCathodePin, OUTPUT);
        digitalWrite(mAnodePin, LOW);
        digitalWrite(mCathodePin, HIGH);
    }

    void StartConversion (void)  {
        switch (mPortIdx)  {
            case 0 : PCMSK2 &= ~mCathodePinMask; break;
            case 1 : PCMSK0 &= ~mCathodePinMask; break;
        }
        ReverseBias();
        delayMicroseconds(5);
        pinMode(mCathodePin, INPUT);
        digitalWrite(mCathodePin, LOW);
        mStart = micros() / cDischargeTimeUnit;
        mState = eDischarging;
        switch (mPortIdx)  {
            case 0 : PCMSK2 |= mCathodePinMask; break;
            case 1 : PCMSK0 |= mCathodePinMask; break;
        }
    }

    void StopConversion (void)  {
        switch (mPortIdx)  {
            case 0 : PCMSK2 &= ~mCathodePinMask; break;
            case 1 : PCMSK0 &= ~mCathodePinMask; break;
        }
        mState = eIdle;
        Light(mLedOn);
    }

    word GetIntensity (void)  {
        noInterrupts();
        word  light = (mRawLight < 511)? 511 - mRawLight : 0;
        interrupts();

        if (mState == eIdle)  {
            // A conversion has finished so start a new one.
            StartConversion();
        }
        else  {
            // A conversion is occurring.
            // If more than 25ms have passed since last conversion result,
            // we're in total darkness... force re-start and return 0.
            dword  now = micros() / cDischargeTimeUnit;
            dword  timeSinceLastConvResult;
            if (now < mStart)  {
                timeSinceLastConvResult = now + (0xffffffff/cDischargeTimeUnit - mStart);
            }
            else  {
                timeSinceLastConvResult = now - mStart;
            }
            if (timeSinceLastConvResult > 25000UL / cDischargeTimeUnit)  {
                StopConversion();
                StartConversion();
                //Serial.println("fr ");
                light = 0;
            }
        }
        return light;
    }

  private:
    byte    mAnodePin, mCathodePin;
    bool    mLedOn;
    volatile word    mRawLight;
    volatile dword   mStart;
    volatile byte    mState;
    volatile byte    mPortIdx;
    volatile byte    mCathodePinMask;

  public:
    static LedSensor*  sLeds[cMaxSensors];

    static void Update (byte idx, byte portVal)  {
        dword  now = micros() / cDischargeTimeUnit;
        for (byte i = 0; i < cMaxSensors; i++)  {
            if (sLeds[i] && (sLeds[i]->mState == eDischarging))  {
                if (sLeds[i]->mPortIdx == idx)  {
                    if ((sLeds[i]->mCathodePinMask & portVal) == 0)  {
                        word  light;
                        if (now < sLeds[i]->mStart)  {    // has the time wrapped?
                            light = now + (0xffffffff/cDischargeTimeUnit - sLeds[i]->mStart);
                        }
                        else  {
                            light = now - sLeds[i]->mStart;
                        }
                        sLeds[i]->mRawLight = light;
                        sLeds[i]->StopConversion();
                    }
                }
            }
        }
    }
};


LedSensor*  LedSensor::sLeds[] = {NULL, NULL, NULL, NULL};


ISR(PCINT0_vect)  // port B - arduino pins 8..13(15)
{
    LedSensor::Update(1, PINB & PCMSK0);
}


ISR(PCINT2_vect)  // port D - arduino pins 0..7
{
    LedSensor::Update(0, PIND & PCMSK2);
}


void setup (void) {
    Serial.begin(19200);
    Serial.println("LED sensor test");
}


LedSensor  led(9, 8);

void loop (void)  {
    byte  cls[] = {0x1b, '[', '2', 'J'};
    Serial.write(cls, 4);
    Serial.print("Light: ");
    Serial.print((int)led.GetIntensity());
    delay(50);
}

« Última modificação: 29 de Março de 2011, 00:20 por Njay »

Offline p0wd3r

  • Mini Robot
  • *
  • Mensagens: 74
Re: Breebot
« Responder #1 em: 08 de Novembro de 2009, 00:06 »
Parece muito bom para quem quer algo simples.

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.089
    • Tróniquices
Re: Breebot
« Responder #2 em: 08 de Novembro de 2009, 16:41 »
Adicionei o código para o LED como sensor de luz.

Offline joao rexinho

  • Mini Robot
  • *
  • Mensagens: 183
  • http://sites.google.com/site/roboticaelectronica/
Re: Breebot
« Responder #3 em: 10 de Novembro de 2009, 02:13 »
Citar
Mais abaixo está uma classe (com um sketch de teste) para lidar com um LED como um sensor de luz. O conceito de usar um LED como sensor de luz é simples e uma 1ª implementação também pode ser muito simples, só que tem alguns problemas que tentei resolver, daí o tamanho do código.
Em 1º lugar a classe é genérica e aceita até 4 LEDs como sensores. O que isto quer dizer é que basta criar uma variável dando-lhe os pinos em que o LED está, por exemplo

Código: [Seleccione]
LedSensor  led(8, 9);      // Led nos pinos 8 e 9, ánodo no 8 e cátodo com resistência no 9

e a partir daqui basta chamar o método (função) GetIntensity() para obter um valor entre 0 (escuro) e 511 (claro), por exemplo:

Código: [Seleccione]
LedSensor  led(8, 9);

void loop (void)  {
    byte  cls[] = {0x1b, '[', '2', 'J'};
    Serial.write(cls, 4);
    Serial.print("Light: ");
    Serial.print((int)led.GetIntensity());
    delay(50);
}

Este método GetIntensity() tem uma particularidade que é não bloquear. O algoritmo para medir a intensidade da luz implica fazer um ciclo à espera que um dos pinos do LED fique a 0; isto para um teste simples não tem problema, agora quando queremos fazer outras coisas ao mesmo tempo já é uma chatice, pois obriga-nos a bloquear no ciclo e este bloqueio tem um tempo muito variável, que pode ir desde alguns microsegundos (muita luz) até mais de 50 milisegundos (escuridão). Para ultrapassar este problema utilizei umas interruções que o AVR tem para detecção de mudança de estado num pino.
Eu nao consigo que o sketch leia diz"error:´LedSensor`does not a name a type in function ´void loop()`:
 At global scope:
 In function ´void loop()`:
http://sites.google.com/site/roboticaelectronica/home
Estudante de Engenharia Eletrônica e Telecomunicações em Aveiro

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.089
    • Tróniquices
Re: Breebot
« Responder #4 em: 11 de Novembro de 2009, 00:45 »
João, tens que copiar todo o 2º bloco de código no final do post (o bloco que começa por "// LED as a light sensor.").

Offline microbyte

  • Mini Robot
  • *
  • Mensagens: 1.322
    • http://ricardo-dias.com/
Re: Breebot
« Responder #5 em: 11 de Novembro de 2009, 13:10 »
Onde é que eu já vi a teoria dos LEDs Bidireccionais::)

Tens aí código para caraças... Mas ainda não percebi muito bem o objectivo... Substituir o digitalWrite(pin, 90+velocidade); ?? Podes explicar melhor?

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.089
    • Tróniquices
Re: Breebot
« Responder #6 em: 12 de Novembro de 2009, 00:59 »
O objectivo é encapsular o facto de se desligar o servo (detach) quando a velocidade é 0, e evitar andar sempre a escrever código para verificar se a velocidade é zero e fazer o detach/attach.
É uma questão de organização e tamanho do código, em vez de teres vários items "espalhados" pelo código e teres que estar sempre a fazer o "if" do SetSpeed(), isso fica tudo escondido dentro da classe e só tens que declarar uma variável dessa classe e usar os métodos, e outras pequenas vantagens como a classe já suportar poder-se mudar o pino em que está o servo.

Offline microbyte

  • Mini Robot
  • *
  • Mensagens: 1.322
    • http://ricardo-dias.com/
Re: Breebot
« Responder #7 em: 12 de Novembro de 2009, 01:11 »
Basicamente tornar o uso de servos de rotação contínua mais "user-friendly" no que diz respeito à programação...  :P Já percebi...