Tiedote ylläpidolta:

Elektroniikkafoorumi sulkeutuu tietoturva ongelmien takia.
Käyttäjien tietoja (yv:t, sähköpostiosoite ja salasanan hash) on saattanut vuotaa vääriin käsiin.

Foorumi on asettettu vain luku tilaan. Vanhoja keskusteluja voi lukea palvelinsopimuksen päättymiseen asti.
Tietokannasta on poistettu kaikki salasanat, sähköpostiosoitteet ja yksityisviestit.

Jos haluat saada omat yksityisviestisi, lähetä sähköpostia yllapito@elektroniikkafoorumi.com
samasta sähköpostista mikä oli foorumin tiedoissa niin voin kaivella niitä varmuuskopioista.

Mielenkiintoni foorumin ylläpitoon on viime vuosina ollut vähäistä jo muutenkin joten tähän on hyvä lopettaa.
Kiitokset kaikille käyttäjilla ja pahoittelut mahdollisista ongelmista.

Päivitys: Näyttäisi siltä että mahdollinen vuoto koski vanhaa phpBB2 tietokantaa,
joten helmikuuta 2012 uudemmat tiedot pitäisi olla turvassa.

-Lahha
yllapito@elektroniikkafoorumi.com


Polttomoottorin vakiokierrossäädin Arduinolla

Keskustelua mikrokontrollereista ja niiden ohjelmoinnista.

Valvoja: Moderaattorit


Kokelas
Kokelas
Viestit: 3
Liittynyt: Pe Huhti 14, 2017 22:11
ViestiLähetetty: Pe Huhti 14, 2017 22:24
Hei kaikille,

Olen jo jonkin aikaa yrittänyt rakentaa Arduinolla laitetta, joka pitää polttomoottorin käyntinopeuden vakiona käyttäen apuna moottorin omaa kierroslukumittaria sekä servoa, joka säätää kaasuläpän asentoa pitäen kierrokset vakiona. Ilmeisesti minulla on kuitenkin ongelmaa ajastinten kanssa, sillä Arduinon olisi samanaikaisesti mitattava nopeutta ja tuotettava PWM-signaalia servolle. Olen onnistunut kyllä tekemään "kierroslukumittarin" ja servon säätimen mutta ohjelmien yhdistäminen toimivalla tavalla ei ole toistaiseksi onnistunut.

Anturi antaa muutaman +12 V pulssin kampiakselin kierrosta kohti, minkä muuttamisen Arduinon vaatimaan 5 volttiin olen toteuttanut transistorin avulla. Arduino laskeekin pulssit keskeytyksen avulla mukavasti ja kun laskentaa suoritetaan esim. sekunnin ajan, saadaan kierrosluku yksinkertaisella laskutoimituksella.

Servon säätö on myös helppoa, ja erityisen helppoa se on Servo.h -kirjastolla, mutta mitähän ajastinta tämä kirjasto mahtanee käyttää? Entä attachInterrupt -komennolla käytettävä keskeytys, jolla pulsseja lasketaan?

Mahdollisesti myös PID-säädin olisi tarpeen, mutta kunhan nyt ensin saisi mittauksen ja säädön toimimaan samanaikaisesti niin olisi helpompaa lähteä laitetta jalostamaan eteenpäin. Millä tavoin tällainen ohjelma siis pitäisi kirjoittaa, ja onko esim. tuo pulssilaskuri toteutettava jollain toisella tavalla?

Vanhempi jäsen
Vanhempi jäsen
Viestit: 400
Liittynyt: Pe Maalis 06, 2009 18:23
ViestiLähetetty: Ti Huhti 18, 2017 13:36
Olisiko sinulle koodia laittaa esille?
Mitä se siis nyt tekee jos yhdistät servokoodin ja laskurikoodin yhteen?
Arduinon valmis servokoodi käyttää timer1:tä.

Uusi jäsen
Uusi jäsen
Viestit: 21
Liittynyt: Pe Maalis 23, 2012 11:45
ViestiLähetetty: Pe Huhti 21, 2017 6:55
Yks vaihtoehto olisi laittaa hiukka lisää rautaa kehiin eli laittaa F/V muuntimen ja lukee prossun AD-muuntimella jännitettä.

Kokelas
Kokelas
Viestit: 3
Liittynyt: Pe Huhti 14, 2017 22:11
ViestiLähetetty: Pe Huhti 21, 2017 21:56
L18 kirjoitti:Olisiko sinulle koodia laittaa esille?
Mitä se siis nyt tekee jos yhdistät servokoodin ja laskurikoodin yhteen?
Arduinon valmis servokoodi käyttää timer1:tä.


Laitan koodin tänne heti kun löydän sen koneen syövereistä. Tai siis parhaan niistä, sillä aika monta versiota olen jo kirjoittanut... :?

Koodi ei oikeastaan tee mitään, ainoastaan servo nytkähtää hieman kun laitteeseen kytkee virrat. Käyntinopeutta voi kyllä seurata serial monitorista tietokoneen ruudulta.

Kun saan ajastimet kohdalleen, niin suurin haaste lienee saada servo pitämään paikkansa, sillä ajattelin muuttaa servon ohjausta puolen sekunnin välein.

simis kirjoitti:Yks vaihtoehto olisi laittaa hiukka lisää rautaa kehiin eli laittaa F/V muuntimen ja lukee prossun AD-muuntimella jännitettä.


Totta kyllä, mutta en sitten tiedä, onko Arduinon "helpompi" lukea jännitettä kuin taajuutta samanaikaisesti servon säädön kanssa. Mahdollisesti myös nopeustiedon tarkkuus kärsisi tuossa jonkin verran? Jokatapauksessa laitan koodin piakkoin tänne, kun varmasti ymmärrätte ohjelmoinnista minua enemmän...


Olisiko tässä tapauksessa järkevää käyttää kahta arduinoa, joista toinen ainoastaan lukisi nopeutta ja siirtäisi tiedon numeerisena sarjaportin kautta toiselle arduinolle, joka ainoastaan suorittaisi mahdollisen PID-säädön ja ajaisi servoa? En tiedä onko tarpeellista/järkevää, mutta tulipahan vaan mieleen...


Muokkausta:

Tässä olisi eräs koodi jolla olen yrittänyt saada laitetta toimimaan:
Koodi: Valitse kaikki
// ----- Sisällytettävät kirjastot -----
#include <Servo.h> //Servon ohjauskirjasto

// ----- Määritellään vakiot ja  muuttujat -----
//vakiot eli kytkettävät pinnit ja parametrit
const int nopeustietoPin = 5; //käyntinopeusanturin pulssi
const int servoPin = 6; //Servon PWM-pulssi

const int mittausInterval = 500; //Aikaväli jolla nopeutta mitataan
const int servoMinDeg = 0; //Servon minimikulma
const int servoMaxGeg = 180; //Servon maksimikulma
const int tavoitepulssimaara = 100;
float kerroin = 0.02

//muuttujat
int pulssimaara = 0;
int servoPosition = 0; //Servon akselin kulma, oletusarvoisesti 0 astetta
int servoInterval = 500; //Aika jonka välein servoa ajetaan
int mittaustila = 0; //Nopeusmittauksen tilamuuttuja
int virhe; //tavoitenopeuden ja mitatun nopeuden erotus

unsigned long currentMillis = 0; //tallennetaan ohjelman alusta kulunut aika tähän muuttujaan
unsigned long previousServoMillis = 0; //Aika jolloin servoa ajettiin viimeksi
unsigned long previousNopeusMillisA = 0; //Aika jolloin nopeutta alettiin mittaamaan viimeksi

// ----- Määritetään servo -----
Servo myservo;


void setup(){
  Serial.begin(9600); //Sarjaporttiliikenne käyntiin

  //Asetetaan pinnien tilat ja kytketään sisäiset ylösvedot
  digitalWrite(nopeustietoPin, HIGH); //Nopeussignaali transistorin kantaan, luku emitteriltä

  myservo.write(servoPosition); //ajaa servon alkuasentoon
  myservo.attach(servoPin); //liittää servon sille määrättyyn pinniin

  //Pulssilaskurin alustaminen
  TCCR1A = 0;
  TCNT1 = 0;
}


void loop(){
  //kutsutaan erillisiä funktioita ja luetaan ohjelman alusta kulunut aika
  currentMillis = millis();
  nopeusmittausAlku();
  nopeusmittausLoppu();
  servo();
}

// ----- määritellään pääloopissa kutsuttavat erilliset funktiot -----
// Nopeusmittauksen aloitus
void nopeusmittausAlku(){
  if(mittaustila = 0){ //Aloitus välittömästi kun edellinen laskenta loppuu
    previousNopeusMillisA = millis(); //Aloitusajan tallentaminen muuttujaan
    bitSet(TCCR1B, CS12); //Pulssilaskennan aloitus
    bitSet(TCCR1B, CS11); //Laskee aallon nousevat kulmat
    mittaustila = 1;
  }
}

//Nopeusmittauksen lopetus
void nopeusmittausLoppu() {
  if(currentMillis - previousNopeusMillisA >= mittausInterval){ //Kun mittauksen aloituksesta kulunut näyteaika, mittaus lopetetaan
    TCCR1B = 0;
    pulssimaara = TCNT1;
    TCNT1 = 0;
    mittaustila = 0;
  }
}

//Servon ajamiseen liittyvä ohjelma
void servo() {
    if(currentMillis - previousServoMillis >= servoInterval){ //Kun 500 ms edellisestä säädöstä kulunut, uusi säätö
    virhe = pulssimaara - tavoitepulssimaara;
    servoPosition = servoPosition - virhe * kerroin;
    myservo.write(servoPosition);
  }
}

Vanhempi jäsen
Vanhempi jäsen
Viestit: 400
Liittynyt: Pe Maalis 06, 2009 18:23
ViestiLähetetty: Ma Huhti 24, 2017 13:51
Kyllä tuon koodin mielestäni pitäisi toimia, muutamia juttuja siellä on mitkä eivät itselle auenneet ihan heti.
Tuossa on kai kirjoitusvirhe, mutta ei varmaan väliä kun tuota servoMaxDeg ei taideta käyttää vielä missään.
Koodi: Valitse kaikki
const int servoMinDeg = 0; //Servon minimikulma
const int servoMaxGeg = 180; //Servon maksimikulma


Tässä ajat servon alkuasentoon ennekuin sitä on liitetty oikeaan pinniin?
Koodi: Valitse kaikki
  myservo.write(servoPosition); //ajaa servon alkuasentoon
  myservo.attach(servoPin); //liittää servon sille määrättyyn pinniin


Tuossa taas ihmetyttää se että miten tuosta servoPosition arvosta pitäisi tulla jotain järkevää?
Kun sen arvo on ensimmäisen kerran funktioon mentäessä nolla, ja sitten vähennät siitä virhe * kerroin, niin servoPositionin arvoksihan tulee negatiivinen kun vähennät nollasta jonkun toisen luvun?
Vai ajattelinko tuon jotenkin ihan väärin?
Koodi: Valitse kaikki
//Servon ajamiseen liittyvä ohjelma
void servo() {
    if(currentMillis - previousServoMillis >= servoInterval){ //Kun 500 ms edellisestä säädöstä kulunut, uusi säätö
    virhe = pulssimaara - tavoitepulssimaara;
    servoPosition = servoPosition - virhe * kerroin;
    myservo.write(servoPosition);
  }


Lisäksi mietin että onko joku syy miksi haluat määrittää servon säätövälin ja mittausvälin erikseen?
Itse varmaan tekisin sen niin että aina kun mittaus on päättynyt niin säätäisi servon paikan uusiksi.

Kokelas
Kokelas
Viestit: 3
Liittynyt: Pe Huhti 14, 2017 22:11
ViestiLähetetty: Ma Huhti 24, 2017 17:46
L18 kirjoitti:Tässä ajat servon alkuasentoon ennekuin sitä on liitetty oikeaan pinniin?
Koodi: Valitse kaikki
  myservo.write(servoPosition); //ajaa servon alkuasentoon
  myservo.attach(servoPin); //liittää servon sille määrättyyn pinniin


Tosiaan, nuo komennot on käännettävä toisin päin.


L18 kirjoitti:Tuossa taas ihmetyttää se että miten tuosta servoPosition arvosta pitäisi tulla jotain järkevää?
Kun sen arvo on ensimmäisen kerran funktioon mentäessä nolla, ja sitten vähennät siitä virhe * kerroin, niin servoPositionin arvoksihan tulee negatiivinen kun vähennät nollasta jonkun toisen luvun?
Vai ajattelinko tuon jotenkin ihan väärin?


Ajattelin tuon siten, että kun käyntinopeus on tavoitenopeutta pinempi, eli pulssimäärä < tavoite, on virheen arvo negatiivinen ja kun tämän etumerkki lausekkeessa servoPosition = servoPosition - virhe * kerroin; on negatiivinen, tulee lisäyksestä positiivinen eli servo kääntää lisää kaasua. Kun pulssimäärä > tavoite, on virhe positiivinen ja tapahtuu päinvastainen eli servoPosition pienenee. Tiedä sitten ajattelinko oikein ja olisiko tuon voinut helpomminkin tehdä... Kuitenkin täytynee sisällyttää tuohon lause joka pitää kulman kuitenkin positiivisena (onnistunee constrain-komennolla).


L18 kirjoitti:Lisäksi mietin että onko joku syy miksi haluat määrittää servon säätövälin ja mittausvälin erikseen?
Itse varmaan tekisin sen niin että aina kun mittaus on päättynyt niin säätäisi servon paikan uusiksi.


Eipä tuolle oikeastaan syytä ole, mietityttää vain että kun loppujen lopuksi säätö tapahtuu noin harvoin niin pitääkö servo kulmansa myös säätöjen välillä, sillä kaasuläpän jousi on melko jäykkä. Siis servon ohjauspulssin tulisi olla jatkuvaa, ja sen duty cyclen tulisi ainoastaan muuttua nopeusmittauksen jälkeen. En tiedä, onko tässä ohjelmassa näin, eli antaako tuo myservo.write pulssia niin kauan kunnes annetaan uusi komento?


Joka tapauksessa tähän liittynee vielä jotain ajastinten kanssa sekoilua, sillä niiden oikeaa käyttöä en oikein ole oikein ymmärtänyt enkä varsinkaan sisäistänyt. Eikö tuo TCCR1A tarkoita juuri ajastinta 1, jota sanoit tuon servon kirjastonkin käyttävän?

Ja vielä se, että jos vaihdan nopeusmittaukselle ajastimen 2, niin onko minun muutettava nopeussignaalin sisääntuloporttia? Jostain luin että jokainen portti käyttää tiettyä ajastinta, mutta mihin porttiin mittaus olisi sitten siirrettävä?

Uusi jäsen
Uusi jäsen
Viestit: 21
Liittynyt: Pe Maalis 23, 2012 11:45
ViestiLähetetty: Ti Huhti 25, 2017 6:11
Niin siis ajatuksena se että luet sekunnin välein tuota kierroslukua tuntuu kovin pitkältä ajalta. Tuossa sekunnissa moottori jo helposti ryntää suuntaan jos toiseenkin. A/D muunnintahan voi lukea kymmeniä tuhansia kertoja sekunnissa jolloin kierroslukutieto pysyy paremmin hanskassa. Onko tuo servo askelmoottori vai joku muu?

Noin niinkuin periaatteessa menis jotenkin näin:

Kaksi luuppia joissa annetaan askelmoottorille joko enemmän tai vähemmän pulsseja, riippuen nopeudesta.

Vanhempi jäsen
Vanhempi jäsen
Viestit: 400
Liittynyt: Pe Maalis 06, 2009 18:23
ViestiLähetetty: Ti Huhti 25, 2017 7:41
Arduino kirjoitti:
L18 kirjoitti:Tuossa taas ihmetyttää se että miten tuosta servoPosition arvosta pitäisi tulla jotain järkevää?
Kun sen arvo on ensimmäisen kerran funktioon mentäessä nolla, ja sitten vähennät siitä virhe * kerroin, niin servoPositionin arvoksihan tulee negatiivinen kun vähennät nollasta jonkun toisen luvun?
Vai ajattelinko tuon jotenkin ihan väärin?


Ajattelin tuon siten, että kun käyntinopeus on tavoitenopeutta pinempi, eli pulssimäärä < tavoite, on virheen arvo negatiivinen ja kun tämän etumerkki lausekkeessa servoPosition = servoPosition - virhe * kerroin; on negatiivinen, tulee lisäyksestä positiivinen eli servo kääntää lisää kaasua. Kun pulssimäärä > tavoite, on virhe positiivinen ja tapahtuu päinvastainen eli servoPosition pienenee. Tiedä sitten ajattelinko oikein ja olisiko tuon voinut helpomminkin tehdä... Kuitenkin täytynee sisällyttää tuohon lause joka pitää kulman kuitenkin positiivisena (onnistunee constrain-komennolla).

Aivan juu, olet ihan oikeassa, en tuota miettinyt tarpeeksi pitkälle. :)
Mutta tosiaan, kannattaa koodissa varmistaa että servoPosition ei karkaa haluttujen säätöarvojen ulkopuolelle, siitä ei oikeen ikinä tiedä mitä se tekee.
Saisitko puskettua tuon servoPositionin arvon sarjaporttiin, siitähän näkisit pyöriikö säätöarvo oikealla alueelle, ja vähän rajattua vikaa?

Arduino kirjoitti:
L18 kirjoitti:Lisäksi mietin että onko joku syy miksi haluat määrittää servon säätövälin ja mittausvälin erikseen?
Itse varmaan tekisin sen niin että aina kun mittaus on päättynyt niin säätäisi servon paikan uusiksi.


Eipä tuolle oikeastaan syytä ole, mietityttää vain että kun loppujen lopuksi säätö tapahtuu noin harvoin niin pitääkö servo kulmansa myös säätöjen välillä, sillä kaasuläpän jousi on melko jäykkä. Siis servon ohjauspulssin tulisi olla jatkuvaa, ja sen duty cyclen tulisi ainoastaan muuttua nopeusmittauksen jälkeen. En tiedä, onko tässä ohjelmassa näin, eli antaako tuo myservo.write pulssia niin kauan kunnes annetaan uusi komento?


Joka tapauksessa tähän liittynee vielä jotain ajastinten kanssa sekoilua, sillä niiden oikeaa käyttöä en oikein ole oikein ymmärtänyt enkä varsinkaan sisäistänyt. Eikö tuo TCCR1A tarkoita juuri ajastinta 1, jota sanoit tuon servon kirjastonkin käyttävän?

Ja vielä se, että jos vaihdan nopeusmittaukselle ajastimen 2, niin onko minun muutettava nopeussignaalin sisääntuloporttia? Jostain luin että jokainen portti käyttää tiettyä ajastinta, mutta mihin porttiin mittaus olisi sitten siirrettävä?


Mietin tuossa että tuon kierrosluvun mittauksen voisi tehdä myös niin, että mittaisi kahden pulssin välistä aikaa. Siitä olisi helppo laskea kierrosluku, jonka jälkeen voisi tehdä servon säädön. Silloin kierroslukuun reagointi olisi nopeampi. Puolen sekunnin mittausvälillä säätö on melkoisen hidas.

Ja tosiaan kun saat servon tekemään yhteistyötä, niin kannattaa tosissaan tutustua johonkin pid-säätöön, koska polttomoottori on elektroniikan näkökulmasta hidas reagoimaan, ja sen käytös ei ole mitenkään kovin lineaarista.

Jäsen
Jäsen
Viestit: 67
Liittynyt: Ma Kesä 02, 2008 12:08
ViestiLähetetty: Pe Heinä 14, 2017 11:16
Joskus esihistoriallisen aikana, kun vielä olin työelämässä, eräs opiskelija värkkäsi vanhaan passattiinsa vakionopeussäätimen juuri tuollaisella servosysteemillä. Arduinosta ei silloin tiedetty vielä mitään joten homma tehtiin atmega8:lla. En tiedä löytäisinkö tuon koodin vielä jostakin. Yritän kaivaa. Toimi kyllä ihan hyvin. Nopeustieto auton oman nopeusanturin pulsseista.
TRauma
Pers'aukiset eläkeläislehtorit ry.

Paluu Mikrokontrollerit ja ohjelmointi

Paikallaolijat

Käyttäjiä lukemassa tätä aluetta: Ei rekisteröityneitä käyttäjiä ja 1 vierailijaa

cron