LusoRobótica - Robótica em Português
Robótica => Projectos de robótica => Projectos em desenvolvimento => Tópico iniciado por: Njay 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.
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
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:
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.
// 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
};
// 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);
}
-
Parece muito bom para quem quer algo simples.
-
Adicionei o código para o LED como sensor de luz.
-
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()`:
-
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.").
-
Onde é que eu já vi a teoria dos LEDs Bidireccionais (http://ricardodias.wordpress.com/2009/10/28/leds-bi-direccionais/)? ::)
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?
-
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.
-
Basicamente tornar o uso de servos de rotação contínua mais "user-friendly" no que diz respeito à programação... :P Já percebi...