AVR servodriver för DC motorer
Ja, såklart. Miss av mig..
JBV: Det kanske är läge att först få en vanlig pid-kontroller fungerandes innan du laborerar med andra typer. Det är trots allt ganska enkelt att få en tajt reglerloop ändå, och det kanske räcker som prestanda. Om det inte är så att du har väldigt precisa parametrar på motor och h-brygga kommer du ändå att få lite meckel när det hamnar i verkligheten.
JBV: Det kanske är läge att först få en vanlig pid-kontroller fungerandes innan du laborerar med andra typer. Det är trots allt ganska enkelt att få en tajt reglerloop ändå, och det kanske räcker som prestanda. Om det inte är så att du har väldigt precisa parametrar på motor och h-brygga kommer du ändå att få lite meckel när det hamnar i verkligheten.
Jadå jag tänkte börja med en textbook PID:
CO = CO + Kp * [E - E_1] + Ki * T * E + (Kd/T) * [E - (2 * E_1) + E_2]
Sen är det ju bara några få rader att byta ut för att pröva andra algoritmer
Justeringen blir "on the fly" med ett program liknande mitt testprogram nedan, man drar i en slider för varje parameter och för varje uppdatering så skickas datan till µCn och ett testprogram körs och returnerar PV och SP så man ser felet i en graf.
CO = CO + Kp * [E - E_1] + Ki * T * E + (Kd/T) * [E - (2 * E_1) + E_2]
Sen är det ju bara några få rader att byta ut för att pröva andra algoritmer

Justeringen blir "on the fly" med ett program liknande mitt testprogram nedan, man drar i en slider för varje parameter och för varje uppdatering så skickas datan till µCn och ett testprogram körs och returnerar PV och SP så man ser felet i en graf.
Nu har jag programerat in en test som mer liknar en motor.
Jag tänkte mig att motorn roterar 0,2 varv per sekund och PWM enhet.
det ger 0,0003 varv per millisekund och PWM enhet.
Vilket, med en 1000 cpr enkoder, ger 0,2 pulser per varv och millisekund (simulerad loophastighet)
Encoder = Encoder + CO * 0.2
PV = Round(Encoder, 0)
Blev genast mycket intressantare att leka med parametrarna!
Edit: Gjorde en bild för att jämföra kurvorna mellan 3 olika algoritmer under samma förhållanden!

Chribbe76's kommer härifrån:
http://www.elektronikforumet.com/forum/ ... hp?t=11034
Jag tänkte mig att motorn roterar 0,2 varv per sekund och PWM enhet.
det ger 0,0003 varv per millisekund och PWM enhet.
Vilket, med en 1000 cpr enkoder, ger 0,2 pulser per varv och millisekund (simulerad loophastighet)
Encoder = Encoder + CO * 0.2
PV = Round(Encoder, 0)
Blev genast mycket intressantare att leka med parametrarna!

Edit: Gjorde en bild för att jämföra kurvorna mellan 3 olika algoritmer under samma förhållanden!

Chribbe76's kommer härifrån:
http://www.elektronikforumet.com/forum/ ... hp?t=11034
Jag har egentligen inte tid att leka med det här men det är ju så spännande.
Jag har en liten teori, min PID är den enklaste man kan göra och den fungerar hyffsat bra om man låter CO styra motor-strömmen (alltså vridmomentet eller "accelerationen").
Andra lite "flashigare" PIDar kanske är mer anpassade för att hantera PWM-styrning.
Det du gör nu, Encoder = Encoder + CO * 0.2 är väl mer likt PWM-styrning än ström-styrning men en simulering med den enkelheten säger egentligen ingenting om vad som kommer hända i verkligheten.
I mina simuleringar med ström-styrning gör jag istället:
Encoder = Encoder + Fart
Fart = Fart + CO
Jag har en liten teori, min PID är den enklaste man kan göra och den fungerar hyffsat bra om man låter CO styra motor-strömmen (alltså vridmomentet eller "accelerationen").
Andra lite "flashigare" PIDar kanske är mer anpassade för att hantera PWM-styrning.
Det du gör nu, Encoder = Encoder + CO * 0.2 är väl mer likt PWM-styrning än ström-styrning men en simulering med den enkelheten säger egentligen ingenting om vad som kommer hända i verkligheten.
I mina simuleringar med ström-styrning gör jag istället:
Encoder = Encoder + Fart
Fart = Fart + CO
Nej jag vet att det är en mycket dålig motorsimulering
Många faktorer som saknas! Men jag orkar inte klura ut en vettigare modell då jag snart får några servon så jag kan leka irl 
Jag räknar ut hur många enkoderpulser PV ser per PID-loop beroende på CO (och därmed varvtal). Det du beskriver låter ju mer som att man skulle köra tachometer!?
Jag såg jag hade gjort ett fel i Typ A PIDen i mitt testprogram. Jag hade gjort så D-termen adderades istället för att subtraheras, märkte det när jag jämförde dom enskilda parametrarna att den drog åt fel håll! När jag hade ändrat det ser typ A EXAKT likadan ut som din (testade lägga dom över varandra)!
En intressant skillnad är att din PID har:
CO = Pt + It - Dt
Och Typ A:
CO = CO + Pt + It - Dt
Vilket gör din variant till en Position PID och den andra till en Velocity PID!
Velocity är tydligen den typ som används mest i digitala kontroller!


Jag räknar ut hur många enkoderpulser PV ser per PID-loop beroende på CO (och därmed varvtal). Det du beskriver låter ju mer som att man skulle köra tachometer!?
Jag såg jag hade gjort ett fel i Typ A PIDen i mitt testprogram. Jag hade gjort så D-termen adderades istället för att subtraheras, märkte det när jag jämförde dom enskilda parametrarna att den drog åt fel håll! När jag hade ändrat det ser typ A EXAKT likadan ut som din (testade lägga dom över varandra)!
En intressant skillnad är att din PID har:
CO = Pt + It - Dt
Och Typ A:
CO = CO + Pt + It - Dt
Vilket gör din variant till en Position PID och den andra till en Velocity PID!
Velocity är tydligen den typ som används mest i digitala kontroller!
Nu har jag börjat knåpa på detta igen, Har just fixat avläsningen på enkodern!
Eftersom mina encoders är på 3600 PPR (max 200kHz) så blir det tungt att få några större hastigheter i quadrature. Jag gjorde en inställning så att man kan läsa av enkodern i:
1 * PPR = 3600 CPR
2 * PPR = 7200 CPR
4 * PPR = 14400 CPR
Fungerar finfint när jag skickar PV till terminalen, riktigt känsligt i quadrature
Eftersom mina encoders är på 3600 PPR (max 200kHz) så blir det tungt att få några större hastigheter i quadrature. Jag gjorde en inställning så att man kan läsa av enkodern i:
1 * PPR = 3600 CPR
2 * PPR = 7200 CPR
4 * PPR = 14400 CPR
Fungerar finfint när jag skickar PV till terminalen, riktigt känsligt i quadrature

Nu har jag kodat lite mer. Alla inställningar går att hämtas och sättas (samt sparas på EEPROM) via RS232:
KP, KI, KD = heltal (ska köra fixed point decimaltal tänkte jag)
EncoderMode = 1, 2 eller 4 x cpr
StepMultiplier = 1-10
ErrorLimit = 0-9999
CurrentLimit = 0-255
CurrentPeakLimit = 0-255
MinPWM = 0-255 = offset (den spänning där motorn börjar röra på sig)
MaxPWM = 0-255
Dock tar min kommando funktion upp över 60% av det tillgängliga minnet så det måste bantas ned lite eventuellt. Har bara själva PID algoritmen kvar att skriva in. Bör inte var speciellt svårt att banta ner dock då mina kommandon är onödigt långa (för att kunna testa enkelt) t.ex. "set kp 1234".
Har även fixat så att Fault loopen fungerar som den ska. Den stoppar vidare PID beräkningar och därmed PWM utgångarna om:
Enable ingången är låg.
Felet blir större än ErrorLimit.
Strömmen blir större än CurrentPeakLimit.
Strömmen blir större än CurrentLimit i minst en sekund.
Jag funderar på om jag ska sätta en liten strömbrytare på kretskortet som nollställer fault och disablar ErrorLimit så länge den när nedtryckt så att servodrivern kan återställas utan förlust av position!
PID loopen körs i 2441,4 Hz och fault loopen kör jag i 101,7 Hz. Är det vettigt? Är det vettigt att ha en sekund på CurrentLimit? Eller bör jag rentav göra det inställningsbart?
KP, KI, KD = heltal (ska köra fixed point decimaltal tänkte jag)
EncoderMode = 1, 2 eller 4 x cpr
StepMultiplier = 1-10
ErrorLimit = 0-9999
CurrentLimit = 0-255
CurrentPeakLimit = 0-255
MinPWM = 0-255 = offset (den spänning där motorn börjar röra på sig)
MaxPWM = 0-255
Dock tar min kommando funktion upp över 60% av det tillgängliga minnet så det måste bantas ned lite eventuellt. Har bara själva PID algoritmen kvar att skriva in. Bör inte var speciellt svårt att banta ner dock då mina kommandon är onödigt långa (för att kunna testa enkelt) t.ex. "set kp 1234".
Har även fixat så att Fault loopen fungerar som den ska. Den stoppar vidare PID beräkningar och därmed PWM utgångarna om:
Enable ingången är låg.
Felet blir större än ErrorLimit.
Strömmen blir större än CurrentPeakLimit.
Strömmen blir större än CurrentLimit i minst en sekund.
Jag funderar på om jag ska sätta en liten strömbrytare på kretskortet som nollställer fault och disablar ErrorLimit så länge den när nedtryckt så att servodrivern kan återställas utan förlust av position!
PID loopen körs i 2441,4 Hz och fault loopen kör jag i 101,7 Hz. Är det vettigt? Är det vettigt att ha en sekund på CurrentLimit? Eller bör jag rentav göra det inställningsbart?
Det slog mig att det nog var bättre att reglera strömen med PWM än att bara bryta så jag gjorde såhär istället:
PWMLimit är altså det värde som sätter den övre gränsen på PWM output från PID loopen! Hur snabb bör jag ha loopen när jag reglerar så här?
Kod: Markera allt
...
if(Current > Settings.CurrentLimit)
{
PWMLimit--;
if(PWMLimit < 0) PWMLimit = 0;
} else {
PWMLimit++;
if(PWMLimit > Settings.MaxPWM) PWMLimit = Settings.MaxPWM;
}
Nu har jag börjat skriva in PID loopen:
Jag tänker mig att om KP ska vara 1,85 så lagrar jag 185 i Settings.KP för att slippa flyttals beräkningar.
Frågan är hur jag ska skala om (PT + IT + DT) så jag får rätt storlek och korrekt avrundning utan att använda flyttalsberäkningar/funktioner? Finns det något vettigt färdigt sätt eller blir det att skriva en egen funktion?
Sedan undrar jag om jag kan skippa T när jag har en fast loop frekvens? Annars, vad ska man sätta för värde där?
Edit:
Knåpade ihop en liten skala/avrunda funktion:
Kod: Markera allt
.
Error_2 = Error_1;
Error_1 = Error;
Error = SetPoint - ProcessVariable;
int PT = Settings.KP * (Error - Error_1);
int IT = Settings.KI * T * Error;
int DT = (Settings.KD / T) * (Error - (2 * Error_1) + Error_2);
CO = CO + (PT + IT + DT);
Frågan är hur jag ska skala om (PT + IT + DT) så jag får rätt storlek och korrekt avrundning utan att använda flyttalsberäkningar/funktioner? Finns det något vettigt färdigt sätt eller blir det att skriva en egen funktion?
Sedan undrar jag om jag kan skippa T när jag har en fast loop frekvens? Annars, vad ska man sätta för värde där?

Edit:
Knåpade ihop en liten skala/avrunda funktion:
Kod: Markera allt
.
static inline int scale(int data)
{
if(data > 0)
{
if((data - ((data / 100) * 100)) >= 50) return ((data + 100) / 100);
} else {
if((data - ((data / 100) * 100)) <= -50) return ((data - 100) / 100);
}
return (data / 100);
}
Nu har jag äntligen lyckats styra ett servo lite!
I brist på gate drivers så kopplade jag ihop 2 av mina små servon axel mot axel och körde low side drivning på varsin motor.
Sedan skrev jag ett program i VB som skickar setpoint över rs232. Värdet ändras när man drar i en slider: -3600 till +3600. Jag kör enkodern i 1X läget, så jag kan styra servot 2 varv.
Har inte orkat koda om kommandofunktionen, tog bort den gamla pga utrymmes brist. Så istället för att tvingas kompilera om varje gång jag skulle testa nya PID parametrar så körde jag: CO = Error; Det oscillerar förståss en del, men ändå kul att få se resultat

Sedan skrev jag ett program i VB som skickar setpoint över rs232. Värdet ändras när man drar i en slider: -3600 till +3600. Jag kör enkodern i 1X läget, så jag kan styra servot 2 varv.

Har inte orkat koda om kommandofunktionen, tog bort den gamla pga utrymmes brist. Så istället för att tvingas kompilera om varje gång jag skulle testa nya PID parametrar så körde jag: CO = Error; Det oscillerar förståss en del, men ändå kul att få se resultat
