Polttomoottorin vakiokierrossäädin

Tavoitteena on rakentaa polttomoottoriin vakiokierrossäädin, joka pyrkii pitämään halutun pyörimisnopeuden vakiona moottorin kuormituksen vaihdellessa. Käytettävissä on enkooderi, arduino uno ja rc servo SG09. Unolla ja enkooderilla saan onnistumaan pyörimisnopeuden mittauksen. Servo liitetään vaijerilla moottorin kaasuvivustoon.
Ongelmana on tiedon puute. Miten ohjelmallisesti saadaan servo reagoimaan pyörimisnopeuden muuttuessa tavoitearvosta ??? Opastusta otetaan kiitollisena vastaan!

PID säädin auttaa tähän. Googlelle PID PHD antanee artikkeliin linkin jossa käydään juurikin tämä keissi läpi.

Eli mielessä on jokin nopeus sitten on todellinen nopeus. Noiden erotus on virhe. Pkerroin * virhe antaa säädön P termille. Jotta moottori reagoi nopeammin I termiä varten muuttujaan summataan tuota virhettä ja sillä kerrotaan I kertoimella ja saadaan I termi. Tämän kanssa pitää olla tarkkana alustuksen ja sen kertymisen kanssa. Ylivuoto on helppohavaita, koska käytös on vain pöljä. Jos sitä virhettä kertyy pitkään, se ampuu kovasti ylitse. D termi on sitten virheen muutos edelliseen * kerroin. Lopuksi säätö syntyy Ptermi + I termi - Dtermi.

Kun olet varastanut tai tehnyt tuon PID säätimen aloita säätäminen P termistä ja muut nollissa. Sitten varovasti I termiä, kunnes se käyttäytyy kivasti. Lopuksi D termillä täydelliseen lopputulokseen.

Mutta tutustu aiheeseen. Törmäät kuitenkin myöhemmin siihen kuitenkin uudelleen.

Kiitokset opastuksesta, säätimen virittely lienee viimeinen ponnistelu projektissa. Olen yrittänyt opiskella ja hahmottaa asiaa.
Onko näin? Servoa komennellaan 50 HZ :n taajuudella 1…2 ms:n pulsseilla. 1,5 ms:n pulssit ajaa servon puoleen väliin. Jos moottorin asetusnopeus ja todellinen nopeus poikkeavat syntyy virhe, joka korjaantuu muuttamalla servon asentoa pullssien pituutta muuttamalla, eli kaasua lisäämällä tai vähentämällä.
Muodostaako PID säädin korjauskertoimen pullssien pituuteen niin, että servo käänyy ja moottorin pyöriminopeusvirhe korjaantuu???

Tässä teksti miten PWM toimii:
arduino.cc/en/Tutorial/SecretsOfArduinoPWM
Ja PID-säädin antaa ulos juurikin luvun jonka avulla pidennetään tai lyhennetään pulssin duty-cycleä (ja näin ollen käännetään servoa).

Vielä “takkuaa” pahasti!

Jos käytän PDI kirjastoa, niin analog inputtiin pitäisi tarjoilla moottorin kierrosnopeus rpm, mutta missä muodossa ja miten se tajoillaan? Entä “setpoint”? Servon ohjailuun on olemassa ohjelma, johon syötetään
50 Hz:n taajuudella pulssin pituus mikrosekunttein (1000…2000). Kuinka saan säätimen tulostuksena saatava pulssin kestojan tarjoiltua digital outputtiin? Tai analog outputin puolelta PWM:n jonka taajuus olisi 50 Hz, jolla voisi suoraan ohjata sevoa?? Löytyisikö ideoita?

Tuolla pitäisi onnistua:

int servoPin = 9; //D9
int servoPosition(1500); //tähän pitäisi saada PDI:n laskema korjaava arvo

void setup()
{
pinMode(servoPin, OUTPUT);
}
void loop()
{
digitalWrite(servoPin, HIGH);
delayMicroseconds(servoPosition);
digitalWrite(servoPin, LOW);
delay(20 - servoPosition/1000);
}

Kirjoittelen tässä itselleni, toivon kuitenkin opastusta ongelmiin.

Olen saanut aikaiseksi kolme erillistä ohjelmaa. 1. kierrosnopeuden mittaus (encoder ja uno) tuottaa pyörimisnopeuden kierrosta minuutissa. 2. PDI säädin, joka tarvii sisäänsä tämän hetkisen pyörimisnopeuden muunnettuna 0…255, halutun pyörimisnopeuden (setpoint) 0…255 ja antaa ulos
korjaavan arvon 0…255. 3. Servon ohjaus, joka tarvii sisäänsä em. korjaava arvon ja antaa ulos servon ohjaukseen 50 Hz:n taajuudella ohjauspulssin 1000…2000 mikrosekuntia. Nämä ohjelmat tuntuisivat toimivan pöydällä “simlaatiossa”. Ainakin pyörimisnopeuden mittaus ja servon ohjaus on helppo kokeilla. Ogelmana on, miten nämä ohjelmat yhdistetään?? Voisiko tehdä pääohjelman ja käyttää näitä erillisinä ohjelmia aliohjelmina?? , vai pitäisikö kirjoittaa yhteen pötköön?
Mitä tietotyypeille pitäisi tehdä jos samalla muuttujalla on eri tyyppi eri ohjelmissa?

Käytätkö resursseja ohjelmissa poissulkevasti? Eli et voi jollain ajastimella OC1A keskeytyksen tapahtuvan laskurin numerolla X ja Y esimerkiksi,

Itsehän en puhu Arduionoa, mutta ohjelmoin neulontakoneesta multicore serveriin…
Jos olet saanut tapahtumaan niin, että sisääntulo imaistaan keskeytyksissä, niin jotain tämän tapaista:


volatile uint16_t kierros_nopeus = 0;
uint8_t nopeus_arvoksi(uint16_t){
  if(kierros_nopeus < 255 * 20)
    return (uint8_t) (kierros_nopeus / 20);
  return 255;
}

void ohjelma() {
  while(1){
    if(kierros_nopeus) {
      saada_servo(pid_saadin(nopeus_arvoksi(kierros_nopeus)));
    }
    kierros_nopeus = 0;
  }
}

aja jokin init mainin aluksi, ja kutsu sitten ohjelma() funktiota. se ei palaa.

Miten saat nuo ohjelmat refaktoroitua sellaisiksi, että ne täyttävät tuollaiset funktiot, onkin sitten kysymys.

PID-säädin pitäisi olla lähes vapaastipyörivä. Saat sen siirrettyä helposti. jos olet tehnyt tuon kierros_nopeuden laskemisen fiksusti, se tulee keskeytykseltä. Silloin keskeyty (PIN_CHANGE katsoo laskurin arvon, vertaa edelliseen keskeytyksen ajankohtaan ja laskee siitä kuluneen ajan ja pyörimisnopeuden. Sen jälkeen tallettaa volatile muuttujaan jotta se voidaan joka ohjelma()n kierroksella lukea.

Mieleeni tuli… Mitä tuo 0…255 kierrosta esittää? jotain haluttua kierrosaluetta? vai esimerkiksi kymmentä kierrosta puolessa minuutissa? Jos puolessa minuutissa, niin arvot ovat 0… 5100 RPM alueella 20 resoluutiolla Onko tämä hyvä?

Siis mittaat kierrosnopeutta jollain tavalla. Se tapa voi olla pääloopissa tai yleensä keskeytyksessä riippuen miten sitä mittaat. Kun olet saanut arvon kutsut pääloopissa PID-säädintä ja sitten sen mukaisesti muutat PWM:ää. Tuo koko homma on helppo tehdä C:llä, muutama kymmenen riviä riittää. Itse en käyttäisi mitään valmiita kirjastoja tuohon. Esimerkiksi tekemäni PI(D)-funktio on hurjat 6 (tai 9) riviä:

double PID(double error) // Must be called at 1 Hz { double Kc=0.3, Ti=500; static double I=0.0; // I term is increased only when output is less than maximum to limit windup. if((Kc*(error+I/Ti)<1.0 && error>0) || ((Kc*(error+I/Ti)>-1.0 && error<0))) I+=error; return Kc*(error+I/Ti); }

Tuossa siis Kc on P-termin kerroin Ti I-termin aikavakio. Error on ko. tapauksessa lämpötilavirhe © ja funktio palauttaa arvon -1 ja +1 väliltä. Yli 0 käyttää lämmitystä (1=täysillä) ja alle jäähdytystä.

Suurin vaikeus on säätää PID-säätimen kertoimet niin, että toimii halutulla tavalla. Tosin ko. säätötilanne saattaa olla hyvin helppo. D-kerroin jätetään usein nollaksi, sillä siitä voi olla vain harmia eli silloin on siis PI-säädin. Toisaalta ehkä ko. tapauksessa D-termi on hyvinkin tarpeen?

Kierrosluvun mittaus vaatii suunnilleen samanpituisen koodinpätkän samoin PWM:n muuttelu.

Kiitokset vastaajille vinkeistä, apu on ollut tarpeen, koska olen askarrelut arduinon ja sen ohjelmoinnin kanssa vasta pari kuukautta ja aikaisemmat ohjelmointikokemukset ovat yli kahdenkymmenen vuoden takaa ja nekin eri kielellä.
Tein näin :
Kuten aiemmin kerroin sain simuloinnissa “pöydällä” toimimaan kolme erillistä ohjelmaa (RPM, PID1 ja SERVO). RPM tieto tulee keskeytykseltä. PID1 ohjelmassa käytin PDI kirjastoa, koska olin siihen jonkin verran tutustunut. Tein pääohjelman, jonka alkuun kokosin osaohjelmien globaalit muuttujat, void setup osaan kokosin osaohjelmien alustukset. Osaohjelmien void loop osat nimesin uusiksi funktioiksi/aliohjelmiksi (kumpikohan on oikein?). Pääohjelman void loop osaan tulivat em. funktioiden kutsut. PID ohjelmaan lisäsin “Setpoint” arvon säädön potentiometrillä, eli moottorin
haluttu vakionopeus voidaan säätää potentiometrillä. Sitkeällä uurastuksella, opiskelulla ja yritys-erehdys menetelmällä sain servon heräämää, kun pyörittelin encooderia porakoneella eri nopeuksilla.
Kokeiluvaiheesa tulostelin muuttujat sarjamonitorille, ne näyttivät loogisilta samoin, kuin servon käyttäytyminenkin. Todellinen testi ja PID:n viritysarvojen kokeilu jää kesään. Vielä voisi miettiä Setpointille ja pyörimisnopeudelle LCD näytön. Täysin uusi alue minulle. Löytyykö ehdotuksia??

Itsellä menossa hyvin samankaltainen projekti sisältäen samat ongelmat… Laitatko koodin tänne niin saa vähän vihjettä.