Felaktig Arduino kod ger nervöst servo

Permalänk

Felaktig Arduino kod ger nervöst servo

Har konverterat en gammaldags hastighetsmätare till servodrift men får det inte att fungera så bra. Använder mig av ett Arduino Nano kort som jag programmerat med hjälp av modifierad kod från likande projekt. Servot är dock väldigt nervöst. En induktiv givare ger insignalen via en optokopplare, har dock inte möjlighet att kika på råsignalen men efter optokopplaren borde den vara okej. Servot är ett 270 graders från Kjell.co. Jag har en "stroke" funktion då servot går från 0-270 grader och tillbaks utan bekymmer, men då jag kör fordonet den sitter i fladdrar servot hej vilt. Har hittat olika medelsnitt och filtreringskoder, men jag saknar kompetensen för att framgångsrikt kunna implementera det i min kod. Jag vet inte heller om min kod är felaktigt eller om det helt enkelt krävs nån form av dämpning av in eller utsignalen.
Finns det nån som kan hjälpa mig med att antingen implementera en lösning eller hitta felet i koden?
Tack!
Magnus

/* Speedometer Stolen from other designers (open source) 31 Jan 2021 by Magnus Eriksson */ // include the Servo library #include <Servo.h> Servo myServo; // create a servo object unsigned int min_speed = 0; // minimal speed to display on scale, km/h ********ta bort??? unsigned int max_speed = 135; // maximum speed to display on scale, km/h******ta bort?? float start, finished; float elapsed, time; float circMetric=0.32; // wheel circumference (in meters)/number of pulses per turn: 1.92/6=0.32 float speedk; // holds calculated speed values int angle; // variable to hold the angle for the servo motor void setup() { Serial.begin(9600); // configure serial for debug myServo.attach(4); // attaches the servo on pin 4 to the servo object attachInterrupt(digitalPinToInterrupt(2), speedCalc, RISING); // interrupt called when sensors sends digital 2 high (every wheel rotation) //start now (it will be reset by the interrupt after calculating revolution time) start=millis(); myServo.write(269); //Stroke test delay(1300); myServo.write(0); //Stroke test delay(1300); } void speedCalc() { //Function called by the interrupt if((millis()-start)>1) // 5 millisec debounce { //calculate elapsed elapsed=millis()-start; //reset start start=millis(); //calculate speed in km/h speedk=(3600*circMetric)/elapsed; //(((circMetric/elapsed)/1000)/1000)*3600 Serial.print(", Speedk: "); Serial.println(speedk); } } void loop() { // The loop will be interrupted by the sensor each time the // magnet passes near the sensor, in other words six times per revolution // convert speedk value to servo angle angle = map(speedk, 0, 135, 269, 0); //135 istället för 140 då servot inte går längre Serial.print(", angle: "); Serial.println(angle); // set the servo position myServo.write(angle); // wait for the servo to get there delay(500); if ((millis() - start) > 2000) { // if there is no signal more than 2 seconds speedk = 0; // so, speed is 0 } }

[code][/code] instoppat /Vzano, Mod
Permalänk
Festpilot 2020, Antiallo
Skrivet av Magnus982:

Har konverterat en gammaldags hastighetsmätare till servodrift men får det inte att fungera så bra. Använder mig av ett Arduino Nano kort som jag programmerat med hjälp av modifierad kod från likande projekt. Servot är dock väldigt nervöst. En induktiv givare ger insignalen via en optokopplare, har dock inte möjlighet att kika på råsignalen men efter optokopplaren borde den vara okej. Servot är ett 270 graders från Kjell.co. Jag har en "stroke" funktion då servot går från 0-270 grader och tillbaks utan bekymmer, men då jag kör fordonet den sitter i fladdrar servot hej vilt. Har hittat olika medelsnitt och filtreringskoder, men jag saknar kompetensen för att framgångsrikt kunna implementera det i min kod. Jag vet inte heller om min kod är felaktigt eller om det helt enkelt krävs nån form av dämpning av in eller utsignalen.
Finns det nån som kan hjälpa mig med att antingen implementera en lösning eller hitta felet i koden?
Tack!
Magnus

/*
Speedometer

Stolen from other designers (open source) 31 Jan 2021
by Magnus Eriksson
*/

// include the Servo library
#include <Servo.h>

Servo myServo; // create a servo object
unsigned int min_speed = 0; // minimal speed to display on scale, km/h ********ta bort???
unsigned int max_speed = 135; // maximum speed to display on scale, km/h******ta bort??

float start, finished;
float elapsed, time;
float circMetric=0.32; // wheel circumference (in meters)/number of pulses per turn: 1.92/6=0.32
float speedk; // holds calculated speed values

int angle; // variable to hold the angle for the servo motor

void setup() {
Serial.begin(9600); // configure serial for debug
myServo.attach(4); // attaches the servo on pin 4 to the servo object
attachInterrupt(digitalPinToInterrupt(2), speedCalc, RISING); // interrupt called when sensors sends digital 2 high (every wheel rotation)

//start now (it will be reset by the interrupt after calculating revolution time)
start=millis();

myServo.write(269); //Stroke test
delay(1300);
myServo.write(0); //Stroke test
delay(1300);
}

void speedCalc()
{
//Function called by the interrupt

if((millis()-start)>1) // 5 millisec debounce
{
//calculate elapsed
elapsed=millis()-start;

//reset start
start=millis();

//calculate speed in km/h
speedk=(3600*circMetric)/elapsed; //(((circMetric/elapsed)/1000)/1000)*3600

Serial.print(", Speedk: ");
Serial.println(speedk);
}
}

void loop()
{
// The loop will be interrupted by the sensor each time the
// magnet passes near the sensor, in other words six times per revolution

// convert speedk value to servo angle
angle = map(speedk, 0, 135, 269, 0); //135 istället för 140 då servot inte går längre
Serial.print(", angle: ");
Serial.println(angle);

// set the servo position
myServo.write(angle);

// wait for the servo to get there
delay(500);

if ((millis() - start) > 2000) { // if there is no signal more than 2 seconds
speedk = 0; // so, speed is 0
}

}

Hur har du kopplat grejerna?
Diagram/bild?

Visa signatur

 | PM:a Moderatorerna | Kontaktformuläret | Geeks Discord |
Testpilot, Skribent, Moderator & Geeks Gaming Huvudadmin

Permalänk
Permalänk
Medlem

Det kan vara rätt mkt RFI i en bil som är igång. Hade börjat med att kolla upp skärmning och signalfiltrering.

Visa signatur

Nu lurade jag dig att slösa bort ett par värdefulla sekunder av ditt liv på att läsa denna fullständigt poänglösa signatur!

Permalänk
Festpilot 2020, Antiallo

Spontant, se till att du har samma jord-ref när du jobbar med ADC alternativt frikopplar så att inga krypströmmar tar bakvägen.

Sedan hade jag gissat på att iom det inte är någon kärnfysik så funkar ett vanligt glidande medelvärde borde avhjälpa det mesta av flimret, om du inte får till det med glidande medelvärde så hade jag kikat på ett vanligt FIR-filter. Finns säkert ett gäng färdiga att plocka hem och plugga in i Arduino där du bara matar det uppmätta värdet från sensorn in i filtret och i andra änden kommer ett styrvärde för servot.

Om det är en bil med alternator så ska du undvika att använda denna spänningskällan för ADC då den varierar alldeles förmycket. Om du har en digitalgivare så är det lättare att jobba med stökiga spänningskällor

Visa signatur

 | PM:a Moderatorerna | Kontaktformuläret | Geeks Discord |
Testpilot, Skribent, Moderator & Geeks Gaming Huvudadmin

Permalänk

Fordonet är eldrivet, så rimligen borde det genereras en mängd störningar, men jag fick låna ett oscilloskop och vad jag kan se så är signalen från givaren ren och fin oavsett varvtal. Matningen till kort och givare kommer från en DC/DC omvandlare. Signalen är uppmätt efter optokopplaren på kortets ingångspinne.
Tittar man på utsignalen till servot så är det en mer bedrövlig syn, Pulsbredd och amplitud ser helt slumpmässiga ut.
På bilden är den röda signalen från givaren och den blåa är utsignalen till servot.

Permalänk
Medlem

Jag skulle börja med att plocka bort Serial.Print ur ISR:en. Om jag inte minns fel så är Serial.Print blockerande... och du vill inte spendera 15-20ms i ISR.

Tänk också på att Arduino har en overhead, så att toggla en pinne kan ta längre tid än väntat. Jag hade kopplat ur det mesta inkl servo och kört funktionen myServo.Write i en loop för att se om det ens fungerar utan ev störningskällor.

Edit: Ahh såg nu att Serial.Write inte längre är blockerande så länge tx-buffer inte är full. Men själv är jag allergisk till att ha sådana funktioner i en ISR. På sin höjd putta in en byte i en USART_TX_ ISR och sedan lämna omgående tills nästa behöver in där.

Visa signatur

MSI K9N SLI Diamond | MSI Diamond HDMI 7600GT | AMD X2 4200+ | 1GB Kingston HyperX| 32" LG 5000:1 screen | Asus EeePC 701

Permalänk

Plockade bort Serial.Print som du föreslog, och Voilà...problemet var som bortblåst. Servot går nu lika stadigt och precist som jag nånsin önskar mig.
Tänk att en funktion som var tillagd för att hjälpa mig vid en eventuell felsökning, vad det som stjälpte hela lasset.

Stort tack! Jag lyfter på hatten. Även tack till er andra som också kommit med goda förslag.

Hälsningar
Magnus

Permalänk
Medlem
Skrivet av Magnus982:

Plockade bort Serial.Print som du föreslog, och Voilà...problemet var som bortblåst. Servot går nu lika stadigt och precist som jag nånsin önskar mig.
Tänk att en funktion som var tillagd för att hjälpa mig vid en eventuell felsökning, vad det som stjälpte hela lasset.

Stort tack! Jag lyfter på hatten. Även tack till er andra som också kommit med goda förslag.

Hälsningar
Magnus

Härligt! Alltid en skön känsla när sådana här problem blir lösta.

Nu vet jag inte hur mycket mer kod du kommer behöva på Interrupt-vectors men jag tror du skulle kunna flytta ut beräkningen av hastighet till main() och bara sätta en flagga i ISR:en som du kollar i main() och anropar hastighetsberäkningen därifrån. På så sätt hoggar du inte hela systemet utan bara ett fåtal instruktioner behöver göras i ISR:en

Visa signatur

MSI K9N SLI Diamond | MSI Diamond HDMI 7600GT | AMD X2 4200+ | 1GB Kingston HyperX| 32" LG 5000:1 screen | Asus EeePC 701