Prototyyppikurssin pääprojekti – Osa 2 (toteutus)

Projekti: Kuusijalkainen kävelijärobotti (muutettu pyörillä toimivaksi) – Osa 2 rakennus ja lopputulos

Tiukan kurssiaikataulun ja erittäin hitaasti saapuneiden osien takia jouduimme muuttamaan alkuperäisiä suunnitelmia ja yksinkertaistamaan robotin rakennetta. Etenkin kaikkien pyöröservojen hankkiminen aikataulun sisällä osoittautui erittäin vaikeaksi, emmekä olisi tällöin ehtineet saada robottia kasattua ennen kurssin loppumista. Muunnellussa versiossa hylkäsimme jalkoihin perustuvan liikkeen ja vaihdoimme liikkeen pyöriin perustavaksi. Robotti saatiin tällöin liikkumaan yhden pyöröservon ja yhden standard servon avulla joka oli juuri riittävä osatilanteemme huomioon ottaen.

Alunperin suunniteltu ihmisen lämpötunnistus osoittautui myös robotin toiminnan kannalta hyödyttömäksi ideaksi, tilaamamme infrapunalämpösensorin (MLX 90614) lyhyen kantaman vuoksi. Sensori tunnisti ihmisen vain alle puolen metrin etäisyydellä,  eikä ihmisen erottaminen ympäristöstä ollut pidemmän matkan päästä enää mahdollista. Tarpeeksi tarkalla sensorilla idea olisi kenties saatu toimimaan, mutta hylkäsimme idean ajan ja osien puutteen vuoksi.

Robotti on nyt kolmipyöräinen. Edessä on standardiservon ohjaama etupyörä ja kaksi takapyörää, jotka saavat voimansa pyöröservolta. Molemmat takapyörät ovat rakennettu tasauspyörästön taakse, jotta robotti olisi mahdollisimman ketterä.  Robotissa on keulassa ultraäänisensorilla toimiva tutka, joka tunnistaa kaikki esteet 180 asteen säteellä robotin edestä ja sivuilta. Robotin ohjauksen hoidimme infrapunasignaalien avulla, joten sitä voidaan ohjata esim. tv:n kaukosäätimellä. Patteriyksikkö, arduino, mini-koekytkentälevy ja infrapunasensori on kiinnitetty robotin katolle, jotta niihin pääsee helposti käsiksi uudelleenohjelmointia tai hienosäätöä silmällä pitäen.

Tarkempi lista robotissa käytetyistä osista:

  • 1x Arduino
  • 1x Arduino servo shield
  • 1x keskikokoinen Pyöröservo
  • 1x Keskikokoinen standard-servo
  • 1 x 9g Mini standard-servo
  • 1 x Ultraäänisensori
  • 1x Infrapunavastaanotin
  • 1x Infrapunakaukosäädin
  • 1x Patteriyksikkö
  • 1x Minikokoinen koekytkentälevy
  • Robotin runko, pyörät ym. muut osat on rakennettu technic-legoista

Kytkentäkaavio:

kaavio

Teimme robottiin kaksi eri tilaa, automaattista ja manuaalista liikuttelua varten. Robotin ollessa manuaalitilassa, robottia voidaan ohjailla kaukosäätimellä eteen, taakse ja sivuille. Robotin kävelunopeutta saadaan säädettyä numeronäppäimillä ja robotin tutka, sekä automaatiotila kytketään päälle tai pois ”*” ja ”#” näppäimillä. Robotti voidaan myös pysäyttää ok nappulalla.

Automaattitilassa robotti etenee omatoimisesti eteenpäin aina kun sen edessä on tarpeeksi tilaa. Jos robotti havaitsee sivuillaan esteitä se pyrkii kääntämään menosuuntaansa poispäin siltä puolelta millä esteitä on enemmän. Jos robotti havaitsee edessään esteitä se hidastaa aluksi vauhtiaan ja muuttaa suuntansa tyhjemmälle sivulle. Jos este tulee liian lähelle, robotti pysähtyy ja peruuttaa kunnes sillä on tilaa vaihtaa suuntaansa. Robotti jatkaa etenemistään kunnes se kytketään pois päältä, jolloin se jää odottamaan uusia komentoja kaukosäätimeltä.

Video #1

Video # 2

Alla kuvat robotista

20160312_194427_resized 20160312_194447_resized 20160312_194512_resized 20160312_194618_resized

/*
AUTHORS: Pasi & Heikki Petrell
E-mail: pasi_petrell@gmail.com,  heikki_petrell@hotmail.com
*/

#include <IRremote.h>
#include <Servo.h>
#include <math.h>
#define PI 3.14159265

int motorServo = 4; // Movement servo pin
int pingPinIn = 11; // Ultrasound in
int pingPinOut = 10; // Ultrasound out

Servo steerServo;
Servo cdServo; // collision detection servo
IRrecv irrecv(5); // IRremote.h
decode_results signals; // IRremote.h

float v = 331.5+0.6*20; //m/s speed of sound in 20 degrees C

// global variables
// 360-servo actions
int speedMultiplier = 1; // sets speed multiplier for 360-servo
boolean servoForward = false;
boolean servoBackward = false;

// turning
int angle = 90; // steering angle for servo
int turnAngle = 90; // desired angle for steering

// obstacle detection
int CollisionAngle = 1; // angle for collision detector servo
int CollisionAngleadd = 1; // speed for collision detector servo turning
int lObstacle = 1; // left area value for obstacles
int rObstacle = 1; // right …
int fObstacle = 1; // front …
int lv = 1; // left area collision node values
int fv = 1; // front …
int rv = 1; // right …
int oldLv = 0; // same as above but older value. Used for to compare if an obstacle is coming closer or not.
int oldRv = 0;
boolean ScannerOff = true; // Ultrasound radar on / off

// navigation
boolean autoToggle = false; // toggle for autonomic navigation
boolean pause = false; // boolean for delay when a major obstacle is detected
boolean reverse = false; // indicates that reverse action is in progress
int pauseCntr = 0; // counter for pause length
int reverseCntr = 0; // counter for reversing
boolean reverseDone = false;
int reverseDoneCntr = 1; // counter value for turning after reversing

void setup() {
Serial.begin(115200);
irrecv.enableIRIn(); // enable input from IR receiver
pinMode(motorServo, OUTPUT);
steerServo.attach(6);
cdServo.attach(7);

}

void getIRvalue(){
if (irrecv.decode(&signals)) { // received IR-signals
switch(signals.value){

case 0xFF629D: // up. Manual move forward
servoForward = true;
servoBackward = false;
break;

case 0xFF22DD: // left. Manual turn left
if(turnAngle < 90){turnAngle=90; break;}
if (turnAngle >= 90) {turnAngle = turnAngle + 20;}
//Serial.println(”vasen”);
break;

case 0xFFC23D: // right. Manual turn right
if(turnAngle > 90){turnAngle=90; break;}
if (turnAngle <= 90){turnAngle = turnAngle – 20;}
//Serial.println(”oikea”);

break;

case 0xFFA857: // down. Manual move backwards
servoForward = false;
servoBackward = true;
break;

case 0xFF02FD: // ok, stop. Stops all movement and actions
servoForward = false;
servoBackward = false;
autoToggle = false;
ScannerOff = true;
digitalWrite(13, LOW);
break;

case 0xFF6897: // 1-button. Values 1 … 10 changes speed for motor
speedMultiplier = 1;
break;

case 0xFF9867: // 2-button
speedMultiplier = 2;
break;

case 0xFFB04F: // 3-button
speedMultiplier = 3;
break;

case 0xFF30CF: // 4-button
speedMultiplier = 4;
break;

case 0xFF18E7: // 5-button
speedMultiplier = 5;
break;

case 0xFF7A85: // 6-button
speedMultiplier = 6;
break;

case 0xFF10EF: // 7-button
speedMultiplier = 7;
break;

case 0xFF38C7: // 8-button
speedMultiplier = 8;
break;

case 0xFF5AA5: // 9-button
speedMultiplier = 9;
break;

case 0xFF4AB5: // 0-button ***not in use***
break;

case 0xFF42BD: // *-button. Start autoToggle
if (autoToggle== false){
autoToggle = true;
ScannerOff = false;
digitalWrite(13, HIGH);
break;}
autoToggle = false;
ScannerOff = true;
digitalWrite(13, LOW);
break;

case 0xFF52AD: // #-button shut down scanner
if (ScannerOff == true){ScannerOff = false; break;}
ScannerOff = true;
break;
}

irrecv.resume(); // get the next signal
}
}

void steeringServo(){
// limits max steering angles
if (turnAngle < 20){turnAngle = 20;}
if (turnAngle > 160){turnAngle = 160;}

// change 1 point from angle until turnAngle = angle
if (turnAngle < angle){
angle = angle – 1;
}
if (turnAngle > angle){
angle = angle + 1;
}
steerServo.write(angle); // turns steering servo
}

void ServoLiike () {
/*
Speed graph for  HIGH LOW (delay(10); settings
910 = stop
+910 = anti clockwise (918 min) (982 max)
-910 = clockwise (902 min) (838 max)
*/
int s = 910; //360-servo stop frequency
if (servoForward == false && servoBackward == false){
return;
}
if (servoForward == true) {
servoBackward = false;
s = s + 8 * speedMultiplier;

}
if (servoBackward == true) {
servoForward = false;
s = s – 8 * speedMultiplier;
}
digitalWrite(motorServo, HIGH);
delayMicroseconds(s);
Serial.println(s); // speed calibration fails without this print line ?!
digitalWrite(motorServo, LOW);

}
void CollisionD () {
if (CollisionAngle == 0 || CollisionAngle == 180) {
// saves previous obstacle values before new ones (used in obstacle avoidance)
oldLv = lv;
oldRv = rv;

// current obstacle values
lv = lObstacle;
fv = fObstacle;
rv = rObstacle;

// reset values
lObstacle = 1;
rObstacle = 1;
fObstacle = 1;
}
// turn ultrasound servo and send ping
if (CollisionAngle < 1) {CollisionAngleadd = 2;}
if (CollisionAngle > 179) {CollisionAngleadd = -2;}
CollisionAngle= CollisionAngle + CollisionAngleadd;
cdServo.write(CollisionAngle);
setCoordinates(CollisionAngle);

}

float distanceCM(){
// Send ping
pinMode(pingPinOut, OUTPUT);
digitalWrite(pingPinOut, LOW);
delayMicroseconds(3);
digitalWrite(pingPinOut, HIGH);
delayMicroseconds(5);
digitalWrite(pingPinOut, LOW);

// listen for a ping
pinMode(pingPinIn, INPUT);
float tUs = pulseIn(pingPinIn, HIGH);
float t = tUs / 1000.0 / 1000.0 / 2;
float d = t * v; // m
return d * 100; // cm
}
// Calculates rads from given degrees
double DegreesToRads( int degrees ){
return degrees * PI / 180;
}
//calculates position of found obstacle in y-axis
int calculateY(double rad, int d) {
float y = round(sin(rad) * d);
return y;

}//calculates position of found obstacle in X-axis
int calculateX(double rad, int d) {
int x = round(cos(rad) * d);
return x;
}

//Obstacle detection method
void setCoordinates(int angle) {
double rad = DegreesToRads(angle);
int d = distanceCM();
if (d < 1){d = 1;}
int y = 0;
int x = 0;
x = 0 + round(calculateX(rad, d));
y = round(calculateY(rad, d));

// Calculates obstacle values and sets their boundaries for each direction
if (x < -12) {
lObstacle = lObstacle + (1 * obstacleMultiplier(d));
}
if (x <= 12 && x >= -12) {
fObstacle = fObstacle + (1 * obstacleMultiplier(d));
}
if (x > 12) {
rObstacle = rObstacle + (1 * obstacleMultiplier(d));
}

}

//returns number depending the distance of the found object
int obstacleMultiplier (int d) {
if (d <= 20) {
return 1000;
}
if (d >= 20 && d <= 50) {
return 10;
}
if (d > 50 && d <= 100) {
return 1;
}
if (d > 100) {
return 0;
}

}

void pauseDelay(){
if(pauseCntr < 99) // 1s
{
pauseCntr++;
return;
}
pauseCntr = 1;
pause = false;
reverse = true;
}

void autoMode(){
if (reverse==true){
speedMultiplier = 4;
turnAngle = 90;
servoBackward = true;
return;
}
if (reverseDone == true) {
if(fv > 1500) // 1500 / 10 = 1,5s
{
servoForward = false;
reverseDone = false;
pause = true;
reverseDone = 1;
return;
}
reverseDoneCntr ++;
if(lv < rv){turnAngle = 145;}
if(lv > rv){turnAngle = 35;}
speedMultiplier = 4;
servoForward = true;
if (reverseDoneCntr > 100) {
reverseDoneCntr =1;
reverseDone = false;
servoForward = false;
}
return;
}
if(oldLv < lv*1.1){
turnAngle = 85;
}
else if (oldLv < rv*1.1){
turnAngle=95;
}
if(fv > 1000)
{
servoForward = false;
pause = true;
return;
}

if (fv < 100) {
servoForward = true;
speedMultiplier=7;
if(turnAngle != 95 && turnAngle != 85){
turnAngle = 90;
}
return;
}

if(fv > 100 && fv < 1000)
{
servoForward = true;
speedMultiplier=4;

if(lv < rv || lv < 150 )
{
speedMultiplier = 4;
turnAngle = 140;
}
if(lv > rv || rv < 150)
{
speedMultiplier = 4;
turnAngle = 40;
}
}
}

void backwardCounter(){
if (reverseCntr > 200)
{
reverseCntr=1;
if (lv >200 && rv > 200) {return;}
reverse = false;
servoBackward = false;
reverseDone = true;
return;
}
reverseCntr++;
}

void loop () {
getIRvalue();
if (ScannerOff == false){CollisionD();}
if (pause == true){pauseDelay();}
if (reverse == true){backwardCounter();}
if (autoToggle == true && ScannerOff == false){autoMode();}
if (pause == false){
steeringServo();
ServoLiike();
}
delay(10);
}

Tekijät: Pasi & Heikki Petrell

Kurssi: Haaga-Helia, Prototyypin rakentaminen, BUS4TN007-7

Kurssin vetäjä: Tero Karvinen, http://www.terokarvinen.com

Mainokset

Prototyyppikurssin pääprojekti – Osa 1 (suunnitelma)

Projekti: Kuusijalkainen kävelijärobotti

Päätimme ottaa kurssin pääprojektimme aiheeksi kuusijalkaisen kävelijärobotin.

Robotin ensisijaisiksi tavoitteiksi otimme sen, että robotti ensinnäkin kykenee itsenäisesti liikkumaan eteenpäin ja taaksepäin sekä kääntymään oikealle tai vasemmalle. Lisäksi robotin olisi tarkoitus tunnistaa kaikki mahdolliset esteet ympäristössään ja osata väistää / kiertää esteet törmäilemättä niihin.

Toissijaisiksi tavoitteiksemme määritimme mahdollisuuden tunnistaa ihminen huoneesta lämmön avulla, mitata ihmisen etäisyys robotista ja kävellä ko. ihmisen luo. Suunnittelimme myös, että robotille voitaisiin tehdä html-pohjainen käyttöliittymä, josta robottia voidaan tarvittaessa ohjailla manuaalisesti.

Punnitsimme ennen projektin aloitusta erilaisia liikkumisvaihtoehtoja. Vaihtoehtoja olivat mm. pyörät, telaketjut, sekä kaksi-, neli- ja kuusijalkainen kävelijä. Kävelijä tyyppinen liikkumismuoto vaikutti kaikkein kiinnostavimmalta ja kuusijalkainen oli ratkaisuna kaikkein helpoin toteuttaa, joten valitsimme liikkumismuodoksi loppujen lopuksi kuusijalkaisen kävelijän, joka muuttaa pyörivän liikkeen kävelyksi pyörivien kampien ja kiinteiden tukipisteiden avulla.

Koneistoa pyörittää jatkuvasti pyörivä servomoottori, joita aiomme sijoittaa robottiin yhden kappaleen per puoli. Tällöin kukin puoli saadaan liikuttamaan robottia itsenäisesti eteen tai taakse, mikä mahdollistaa robotin liikuttelun lisäksi myös robotin kääntämisen.

Esteiden tunnistuksen aiomme toteuttaa suurella todennäköisyydellä ultraäänisensorien avulla. Aiomme yrittää aluksi esteiden tunnistusta samalla periaatteella mitä käytimme ultraäänitutkaharjoituksessa sillä erotuksella, että jätämme graafisen osion kokonaan tutkasta pois.

Ihmisen tunnistukseen aiomme yrittää käyttää infrapunalämpömittarin ja etäisyysmittarin yhdistelmää, jossa lämpömittari tarkistaa, onko kohde ihminen ja etäisyysmittari laskee kohteen etäisyyden siinä tapauksessa mikäli ihmisen kaltainen kohde on löytynyt. Molemmat sensorit olisivat kiinni ”tornissa”, joka pyörii vaakatason lisäksi myös pystysuunnassa.

Mahdolliseen käyttöliittymään tulisi ohjaus robotille, missä se kääntyisi, tai liikkuisi eteen-, tai taaksepäin halutun aikaa kunnes tulisi uusi tai pysäytyskäsky.  Lisäksi robotin voisi määrittää kulkemaan eteenpäin esimerkiksi 10s ajan ja robotti suunnistaisi tuolloin itsenäisesti ja tekisi väistöliikkeitä mikäli matkan aikana ilmenisi esteitä. Voisimme myös säätää sensorien toimintaa niin, että väistövarotoimenpiteet saisi päälle / pois tai sensorien toiminnan saisi kokonaan pois päältä. Käyttöliittymmä toteutettaisiin yksinkertaisilla nappuloilla ja check boxeilla.

Arvioidut osat, joita tarvitsemme arduinoa varten listattuna:

-2x pyöröservoa liikkumista varten

-3x 180 asteen servoa sensoreille ja tornille

-2x ultraäänisensoria (yksi esteiden tunnistukseen ja yksi ihmisen etäisyyden laskemista varten

-1x IR sensori ihmisen tunnistusta varten

-1x mahdollinen potentiometri robotin kävelynopeuden säätöä varten

-Osat johdotuksia, sekä virtalähdettä varten

Runko-osat, sekä muut rakenteet aiomme rakentaa ensisijaisesti tekniikkalegoista, sekä sekalaisia tarvikkeita kuten rautalankaa käyttäen. Jatkamme projektia eteenpäin kun robottia varten tilatut osat saapuvat postista.

Alla kuvat robotin toimintaperiaatteesta, sekä legoista rakennetusta kampikoneistosta:

20160228_202820_resized

walker

Tekijät: Pasi & Heikki Petrell

Kurssi: Haaga-Helia, Prototyypin rakentaminen, BUS4TN007-7

Kurssin vetäjä: Tero Karvinen, http://www.terokarvinen.com

Prototyyppiharjoitus 4

Kaikuluotain graafisella näkymällä

Tällä kertaa saimme testattaviksi komponenteiksi ultraäänisensorin ja servon.  Päätimme rakentaa osista kaikuluotaimen, joka luotauksen lisäksi pystyy näyttämään havaitsemansa kohteet graafisesti tietokoneelle.

Ensimmäinen suuri haaste oli miettiä sopivanlainen metodi kohteiden muuntamiseksi graafiseen muotoon. Päädyimme ratkaisuun, jossa matriisi / näkymä on rakennettu string taulukkoon. Jokainen taulukon lokero vastaa yhtä y-akselin väliä ja jokaisen lokeron sisällä oleva merkki vastaa x-akselin arvoa. Ohjelma palauttaa matriisin oletustilaan joka loopin alussa, skannaa ympäristönsä 10 asteen välein ja merkkaa ultraääniskannerin havaitsemat osumat sen sijaintia vastaavaan lokeroon ja paikkaan matriisissa. Loopin lopussa ohjelma piirtää osumien perusteella muokatun matriisin ruudulle ja aloittaa loopin jälleen alusta.

Osumapisteet on laskettu trigonometrian avulla käyttämällä laskuissa etäisyyttä hypotenuusan ja servon kulmaa joko sin tai cos kulman arvona. Laskuissa saatu kateetin pituus kertoo X- tai Y-akselin sijainnin matriisissa. Koska arduino laskee kulmat radiaaneina, eikä asteiden perusteella jouduimme lisäksi miettimään kaavan mikä pystyy muuntamaan asteet radiaaneiksi ennen kuin laskutoimitukset saatiin toimimaan oikein. Laskukaavoja voi tarkastella itse koodista.

Koska komponettimme olivat lainassa, emme voineet kiinnittää niitä toisiinsa esimerkiksi liimaamalla. Meillä oli myös erittäin rajallinen määrä käytettävissä olevia komponentteja. Jouduimmekin kasaamaan laitteemme hyödyntäen sekalaisia kotoa löytyneitä esineitä. Esimerkiksi ultraääniskannerin liittäminen arduinoon ilman koekytkentälevyä olisi vaatinut naaras-naaras tai uros-naaras liittimiä, joita meillä ei ollut. Onnistuimme kiertämään ongelman hyödyntämällä voileipäpussin muoviklipsiä ultraääniskannerin pinnien ja hyppylankojen yhteenliittämistä varten ja luovasti käytettyjä pussinsulkijoita, legopalikkaa ja teippiä servon ja ultraääniskannerin yhteenliittämiseksi. Servon saimme tuettua kiinni pöytään tulitikkuaskin ja teipin avulla. Lopputulos ei ollut kaunis, mutta viritelmä toimi kuitenkin hienosti.

Tämänkertainen projekti oli erittäin hyvää harjoitusta mitä tahansa skannausta, esteiden tunnistusta tms. ominaisuutta vaativaa jatkoprojektia varten.

Alla kuvia kytkennöistä sekä laitteessa käytetty koodi.

Tässä lisäksi video laitteen toiminnasta.

20160213_172137_resized

20160213_172203_resized

20160213_171955_resized

Laitteen Koodi:

/*
AUTHOR: Heikki & Pasi Petrell
*/
#include <math.h>
#include <Servo.h>
#define PI 3.14159265

int pingPinIn = 6;
int pingPinOut = 7;
float v = 331.5+0.6*20; //m/s speed of sound in 20 degrees C
int servoPin = 8;

Servo sensorServo;
int maxScreenX = 35;
int maxScreenY = 20;
int maxDistance = 400; //cm
String Matrix [20];

int d = 0;
void setup (){
Serial.begin(115200);
buildMatrix();
sensorServo.attach(8);
}

float distanceCM(){
// Send ping
pinMode(pingPinOut, OUTPUT);
digitalWrite(pingPinOut, LOW);
delayMicroseconds(3);
digitalWrite(pingPinOut, HIGH);
delayMicroseconds(8);
digitalWrite(pingPinOut, LOW);

// listen for a ping
pinMode(pingPinIn, INPUT);
float tUs = pulseIn(pingPinIn, HIGH);
float t = tUs / 1000.0 / 1000.0 / 2;
float d = t * v; // m
return d * 100; // cm
}

// Converts angle(degrees) to Radians
int DegreesToRads( int degrees )
{
return round(degrees * PI / 180);
}
// default matrix template
void buildMatrix() {
Matrix[0]  = ”\7_6_5_4_3_2_1___^___1_2_3_4_5_6_7/”;
Matrix[1]  = ”|                                 |”;
Matrix[2]  = ”10                               10”;
Matrix[3]  = ”|               |                 |”;
Matrix[4]  = ”20                               20”;
Matrix[5]  = ”|               |                 |”;
Matrix[6]  = ”30                               30”;
Matrix[7]  = ”|               |                 |”;
Matrix[8]  = ”40                               40”;
Matrix[9]  = ”|               |                 |”;
Matrix[10] = ”50                               50”;
Matrix[11] = ”|               |                 |”;
Matrix[12] = ”60                               60”;
Matrix[13] = ”|               |                 |”;
Matrix[14] = ”70                               70”;
Matrix[15] = ”|               |                 |”;
Matrix[16] = ”80                               80”;
Matrix[17] = ”|               |                 |”;
Matrix[18] = ”90                               90”;
Matrix[19] = ”___________________________________”;
}

//  draws the srceen
void MatrixScreen () {
for (int i = maxScreenY -1; i >= 0; i–) {
Serial.println(Matrix[i]);
}
}
// Limits the min and max distance
int radarNode (){
int d = distanceCM();
if (d > maxDistance){
d = maxDistance;
}
else if(d < 10 ) {
d = 10;
}
return d;
}

// Sends necessary information to calculateX / calculateY for x/y values
void setCoordinates(int angle) {
int rad = round(DegreesToRads(angle));
int d = radarNode();
int y = 0;
int x = 0;
if (angle < 90){
x = 18 – round(calculateX(rad, d) /10 );
y = round(calculateY(rad, d) /10 );
}
if (angle > 90) {
angle = angle -90;
rad = round(DegreesToRads(angle));
x = 18 + round(calculateX(rad, d) /10 );
y = round(calculateY(rad, d) /10 );
}
if (angle == 90){
x = 0;
y = d / 10;
}

if (x > 35){x = 35;}
if (x < 0){x = 0;}
if (y > 19){y = 19;}
if (y < 0){y = 0;}
// Takes the premade matrix line and adds ’hit’ to the line
String newXline = Matrix[y].substring(0,x) + ”@” + Matrix[y].substring(x+1,Matrix[y].length());
Matrix[y] = newXline;

}
int calculateX(float rad, int d) {
float x = round(sin(rad) * d);
return x;
}
int calculateY(float rad, int d) {
int y = round(cos(rad) * d);
return y;
}
// turns the servo and forwards angle for distance measuring. Range is from 0 to 180 dergrees
// Angle is increased by 10 in each loop
void servoMove() {
for (int i = 0; i <= 180; i= i+10) {
sensorServo.write(i);
setCoordinates(i);
delay(100);
}
}

void loop () {
buildMatrix(); // build default matrix
servoMove();   // moves servo, sends ping and adds hits to matrix
MatrixScreen(); // draws matrix
delay(1000);
}

Tekijät: Pasi & Heikki Petrell

Kurssi: Haaga-Helia, Prototyypin rakentaminen, BUS4TN007-7

Kurssin vetäjä: Tero Karvinen, http://www.terokarvinen.com

Prototyyppiharjoitus 3

Elektroninen kompassi

Tutustuimme seuraavaksi HM55B – kompassisensorin toimintaan.

HM55B

Sensori kykenee tunnistamaan oman asentonsa suhteessa pohjoiseen vertaamalla x- ja y-akseleiden kulmaa suhteessa toisiinsa.  Kompassisensorin täydellinen ohjelmoiminen olisi vaatinut binaarikoodin kääntämistä (two’s complement), josta kummallakaan meistä ei ole aiempaa kokemusta, joten jouduimme käyttämään Arduino Playgroundista löytämäämme esimerkkikoodia pohjana omalle koodillemme.

Laite itsessään toimii siten, että se tarkistaa reaaliaikaisesti missä asennossa se on pohjoiseen nähden. Laitteeseen kytketty vihreä ledi on säädetty vilkkumaan sitä tiheämmin mitä lähemmäksi laite on suunnattu pohjoista kohti. Tämän lisäksi kun laite osoittaa vähemmän kuin 10 astetta pohjoisesta, arduinon runkoledi syttyy päälle.

Suurin haaste tehtävässä oli laitteen vaatiman toimintakoodin tulkitseminen, mikä oli laitteen toimintaan nähden odotettua haasteellisempaa. Saimme kuitenkin loppujen lopuksi selville miten laite tulisi suurinpiirten ohjelmoida ja jos meillä olisi enemmän kokemusta binaarikoodin kääntämisestä, olisimme todennäköisesti saaneet tehtyä koodin kokonaan itse.

Alla kuvia kytkennöistä sekä laitteessa käytetty koodi.

Tässä lisäksi video laitteen toiminnasta.

20160205_194031

20160205_194056

Laitteen Koodi:

/*
/////////////////////////////////
Htachi HM55B Compass
parallax (#)

AUTHOR:   kiilo kiilo@kiilo.org
License:  http://creativecommons.org/licenses/by-nc-sa/2.5/ch/

http://parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/hm55b/List/1/ProductID/98/Default.aspx?SortField=ProductName%2cProductName
http://sage.medienkunst.ch/tiki-index.php?page=HowTo_Arduino_Parallax_HM55B_Kompass
http://playground.arduino.cc/HM55B

/////////////////////////////////
*/
#include <math.h> // (no semicolon)
//// VARS
byte CLK_pin = 8;
byte EN_pin = 9;
byte DIO_pin = 10;

int X_Data = 0;
int Y_Data = 0;
int angle;

// ## Koodin lisatyt muuttujat
int gLed = 12;
int yLed = 13;
int gdel = 0;
boolean GledON = false;
// ##

//// FUNCTIONS

// ## lisatty metodi. laskee viiveen ledeille
int DelayCounter(int angle){
int viive = 0;
if (angle < 0){
viive = abs(angle) * 3; // abs() muuntaa negatiivisen luvun positiiviseksi
}
else {
viive = angle * 3; // esim. 180 astetta * 3 = 540ms
}
return viive;
}
// ##

void ShiftOut(int Value, int BitsCount) {
for(int i = BitsCount; i >= 0; i–) {
digitalWrite(CLK_pin, LOW);
if ((Value & 1 << i) == ( 1 << i)) {
digitalWrite(DIO_pin, HIGH);
//Serial.print(”1”);
}
else {
digitalWrite(DIO_pin, LOW);
//Serial.print(”0″);
}
digitalWrite(CLK_pin, HIGH);
delayMicroseconds(1);
}
//Serial.print(” ”);
}

int ShiftIn(int BitsCount) {
int ShiftIn_result;
ShiftIn_result = 0;
pinMode(DIO_pin, INPUT);
for(int i = BitsCount; i >= 0; i–) {
digitalWrite(CLK_pin, HIGH);
delayMicroseconds(1);
if (digitalRead(DIO_pin) == HIGH) {
ShiftIn_result = (ShiftIn_result << 1) + 1;
//Serial.print(”x”);
}
else {
ShiftIn_result = (ShiftIn_result << 1) + 0;
//Serial.print(”_”);
}
digitalWrite(CLK_pin, LOW);
delayMicroseconds(1);
}
//Serial.print(”:”);

// below is difficult to understand:
// if bit 11 is Set the value is negative
// the representation of negative values you
// have to add B11111000 in the upper Byte of
// the integer.
// see: http://en.wikipedia.org/wiki/Two%27s_complement
if ((ShiftIn_result & 1 << 11) == 1 << 11) {
ShiftIn_result = (B11111000 << 8) | ShiftIn_result;
}

return ShiftIn_result;
}

void HM55B_Reset() {
pinMode(DIO_pin, OUTPUT);
digitalWrite(EN_pin, LOW);
ShiftOut(B0000, 3);
digitalWrite(EN_pin, HIGH);
}

void HM55B_StartMeasurementCommand() {
pinMode(DIO_pin, OUTPUT);
digitalWrite(EN_pin, LOW);
ShiftOut(B1000, 3);
digitalWrite(EN_pin, HIGH);
}

int HM55B_ReadCommand() {
int result = 0;
pinMode(DIO_pin, OUTPUT);
digitalWrite(EN_pin, LOW);
ShiftOut(B1100, 3);
result = ShiftIn(3);
return result;
}

void setup() {
Serial.begin(115200);
pinMode(EN_pin, OUTPUT);
pinMode(CLK_pin, OUTPUT);
pinMode(DIO_pin, INPUT);
pinMode(gLed, OUTPUT);
pinMode(yLed, OUTPUT);
HM55B_Reset();
}

// *** loop metodiin lisatty tunnisteet kullekkin arvolle ***
void loop() {
HM55B_StartMeasurementCommand(); // necessary!!
delay(40); // the data is 40ms later ready
Serial.print(HM55B_ReadCommand()); // read data and print Status
Serial.print(” ”);
X_Data = ShiftIn(11); // Field strength in X
Y_Data = ShiftIn(11); // and Y direction
Serial.print(X_Data); // print X strength
Serial.print(” X-akseli ”);
Serial.print(Y_Data); // print Y strength
Serial.print(” Y-akseli ”);
digitalWrite(EN_pin, HIGH); // ok deselect chip
angle = 180 * (atan2(-1 * Y_Data , X_Data) / M_PI); // angle is atan( -y/x) !!!
Serial.print(angle); // print angle
Serial.print(” Suunta”);
Serial.println(””);

// Oma koodi alkaa
// Ohjelma vilkuttaa vihreaa ledia nopeammin mita lahempana suunta
// on pohjoista, ja kun pohjoinen on lahempana kuin 10 astetta,
// keltainen ledi syttyy paalle.

// Ohjelma tarkistaa onko uusi asento pienempi kuin vanha, mikali on
// niin silloin uusi viive annettaan laskurille.
if(gdel > DelayCounter(angle)){
gdel = DelayCounter(angle);
}
gdel = gdel – 10; // laskee viivetta 10:lla kunnes se on 0 tai vahemman
if(gdel < 1){ // sytyttaa ledit kun laskuri on lopussa.
if(GledON == false){
digitalWrite(gLed, HIGH);
GledON = true;}
else {
digitalWrite(gLed, LOW);
GledON = false;
}
gdel = DelayCounter(angle); // hakee uuden arvon laskurille
}
if(angle > -10 && angle < 10) { // keltainen ledi syttyy jos suunta alle 10 astetta pohjoiseen
digitalWrite(yLed, HIGH);
}
else {
digitalWrite(yLed, LOW);}

}

Tekijät: Pasi & Heikki Petrell

Kurssi: Haaga-Helia, Prototyypin rakentaminen, BUS4TN007-7

Kurssin vetäjä: Tero Karvinen, http://www.terokarvinen.com

Prototyyppiharjoitus 2

Varoitusvalojärjestelmä

Teimme Joystickillä toimivan varoitusvalojärjestelmän. Muuttamalla joystickin X- ja Y-akselia, kukin akseli muuttaa oman ledi-valonsa vilkkumistaajuutta portaattomasti. Lisäksi joystickin painonappula kytkee valot päälle ja pois. Suurin ongelma tehtävässä oli saada ledit vilkkumaan samanaikaisesti omalla nopeudellaan. Ongelmana oli se, että kummankin ledin viive on kumulatiiivinen toistensa kanssa, eli lisäämällä viivettä toiseen lediin se vaikutti myös toiseen.

Saimme ongelman korjattua luomalla loopin jossa kullakin ledillä oli omat laskurinsa, jotka vähenivät kukin itsenäisesti. Kun laskuri pääsee loppuun se  käynnistää / sulkee ledin jonka jälkeen se lukee uuden arvon joystickiltä.

Tämä opittu tieto on loistavaa harjoitusta jatkoprojekteja varten, mitkä tarvitsevat ”moniajoa”.

Alla kuvia kytkennöistä sekä laitteessa käytetty koodi.

Tässä on lisäksi videonäyte laitteen toiminnasta.

 

20160131_173350.jpg20160131_173248.jpg

Laitteen Koodi:

const int VRxPin = 1; // x-akselin pinni
const int VRyPin = 2; // y-akselin pinni
const int SwButtonPin = 8; // joikkarin nappulan pinni
int yTimer = 11; // y-akselin ledin vilkkumistiheyn
int xTimer = 11; // x-akselin ledin vilkkumistiheys
boolean on = false; // onko nappula paalla vai ei

int button = -1; // nappulan asento 0=paalla
int x = -1; // 0 .. 1023
int y = -1; // 0 .. 1023
int ledi = 12;  // vihrea ledi pinni
int ledi2 = 13; //  keltainen ledi
boolean ledBy = false; // ledin sammutus / kaynnistys boolean
boolean ledBx = false; // toisen ledin sammutus / kaynnistys boolean

// metodi joka lukee joystickin asennon
void readJoystick(){
button = digitalRead(SwButtonPin);
x = analogRead(VRxPin);
y = analogRead(VRyPin);
}

// ohjelman alustus
void setup(){
pinMode(SwButtonPin, INPUT);
digitalWrite(SwButtonPin, HIGH);
Serial.begin(115200);
pinMode(ledi, OUTPUT);
pinMode(ledi2, OUTPUT);
}
// luuppi alkaa
void loop(){
readJoystick(); // kysyy joystickin asennon

// tarkistaa onko joystickin nappi painettu, jos on niin ohjelma ei sytyta ledeja.
if(button == 0){
delay(10);
if(on== true){
on = false;
}
else {
on = true;
}
}

// Timerit ohjaavat ledien syttymista ja sammumista. xTimer x-akselille ja yTimer y-akselille. Timerin lukema
  // pienenee joka loopin aikana arvolla 10 (kun 1 tai vahemman, ledien tila vaihtuu). Timerin arvot saadaan joystickin asennosta.
xTimer = xTimer – 10;
yTimer = yTimer -10;

  // timer > x tai y on lisatty koodiin, koska muuten arvoa nopeasti pienennettaessa,
  // jouduttaisiin odottamaan timerin laskua 1:een, ennen kuin ledi reagoi muutokseen.
if (xTimer > x) {
xTimer = x;
}
if (yTimer > y) {
yTimer = y;
}

// Muuttaa ledin tilan on tai off tilaan kun timeri saavuttaa lukeman 1 tai vahemman.
if (on == false) {
delay(10);
if (xTimer <= 1) {
if (ledBx == true){
digitalWrite(ledi, LOW);
ledBx = false;
}
else{
digitalWrite(ledi, HIGH);
ledBx = true;
}

// varmistaa, ettei viive ole alle 10ms tai yli 1000ms.  
xTimer = x;
if (xTimer < 10) {
xTimer = 10;
}
if (xTimer > 1000) {
xTimer = 1000;
}
}

// samat maaritykset y-ledille
if (yTimer <= 1) {
if (ledBy == true){
digitalWrite(ledi2, LOW);
ledBy = false;
}
else{
digitalWrite(ledi2, HIGH);
ledBy = true;
}

yTimer = 10 +y;
if (yTimer < 1) {
yTimer = 11;
}
if (yTimer > 1000) {
yTimer = 1000;
}
}

}
// Sammuttaa ledit mikali switch-painiketta on painettu joystickilta
else {
delay(10);
digitalWrite(ledi, LOW);
digitalWrite(ledi2, LOW);
}
// Viive, joka tulee kaikissa tilanteissa koko loopille
delay (10);
}

///

Tekijät: Pasi & Heikki Petrell

Kurssi: Haaga-Helia, Prototyypin rakentaminen, BUS4TN007-7

Kurssin vetäjä: Tero Karvinen, http://www.terokarvinen.com