Mechatronics Exercises

Workspace Navigation

IR-nopeusmittari

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Ohjelmassa valoportteja tarkkaillaan keskeytyksien avulla. Nappuloiden painallukset tunnistetaan pollaamalla. Yksinkertaisuuden vuoksi kontrollerit eivät kommunikoi ammuksen painon muutoksista toisilleen, vaan ammuksen paino on korttikohtainen. Tämän ei käytettäessä pitäisi aiheuttaa suuria ongelmia. 

 

 

 

LOPPUTULOS 

Kronometri toimii jotakuinkin tavoitteiden mukaisestiKronometri kykenee mittaamaan 6 mm airsoft kuulan ainakin 100 m/s nopeudella varsin luotettavasti. Välillä mittaus epäonnistuu mutta kuitenkin niin harvoin, että se ei käyttöä häiritse.  Airsoft kuula täytyy kuitenkin ampua lähellä fotodiodeja (sininen puoli valoportista), jotta kronometri huomaa sen. Isommat ammukset kuten Nerf nuolet kronometri tunnistaa mittaus alueen keskeltäkin. Suorassa auringonvalossa kronometri ei toimi ollenkaan, tämä oli sinänsä oletettavaa, koska valoportit ovat avoimet.  

Image Removed

Image Removed

 

 

JATKOKEHITTELY 

Kronometri pitää vielä kalibroida jollakin tiedetyllä nopeudella, koska Arduinon sisäinen kello ei ole kovinkaan tarkka. 

Mittausetäisyyden optimointi. Nyt pienemmät ammukset on ammuttava melko läheltä fotodiodeja niiden rekisteröimiseksi. Fotodiodin vastusta suurentamalla mittausetäisyys parani, mutta samalla tietyn raja-arvon ylittyessä (n. yli 400k Ω) signaali ei enää palautunut ammuksen ohituksen jälkeen. 

Saattaisi toimia paremmin, jos IR-ledejä olisi vain yksi per portti, vaatisi tosin rakenteen uudelleen suunnittelemisen. 

Tulinopeuden (rpm) ja kierrosnopeuden (avoimien porttien avulla) mittaaminen olisi hyvä lisäominaisuus. 

// include the library code: #include <LiquidCrystal.h> // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); const byte firstGate = 6; const byte secondGate = 7; const byte leftButton = 10; const byte rightButton = 9; const byte lowerButton = 8; volatile unsigned long lastTime1 = 0; volatile unsigned long lastTime2 = 0; volatile unsigned long difference = 0; volatile bool hasResult = 0; volatile float mass = 0.28; // grammaa float weightIncrement = 0.01; // painon muutos per painallus grammoina float kineticEnergy = 0.00; // joulea float velocity = 0.0; void setup() { Serial1.begin(19200); pinMode(firstGate, INPUT); //eka portti pinMode(secondGate, INPUT); // toka portti pinMode(leftButton, INPUT); // vasen ylänappi pinMode(rightButton, INPUT); // oikea ylänappi pinMode(lowerButton, INPUT); // alin keskinappi // LCD konfigurointi: lcd.begin(16, 2); lcd.setCursor(0, 0); lcd.print(velocity); lcd.print(" m/s "); attachInterrupt(digitalPinToInterrupt(firstGate), timeStamp1, RISING); attachInterrupt(digitalPinToInterrupt(secondGate), timeStamp2, RISING); } void timeStamp1() { lastTime1 = micros(); } void timeStamp2() { lastTime2 = micros(); difference = lastTime2 - lastTime1; hasResult = 1; } void loop() { // Valitaan painonsäädön inkrementti neljästä vaihtoehdosta 0.01 g, 0.1 g, 1 g tai 10 g: if (digitalRead(lowerButton)) { if (weightIncrement < 10) { weightIncrement = weightIncrement * 10; } else if (weightIncrement >= 10) { weightIncrement = 0.01; } delay(300
Code Block
languagecpp
titleNano_every
collapsetrue
Code Block
languagecpp
titleNano_every
collapsetrue
// include the library code:
#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const byte firstGate = 6;
const byte secondGate = 7;

const byte leftButton = 10;
const byte rightButton = 9;
const byte lowerButton = 8;

volatile unsigned long lastTime1 = 0;
volatile unsigned long lastTime2 = 0;
volatile unsigned long difference = 0;
volatile bool hasResult = 0;

volatile float mass = 0.28; // grammaa
float weightIncrement = 0.01; // painon muutos per painallus grammoina
float kineticEnergy = 0.00; // joulea
float velocity = 0.0;

void setup() {
  Serial1.begin(19200);

  pinMode(firstGate, INPUT); //eka portti
  pinMode(secondGate, INPUT); // toka portti
  pinMode(leftButton, INPUT); // vasen ylänappi
  pinMode(rightButton, INPUT); // oikea ylänappi
  pinMode(lowerButton, INPUT); // alin keskinappi


  // LCD konfigurointi:
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print(velocity);
  lcd.print(" m/s ");

  attachInterrupt(digitalPinToInterrupt(firstGate), timeStamp1, RISING);
  attachInterrupt(digitalPinToInterrupt(secondGate), timeStamp2, RISING);
}



void timeStamp1() {
  lastTime1 = micros();
}


void timeStamp2() {
  lastTime2 = micros();
  difference = lastTime2 - lastTime1;
  hasResult = 1;

}

void loop() {

  // Valitaan painonsäädön inkrementti neljästä vaihtoehdosta 0.01 g, 0.1 g, 1 g tai 10 g:


  if (digitalRead(lowerButton)) {
    if (weightIncrement < 10) {
      weightIncrement = weightIncrement * 10;
    } else if (weightIncrement >= 10) {
      weightIncrement = 0.01;
    }
    delay(300);
  }


  // Napeilla asetettava paino. Jos menisi negatiiviseksi, asetetaan 0.01 g.
  if (digitalRead(leftButton) && (mass - weightIncrement >= 0.01)) {
    mass = mass - weightIncrement;
    delay(300);
  } else if (digitalRead(leftButton) && (mass - weightIncrement < 0.01)) {
    mass = 0.01;
    delay(300);
  } else if (digitalRead(rightButton)) {
    mass = mass + weightIncrement;
    delay(300);
  }

 

  if (hasResult) {
    velocity = 160500.00 / difference; //väli on 160500 mikrometria
    kineticEnergy = 0.5 * mass / 1000 * pow(velocity, 2); // massa muutetaan samalla grammoista kiloiksi
    hasResult = 0;
    Serial1.println(velocity);

    lcd.setCursor(0, 0);
    lcd.print(velocity);
    lcd.print(" m/s    ");

    lcd.setCursor(0, 1);
    lcd.print(kineticEnergy);
    lcd.print(" J  ");
  }

  
// Napeilla asetettava paino. Jos menisi negatiiviseksi, asetetaan 0.01 g.
  if (digitalRead(leftButton) && (mass - weightIncrement >= 0.01)) {
    mass = mass - weightIncrement;
    delay(300);
  } else if (digitalRead(leftButton) && (mass - weightIncrement < 0.01)) {
   lcd.setCursor(12, 0);
  lcd.print(weightIncrement);
  
  lcd.setCursor(8, 1);
  lcd.print(mass);
  lcd.print(" g   ");



}

 

Code Block
languagecpp
titleNano 33 BLE
collapsetrue
#include <ArduinoBLE.h>

float velocity = 0.0;
unsigned int velocityInt = 0;
unsigned int energy = 0.0;
float mass = 0.0128;

BLEService   delay(300velocityService("1101");
BLEUnsignedIntCharacteristic  } else if (digitalRead(rightButton)) {
    mass = mass + weightIncrement;
    delay(300);
  }

 

  if (hasResult) {
    velocity = 160500.00 / difference; //väli on 160500 mikrometria
    kineticEnergy = 0.5 * mass / 1000 * pow(velocity, 2); // massa muutetaan samalla grammoista kiloiksi
    hasResult = 0;
    Serial1.println(velocity);

    lcd.setCursor(0, 0);
    lcd.print(velocity);
    lcd.print(" m/s    ");

    lcd.setCursor(0, 1);
    lcd.print(kineticEnergy);
    lcd.print(" J  ");
  }

  
  lcd.setCursor(12, 0);
  lcd.print(weightIncrement);
  
  lcd.setCursor(8, 1);
  lcd.print(mass);
  lcd.print(" g   ");



}
Code Block
languagecpp
titleNano 33 BLE
collapsetrue
#include <ArduinoBLE.h>

float velocity = 0.0;
unsigned int velocityInt = 0;
unsigned int energy = 0.0;
float mass = 0.28;

BLEService velocityService("1101");
BLEUnsignedIntCharacteristic velocity_ms("2101", BLERead | BLENotify);
BLEUnsignedIntCharacteristic energy_mJ("3101", BLERead | BLENotify);
BLEUnsignedIntCharacteristic mass_mg("4101", BLERead | BLEWrite);

void setup() {
  Serial1.begin(19200);

  BLE.begin();

  BLE.setLocalName("KronoBLE");
  BLE.setAdvertisedService(velocityService);
  velocityService.addCharacteristic(velocity_ms);
  velocityService.addCharacteristic(energy_mJ);
  velocityService.addCharacteristic(mass_mg);
  BLE.addService(velocityService);
  BLE.advertise();
}

void loop() {
  BLEDevice central = BLE.central();
  delay(50);

  if (Serial1.available() >= 4) { //float vie 4 byteä siksi 4
    velocity = Serial1.parseFloat();
  }

  if (central) {

    while (central.connected()) {
      delay(50);
      if (Serial1.available() >= 4) { //float vie 4 byteä siksi 4

        velocity = Serial1.parseFloat();
        velocityInt = velocity;
        mass = mass_mg.value() / 1000.0;
        energy = 0.5 * mass * velocity * velocity;
        velocity_ms.writeValue(velocityInt);
        energy_mJ.writeValue(energy);
      


      }

    }

  }

}velocity_ms("2101", BLERead | BLENotify);
BLEUnsignedIntCharacteristic energy_mJ("3101", BLERead | BLENotify);
BLEUnsignedIntCharacteristic mass_mg("4101", BLERead | BLEWrite);

void setup() {
  Serial1.begin(19200);

  BLE.begin();

  BLE.setLocalName("KronoBLE");
  BLE.setAdvertisedService(velocityService);
  velocityService.addCharacteristic(velocity_ms);
  velocityService.addCharacteristic(energy_mJ);
  velocityService.addCharacteristic(mass_mg);
  BLE.addService(velocityService);
  BLE.advertise();
}

void loop() {
  BLEDevice central = BLE.central();
  delay(50);

  if (Serial1.available() >= 4) { //float vie 4 byteä siksi 4
    velocity = Serial1.parseFloat();
  }

  if (central) {

    while (central.connected()) {
      delay(50);
      if (Serial1.available() >= 4) { //float vie 4 byteä siksi 4

        velocity = Serial1.parseFloat();
        velocityInt = velocity;
        mass = mass_mg.value() / 1000.0;
        energy = 0.5 * mass * velocity * velocity;
        velocity_ms.writeValue(velocityInt);
        energy_mJ.writeValue(energy);
      


      }

    }

  }

}

 

 

LOPPUTULOS 


Image Added

Image Added

 

Kronometri toimii jotakuinkin tavoitteiden mukaisesti. Kronometri kykenee mittaamaan 6 mm airsoft kuulan ainakin 100 m/s nopeudella varsin luotettavasti. Välillä mittaus epäonnistuu mutta kuitenkin niin harvoin, että se ei käyttöä häiritse.  Airsoft kuula täytyy kuitenkin ampua lähellä fotodiodeja (sininen puoli valoportista), jotta kronometri huomaa sen. Isommat ammukset kuten Nerf nuolet kronometri tunnistaa mittausalueen keskeltäkin. Herkkyyttä voisi parantaa lisäämällä kronometriin jonkinlaisen vahvistimen tai kalibroimalla fotodiodien edessä olevia vastuksia paremmin.

Mittaustarkkuudesta on vaikea sanoa, koska meillä ei ole mitään mihin verrata kronometrin antamia tuloksia. Mittaustuloksissa esiintyy hieman vaihtelua, mutta se on noin 5% luokka, jonka verran airsoft aseen lähtönopeus voi heitellä.

 Suorassa auringonvalossa kronometri ei toimi ollenkaan, tämä oli sinänsä oletettavaa, koska valoportit ovat avoimet. Ongelman voi varmaankin korjata rakentamalla varjostavan ulkokuoren valoporttien ja mittausalueen ympärille.

Bluetooth toimii, mutta se on tuntemattomasta syystä hieman hidas. Käyttöliittymä on surkea, koska tällä hetkellä käyttöliittymänä käytetään BLE debuggaustyökalua LightBlue. Käyttöliittymää tietenkin voisi parantaa kehittämällä sopivan Android apin.

 


JATKOKEHITTELY 

Kronometri pitää vielä kalibroida jollakin tiedetyllä nopeudella, koska Arduinon sisäinen kello ei ole kovinkaan tarkka. 

Mittausetäisyyden optimointi. Nyt pienemmät ammukset on ammuttava melko läheltä fotodiodeja niiden rekisteröimiseksi. Fotodiodin vastusta suurentamalla mittausetäisyys parani, mutta samalla tietyn raja-arvon ylittyessä (n. yli 400k Ω) signaali ei enää palautunut ammuksen ohituksen jälkeen. 

Saattaisi toimia paremmin, jos IR-ledejä olisi vain yksi per portti, vaatisi tosin rakenteen uudelleen suunnittelemisen. 

Tulinopeuden (rpm) ja kierrosnopeuden (avoimien porttien avulla) mittaaminen olisi hyvä lisäominaisuus.