Område 4 · lektion 22
Visa mätvärdet
Ett mätvärde som bara finns inuti kortet är ingen mätning. Den här guiden handlar om det sista steget i kedjan sensor → styrenhet → aktuator: att göra talet läsbart för en människa. Tre vägar finns, och den tredje – en visare som rör sig – är den enda som gör bygget till ett mekatroniskt system. Den ställer också ett krav de två andra inte gör: skalan måste stämma, inte bara se proportionerlig ut.
Mål
- Du kan välja mellan seriemonitor, teckenfönster och mekanisk visare efter vad visningen ska användas till.
- Du kan räkna ut hur ofta en utskrift eller en skärmuppdatering får ske utan att den bromsar bygget.
- Du kan omvandla en kalibrerad storhet till en servovinkel i rätt ordning, och förklara vad som går fel om ordningen kastas om.
- Du kan rita en skala som stämmer med visarens rörelse, och räkna ut hur fint den får graderas.
Genomgång
Serial: utmärkt att felsöka med, dåligt som instrument
Seriemonitorn har varit ditt fönster in i bygget sedan område 2, och den förblir det bästa felsökningsverktyget du har. Men som instrument har den tre problem: den kräver en dator med sladd, den visar en rullande lista i stället för ett värde, och den är långsam.
Räkna på hastigheten, för den överraskar. Serial.begin(9600)
betyder 9600 bitar i sekunden. Varje tecken skickas med en startbit och en
stoppbit runt sina åtta databitar, alltså tio bitar per tecken:
9600 / 10 = 960 tecken i sekunden
En rad som 24.3 C 47 % med radbrytning är omkring sexton
tecken, alltså
16 / 960 = 0,0167 s = 17 ms per rad
Fyra rader i sekunden kostar 4 · 17 = 68 ms av varje sekund,
alltså knappt sju procent av tiden. Det märks inte. Men en utskrift
varje loopvarv, med tusentals varv i sekunden, skulle kräva
tiotusentals tecken i sekunden när bara 960 ryms. Kortet har en
sändbuffert på 64 byte, och när den är full väntar
Serial.print tills det finns plats. Utskriften blir
då ett delay() i förklädnad, och hela området 3:s
arbete med att inte blockera går förlorat på en rad som såg ofarlig ut.
Regeln är densamma som för allt annat i det här området: lägg utskriften i tidsstämpelmönstret och skriv fyra gånger i sekunden. Snabbare än så kan ingen läsa ändå.
LCD 16×2: ett instrument utan dator
Ett teckenfönster på två rader med sexton tecken gör bygget fristående. Modulerna i kitet har ett litet kort på baksidan som gör om displayens sexton anslutningar till fyra: matning, jord, SDA och SCL. De två sista är en databuss som heter I²C.
I²C tar två av dina analoga pinnar
På ett Uno-kort ligger SDA på A4 och SCL på
A5. Kopplar du in en I²C-display är alltså bara A0 till
A3 kvar för analogRead. Det här är exakt samma sorts fälla som
språkreferensen beskriver för
Servo och Timer1: ett bibliotek tar hårdvara med sig, och det
står inte i din kod. Planera pinnarna innan du kopplar, inte efter.
Varje I²C-enhet har en adress, och displaymodulerna kommer med olika. 0x27 och 0x3F är de två vanliga, men det finns fler, och vilken just din har går inte att se på den. Att ta reda på det är en del av arbetet: ladda upp en I²C-avsökare – de finns som exempel i Arduino-miljön – och läs adressen i seriemonitorn. Fungerar displayen inte alls, och du inte kört avsökaren, är det där du ska börja.
Displayen är också långsam på ett sätt som spelar roll. Varje tecken kräver flera överföringar på bussen, och en full omskrivning av båda raderna – trettiotvå tecken – landar i storleksordningen tio millisekunder. Fyra uppdateringar i sekunden kostar då
4 · 10 ms = 40 ms av varje sekund, alltså 4 %
vilket är oproblematiskt. En uppdatering varje loopvarv skulle däremot göra att loopen inte kan gå fortare än ungefär hundra varv i sekunden, och texten skulle dessutom flimra.
Två vanor gör displayen lugn att titta på:
- Använd inte
clear()vid varje uppdatering. Kommandot tar ensamt drygt en och en halv millisekund i displayen, och under tiden är rutan tom – det är just det som ser ut som flimmer. Sätt markören och skriv över den gamla texten i stället. - Skriv alltid lika många tecken. Går värdet från
9.8till10.2och tillbaka blir det annars en kvarglömd siffra på slutet. Fyll ut med mellanslag till en bestämd bredd.
Den mekaniska visaren
Ett servo med en arm är en visare. Det är den tredje visningsvägen och den enda som ger bygget en mekanisk struktur – utan den är bygget inget mekatroniskt system i kriteriernas mening, hur bra mätningen än är.
Visaren har egenskaper som varken serieporten eller displayen har. Den syns tvärs över rummet. Den visar riktning: att värdet är på väg upp ser du utan att läsa något. Och den visar avvikelse från det normala på en enda blick, förutsatt att skalan är ritad så. Priset är att den bara kan visa en enda storhet, och att den kräver att du gör två saker rätt som inte finns i de andra vägarna: ordningen på räkningen, och skalan.
Kalibrera först, mappa sedan
Det här är guidens viktigaste avsnitt, och det fel det beskriver är besvärligt just för att bygget ser rätt ut när man provar det.
Frestelsen är att göra det i ett steg: mappa råvärdet direkt till en vinkel.
vinkel = map(rå, rå_vid_0_grader, rå_vid_50_grader, 0, 180)
Det ger en visare som står rätt vid båda ändarna av skalan – och fel överallt däremellan. Skälet är att givaren inte är rätlinjig. Råvärdet ändrar sig inte lika mycket per grad överallt, och en rät mappning från råvärde till vinkel ärver den skevheten.
Räkna igenom det med NTC:n från kalibreringsguiden, en tavla graderad 0–50 °C över 180 grader. Rätt vinkel är
vinkel = 180 · T / 50 = 3,6 grader per grad Celsius
| Verklig temp | Råvärde | Rätt vinkel | Vinkel om du mappar råvärdet | Vad tavlan då visar | Fel |
|---|---|---|---|---|---|
| 0 °C | 788 | 0,0° | 0,0° | 0,0 °C | 0,0 |
| 10 °C | 684 | 36,0° | 36,1° | 10,0 °C | +0,0 |
| 20 °C | 569 | 72,0° | 76,1° | 21,1 °C | +1,1 |
| 25 °C | 512 | 90,0° | 95,9° | 26,6 °C | +1,6 |
| 30 °C | 456 | 108,0° | 115,4° | 32,1 °C | +2,1 |
| 35 °C | 403 | 126,0° | 133,8° | 37,2 °C | +2,2 |
| 40 °C | 354 | 144,0° | 150,8° | 41,9 °C | +1,9 |
| 50 °C | 270 | 180,0° | 180,0° | 50,0 °C | 0,0 |
Vinklarna står med en decimal, så att avläsningskolumnen går att räkna ur dem; servot tar förstås ett heltal, och den avrundningen är mindre än servots eget dödband.
Kolumnen längst till höger är hela poängen. Instrumentet är exakt rätt i båda ändarna och över två grader fel på mitten. Och de två ändarna är precis det man kontrollerar när man provar ett nybyggt instrument. Felet överlever därför provkörningen, hamnar i redovisningen, och upptäcks först av den som jämför med en termometer mitt i området.
Räkningen bakom en rad, så att du kan göra om den med dina egna tal. Vid 25 °C är råvärdet 512, och den direkta mappningen ger
(512 − 788) · 180 / (270 − 788) = (−276) · 180 / (−518) = 49 680 / 518 = 95,9°
På en tavla där 180 grader svarar mot 50 °C avläses den vinkeln som
95,9 / 180 · 50 = 26,6 °C
alltså 1,6 grader fel.
Rätt ordning är i två steg. Först råvärde till storhet med din kalibrering, sedan storhet till vinkel med en rät linje. Den andra omvandlingen får vara rätlinjig, för nu är storheten en riktig temperatur.
const float SKALA_MIN = 0.0; // grader vid visarens ena andlage
const float SKALA_MAX = 50.0; // grader vid det andra
const int VINKEL_MIN = 10; // servovinkel vid SKALA_MIN
const int VINKEL_MAX = 170; // servovinkel vid SKALA_MAX
int graderTillVinkel(float grader) {
float begransad = constrain(grader, SKALA_MIN, SKALA_MAX);
float andel = (begransad - SKALA_MIN) / (SKALA_MAX - SKALA_MIN);
return VINKEL_MIN + (int)(andel * (VINKEL_MAX - VINKEL_MIN) + 0.5);
}
Kontrollräkna funktionen vid tre punkter, som du alltid ska göra med en
egen omvandling. Vid 0,0 grader är andel lika med 0 och svaret
blir 10 + 0 = 10. Vid 50,0 är andelen 1 och svaret
10 + 160 = 170. Vid 25,0 är andelen 0,5 och svaret
10 + (int)(0,5 · 160 + 0,5) = 10 + 80 = 90
Mitt emellan, som det ska vara. + 0.5 före omvandlingen till
int är avrundning: utan den kapas decimalerna neråt, precis
som map() gör.
Och constrain ligger före räkningen, så att ett
värde utanför skalan parkerar visaren i ändläget i stället för att skicka
servot mot ett stopp. Det var
kalibreringsguidens
andra halva av map-fällan.
Skalan ska ritas, inte gissas
- Strecken sitter var tionde grad Celsius, alltså sex stycken från 0 till 50.
- De sitter jämnt fördelade längs bågen. Det är rätt bara om omvandlingen gjordes i två steg, med kalibreringen först. Mappar du råvärdet direkt måste strecken sitta ojämnt, precis där visaren faktiskt hamnar vid varje referenstemperatur.
- Visaren pekar rakt upp vid 25 °C, alltså på mitten av rörelsen, eftersom 25 ligger mitt i skalan 0–50.
- Tavlan är en del av instrumentet. En visare som rör sig proportionellt mot en skala som inte stämmer är ett fel instrument, inte ett halvfärdigt.
Räkna ut var strecken ska sitta i stället för att uppskatta. Med servovinklarna 10 till 170 grader över 0 till 50 °C sveper visaren
170 − 10 = 160 grader för 50 °C, alltså 160 / 50 = 3,2 grader per grad Celsius
Ett streck var tionde grad Celsius sitter alltså
10 · 3,2 = 32 grader isär
Har din visare längden 60 mm blir avståndet mellan två streck, mätt längs bågen,
60 mm · 32 · π / 180 = 60 · 0,559 = 33,5 mm
och hela skalans båge
60 · 160 · π / 180 = 60 · 2,793 = 168 mm
Nu vet du hur stor tavlan blir innan du klipper ut den, och du kan rita strecken med gradskiva i stället för på ögonmått.
Hur fint det är meningsfullt att gradera
Tre saker begränsar upplösningen, och den grövsta av dem bestämmer.
- Omvandlaren. Ett råvärdessteg var 0,0885 °C i
kalibreringsexemplet, alltså
0,0885 · 3,2 = 0,28grader visarrörelse. Försumbart. - Bruset. Efter medelvärde över sexton prov låg det
kvar på ungefär 0,13 °C, alltså
0,13 · 3,2 = 0,42graders oro i visaren. Knappt synligt. - Servot.
write()spänner pulslängderna 544 till 2400 mikrosekunder över 180 grader, alltså(2400 − 544) / 180 = 10,3 µs per grad. Ett litet servo har ett dödband på storleksordningen 10 µs – ändringar mindre än så gör ingenting alls. Det motsvarar ungefär en grad visarrörelse, vilket i temperatur är1 / 3,2 = 0,31 °C.
Servot är alltså grövst, med god marginal. Ungefär en tredjedels grad Celsius är instrumentets verkliga upplösning, och att gradera tavlan finare än varannan grad vore att låtsas. Ett streck var femte grad, med siffra var tionde, är en ärlig tavla.
Varför 10 och 170 och inte 0 och 180 Ett SG90 är märkt 0–180 grader, men de verkliga ändlägena varierar mellan exemplar. Ett servo som beordras dit det inte kommer surrar mot sitt stopp, drar ström och blir varmt – och drar det tillräckligt mycket startar kortet om, precis som beskrivs i servoguidens avsnitt om strömbehov. Ta tio graders marginal i vardera änden och prova dig fram till dina egna gränser. Du förlorar inget: skalan ritar du ändå själv, efter de vinklar din visare faktiskt når.
Sista detaljen, från område 3: skriv bara till servot när vinkeln
ändrats. En write() per loopvarv med samma värde ger
ett servo som surrar och drar ström i onödan.
#include <Servo.h>
const int SERVO_PIN = 9;
Servo visare;
int forraVinkeln = -1; // ett omojligt varde, sa forsta skrivningen alltid sker
void satVisare(int vinkel) {
if (vinkel != forraVinkeln) {
visare.write(vinkel);
forraVinkeln = vinkel;
}
}
void setup() {
visare.attach(SERVO_PIN);
}
I industrin och i vardagen
I industrin: i ett ställverk eller ett kontrollrum sitter pekarinstrument kvar bredvid skärmarna, femtio år efter att siffrorna fanns tillgängliga. Skälet är att en visare visar avvikelse snabbare än en siffra gör. Nätfrekvensen ska hållas vid 50 Hz med små marginaler; ett instrument som spänner 49 till 51 Hz över 90 grader ger
90 grader / 2 Hz = 45 grader per hertz
så en avvikelse på 0,1 Hz blir
0,1 · 45 = 4,5 grader
tydligt synlig visarrörelse. En display som växlar mellan 49,9 och 50,1 kräver att du läser, minns och drar ifrån. Vanan att ställa in alla instrument i en rad så att visarna pekar rakt upp vid normalläge bygger på samma tanke: då är en avvikande visare det enda som inte är lodrätt, och den syns utan att ett enda tal läses.
I vardagen: bilens hastighetsmätare är ett instrument som med flit visar fel, och det står i regelverket. EU:s typgodkännanderegler säger att den visade hastigheten aldrig får vara lägre än den verkliga, men gärna högre – upp till tio procent plus fyra kilometer i timmen. Vid en verklig hastighet på 100 km/h får mätaren alltså visa ända upp till
100 + 0,10 · 100 + 4 = 114 km/h
men aldrig 99. Tillverkaren lägger sig medvetet en bit över, eftersom en mätare som visar för lite kan ge föraren fortkörningsböter medan en som visar för mycket bara är irriterande. Skalan är alltså inte konstruerad för att vara sann utan för att felet ska falla åt ett bestämt håll.
Det är precis motsatsen till kravet på ditt bygge, och kontrasten är poängen: ett instrument ska ha ett känt fel som pekar åt ett känt håll. Bilens har det, av ett skäl som går att skriva ned. Ett bygge där skalan är ritad på ögonmått har ett okänt fel åt okänt håll, och det är något helt annat.
Kravställ ditt bygge 3
De sista trettio minuterna av det här passet ägnas åt att bestämma vad ditt bygge 3 ska vara. Hela kravlistan står på byggsidan; det som ska bestämmas nu är de val som allt annat hänger på.
Sportlovet ligger mellan det här passet och arbetspasset Efter den här lektionen är det två veckor till arbetspasset, med kravlistan och kitet hemma hos dig och utan handledning emellan. Det är där bygget ska bli till. Kom till arbetspasset med något halvbyggt och en specifik fråga – "råvärdet står stilla på 1023 fast jag mätt att LDR:n ändrar sig" är en fråga som går att svara på under ett pass. Ett tomt kopplingsdäck är det inte.
Välj din givare
| Givare | Mäter | Talar för | Talar emot |
|---|---|---|---|
| LDR | ljus | Enklast att koppla och lättast att göra stora, tydliga ändringar med. Kalibrering mot en mobil ljusmätarapp går snabbt. | Det finns ingen naturlig enhet du redan har en referens för, om du inte har en app du litar på. |
| NTC | temperatur | Alla guidens exempel går att följa rakt av. En vanlig termometer duger som referens och alla har en. | Trögheten gör kalibreringen långsam – räkna en minut per punkt. |
| Potentiometer | vinkel eller läge | Rätlinjig, snabb och stabil. Referensen är en gradskiva eller en linjal. | Ger mindre att skriva om felkällor, eftersom nästan ingenting stör den. |
| HC-SR04 | avstånd | Ett tumstocksmått är en utmärkt referens och kalibreringen blir övertygande. | Kräver pulseIn och en tidsgräns, och ekot uteblir
ibland. Mer att felsöka. |
| DHT11 eller DHT22 | temperatur och fukt | Ett bibliotek gör avläsningen enkel, och du får två storheter. | Givaren är redan kalibrerad, så kalibreringskravet blir en kontroll i stället för en kalibrering. Diskutera vid samrådet hur du löser det. |
Åtta frågor att svara på innan du bygger
- Vilken storhet ska instrumentet visa, och med vilken enhet? En mening. "Rumstemperaturen i grader Celsius."
- Vilket område ska tavlan täcka? Från vilket värde till vilket. Det är samma område som dina kalibreringspunkter ska ligga i ändarna av.
- Vilken referens ska du kalibrera mot, och hur bra är den? Namnge den. "Termometern i köket, som visar hela grader." Referensens noggrannhet är ett golv för hela ditt instrument.
- Vilka två punkter ska du mäta upp, och hur får du fram dem? Konkret: kranvatten och varmvatten, mörker och skrivbordslampa, 10 cm och 100 cm.
- Vilket fast motstånd, och varför just det? Räknat ur givarens uppmätta ytterlägen, inte hämtat ur en guide.
- Hur ser visaren och tavlan ut? Armens längd, var strecken sitter och hur ofta det står en siffra. Räknat med gradskiva.
- Vad händer utanför området? Visaren parkerar i ändläget – och vad mer? En lysdiod, en text, ingenting? Beslutet ska vara ditt och stå i dokumentationen.
- Vilka två exempel ska du beskriva ditt system mot? Ett ur industrin och ett ur vardagen. Den frågan är lättast att skjuta upp och dyrast att skjuta upp – den bär hela belägget för F4, och den ska med i redovisningen.
Ta med svaren, tillsammans med samrådsunderlaget, till arbetspasset.
Prova själv
- Skriv ut ett värde varje loopvarv i en halv minut och räkna hur många rader du får. Flytta sedan utskriften in i tidsstämpelmönstret med 250 ms intervall och räkna igen. Jämför med de 960 tecken i sekunden räkneexemplet gav.
- Koppla in en LCD och kör en I²C-avsökare. Skriv ned adressen din modul
har. Kör sedan ett program som skriver ett räknande tal, först med
clear()vid varje uppdatering och sedan utan. Titta på skillnaden. - Sätt en arm på servot och skriv ett program som sveper från
VINKEL_MINtillVINKEL_MAXoch tillbaka. Markera med penna var armen står vid 10, 90 och 170 grader. Kontrollera att avståndet mellan 10 och 90 är lika stort som mellan 90 och 170. - Prova
write(0)ochwrite(180)och lyssna. Hör du ett surr står armen mot sitt stopp. Krypa uppåt från 0 och nedåt från 180 tills surret upphör – där har du dina verkliga ändlägen. - Rita tavlan på papper med gradskiva, enligt räkningen i guiden. Mät med linjal att strecken sitter jämnt utmed bågen.
- Bygg felet med flit: mappa råvärdet direkt till vinkeln, i ett steg. Kontrollera instrumentet vid båda ändarna – det stämmer. Kontrollera det sedan på mitten mot en termometer. Anteckna avvikelsen, och jämför med tabellen i guiden.
Kontrollfrågor
Varför får inte utskriften ligga direkt i loop()
utan tidsstämpel?
Vid 9600 baud ryms 960 tecken i sekunden. En loop som går tusentals
varv i sekunden skulle producera mångdubbelt fler, sändbufferten på 64
byte fylls, och Serial.print väntar tills det finns plats.
Utskriften blir då en blockering, precis det som område 3 handlade om
att bli av med.
Du kopplar in en I²C-display och plötsligt ger
analogRead(A4) skräp. Varför?
På ett Uno-kort ligger I²C-bussen på A4 och A5. De två pinnarna används nu av displayen och kan inte längre läsas som analoga ingångar. Flytta givaren till A0 till A3.
Din visare står rätt vid 0 och vid 50 grader men visar 32 när termometern säger 30. Vad är felet?
Omvandlingen görs i ett steg, direkt från råvärde till vinkel. Givarens kurva är inte rät, och skevheten hamnar i vinkeln. Ändarna stämmer per konstruktion, mitten gör det inte. Rätta genom att först räkna om råvärdet till grader med din kalibrering och sedan grader till vinkel med en rät linje.
Din tavla ska visa 10 till 30 °C över servovinklarna 15 till 165. Hur många grader visarrörelse går det på en grad Celsius, och var sitter strecket för 20 °C?
Svepet är 165 − 15 = 150 grader över
30 − 10 = 20 °C, alltså 150 / 20 = 7,5 grader
per grad Celsius. 20 °C ligger tio grader upp från skalans början, alltså
vid 15 + 10 · 7,5 = 90 grader – rakt upp, mitt i
svepet.
Varför skriver man bara till servot när vinkeln ändrats?
Loopen går tusentals varv i sekunden och skulle annars anropa
write() lika ofta med samma värde. Servot försöker då hålla
sitt läge aktivt hela tiden, surrar och drar ström. Det syns som ett
instrument som brummar och ett kort som blir varmt, och i värsta fall
startar om.
Varför är det ingen idé att gradera tavlan i tiondels grader Celsius?
Därför att servot inte kan ställa in sig finare än ungefär en grads vinkel, vilket med 3,2 grader per grad Celsius motsvarar ungefär 0,3 °C. En gradering finare än instrumentets upplösning påstår en precision som inte finns – samma fel som att skriva ut fyra decimaler ur en tiobitars omvandlare.
Vanliga fel
- Displayen är svart eller visar bara vita rutor. Antingen fel I²C-adress i koden, eller kontrasten som behöver justeras med den lilla skruven på modulens baksida. Kör avsökaren först, vrid skruven sedan.
- Displayen flimrar.
clear()anropas vid varje uppdatering, eller så uppdateras den varje loopvarv. Skriv över texten i stället, fyra gånger i sekunden. - En siffra blir kvar när talet blir kortare. 10,2 blev 9,8 men tvåan står kvar. Skriv alltid lika många tecken och fyll ut med mellanslag.
- Visaren rör sig men står fel på mitten. Omvandlingen görs i ett steg. Se avsnittet om ordningen ovan – det här är områdets signaturfel.
- Visaren står i ändläget och surrar. Värdet ligger
utanför skalan och
constrainsaknas, eller så är servovinklarna satta till 0 och 180 utan marginal. - Visaren darrar oroligt. Ingen medelvärdesbildning, för få prov, eller så skrivs servot vid varje varv. Sexton prov och en skrivning bara vid ändring löser oftast båda.
- Kortet startar om när visaren rör sig. Brownout. Servot drar mer än kortets 5 V orkar med. Se servoguiden – ge servot egen matning med gemensam GND.
- Tavlan är ritad efter att visaren monterats. Då blir strecken satta där armen råkade hamna, inte där räkningen säger. Rita efter gradskiva och kontrollera mot minst tre uppmätta punkter.
- Instrumentet är bara kontrollerat i ändarna. Det är just där båda metoderna stämmer. Kontrollera alltid på mitten också.
Vidare
Kapitlen Funktioner och
Bibliotek i språkreferensen
hör till den här guiden. Funktionen graderTillVinkel() är
områdets andra egna funktion, och den finns av samma skäl som den första:
samma tal ska användas på två ställen.
Nästa steg är bygge 3, som sätter ihop hela området till ett system och som redovisas muntligt. Det är kursens första redovisning, och byggsidan beskriver både bygget och hur du förbereder dig för att förklara det.