RTV forum PL | NewsGroups PL

Optymalne strojenie PID dla silnika z enkoderem na Pi Pico zjawiska oscylacji?

PID - jeszcze raz

NOWY TEMAT

elektroda NewsGroups Forum Index - Elektronika Polska - Optymalne strojenie PID dla silnika z enkoderem na Pi Pico zjawiska oscylacji?

Goto page Previous  1, 2, 3, 4, 5  Next

J.F
Guest

Sat Jan 31, 2026 5:50 pm   



On Sat, 31 Jan 2026 13:19:25 +0100, Mirek wrote:
Quote:
W dniu 24.01.2026 o 20:04, Waldek Hebisch pisze:
Bez członu "całkowego" prawie niemożliwe jest uzyskanie naprawdę
małego błedu.
Już się o tym przekonałem, i jakoś dało się ustabilizować z niezerowym
Ki, ale nie jestem do końca usatysfakcjonowany. Silnik wyraźnie rusza z
kopyta, zapiernicza i hamuje przed celem, zwykle nie trafia idealnie i
widać, że człon I po chwili dopiero wolniutko dociąga do celu.

Zwiększenie Ki przyspiesza to, ale prędzej czy później wprowadza układ w
oscylacje.

No tak to działa.
Nie udaje się trafić tak, żeby szybko dociągał i bez oscylacji?

Jeśli widzisz, że "wolniutko dociąga do celu", to chyba trzeba go
przyśpieszyć.

Quote:
Mały update i garść przemyśleń:
Próbowałem przepisać to i sprawdzić:
https://youtu.be/FJwgDaSsob8?t=664
Nie działa - enkoder gubi kroki, te przerwania chodzą jak chcą. Nie wiem
jak mogło mu to działać, może miał inną wersję i kupę szczęścia.

Obsluga enkodera w Pythonie?
A jakies maksymalnej częstotliwości kroków się spodziewasz?

Quote:
U mnie enkoder działa na PIO, ale jest (był) problem z utratą
rozdzielczości i długim czasem dostępu.
Przypadkiem znalazłem to:
https://forum.core-electronics.com.au/t/rotary-encoder-for-rp2040-board-in-arduino-pico-core/18120/3
Czyli okazuje się, że wystarczy dopisać parę nop-ów żeby kod ładował się
poprawnie tam gdzie powinien i mam dwukrotnie większą rozdzielczość.
Przy okazji okazało się że tutaj:

if(StateMachine.rx_fifo()>0:
pozycja_z_enkodera=StateMachine.get()

działa tak sto razy szybciej niź:

StateMachine.exec("in_(x, 32)")
pozycja_z_enkodera=StateMachine.get()
które miałem w poprzednim kodzie.

A co się dziwisz, że analiza tekstu "in_ (x, 32)" trwa?
Szczególnie, jak jeszcze w Pythonie oprogramowane? :-)

Quote:
Mogłem teraz radykalnie zwiększyć częstotliwość przeliczania PID-a...
czy coś to zmieniło? Nie specjalnie. Nadal jest problem przy małych
błędach - wtedy wyraźnie słychać kwantyzację enkodera. Są szarpnięcia
przy poszczególnych krokach i do tego jakby trochę losowe. Zastanawiam
się, czy to nie jest wina złej obsługi PWM tzn, co się dzieje jeśli się
zmienia wartość PWM w dowolnym momencie? - powinien skrócić go lub
wydłużyć jeśli jeszcze jest czas, jeśli nie, zacząć generować nowy czas
od nowego okresu. No chyba, że ten PWM ma być generowany symetrycznie,
to tylko od nowego okresu.

Zastanawiam się jeszcze, czy na tą kwantyzację przy małym błędzie nie
pomógł by jakiś filtr - może na samym członie D?

Jak pisałem - z D jest pewien kłopot.

Quote:
Jeszcze przy okazji:

jeśli sterowanie = P + I + D
to:
I = poprzednie_I + błąd*Ki
czy:
I = Ki*(poprzednie_I + błąd*Ki)
czy liczymy osobno:
Integral = Ki*(poprzednie_Integral + błąd*Ki)
i na końcu:
I = Ki * Integral

Bo spotkałem wszystkie 3 sposoby w kodach z internetu.
Niby nie wiele zmienia, ale w przypadkach brzegowych może mieć znaczenie.

Dokładnie - wszystko dopuszczalne, tylko w drugiej wersji masz chyba
jakiś błąd, bo za dużo Ki.
Jeśli to Ki ma odpowiadać temu z teorii, to musi być w odpowiednim
miejscu wliczane, ale nie ma takiego obowiązku - wystarczy przeliczać
wartość zadaną przez użytkownika na używaną wewnętrznie do obliczeń
Gdzieś tam jest kwestia wygody ogranicznia wartosći całki - czy ma to
być na wartosciach błędu, czy wyjsciowych, może kwestia dokładnosci
obliczen numerycznych - szczególnie, jak sie chce to robić na liczbach
całkowitych, uwzględniania kroku czasu, jeśli zmienny, itp.

Quote:
No i sprawa ograniczania I, czyli anti windup, czy integral clamping.
Ograniczamy do dobranej doświadczalnie (obliczonej?) wartości, czy
ograniczamy do pełnej mocy z samego członu I, czyli u mnie:
-65535 < I < 65535

Czy to nie sa liczby zmiennoprzecinkowe u Ciebie ?

Quote:
czy ograniczamy, gdy cała wartość sterowania PID przekroczy 65535, ale
to by wymagało nieskończonej liczby iteracji, więc w praktyce można
ograniczyć (nie zmieniać) jeśli poprzednia wartość PID była 65535?
Wg mnie ten ostatni sposób ma najwięcej sensu.

A jaki masz zakres wartosci wyjsciowych?
PWM ... 0-64K, czy np 0-1023

Bo od tego może zależeć, jak to sensownie ograniczyć ... no i żeby się
jakaś wartość nie przekręciła na ostatnim sumowaniu.

J.

Mirek
Guest

Sat Jan 31, 2026 6:39 pm   



W dniu 31.01.2026 o 16:50, J.F pisze:

Quote:
Obsluga enkodera w Pythonie?
A jakies maksymalnej częstotliwości kroków się spodziewasz?

Ja się nie spodziewałem ale gość pokazuje, że mu działa.



Quote:
A co się dziwisz, że analiza tekstu "in_ (x, 32)" trwa?
Szczególnie, jak jeszcze w Pythonie oprogramowane? :-)


Nie, to nie to. Wydaje mi się, że samo StateMachine.get() czyta z bufora
o ile jest napełniony, jak jest pusty to czeka. Natomiast czemu
StateMachine.exec("in_(x, 32)") nie napełnia go natychmiast to nie wiem.
Sytuację trochę poprawiło umieszczenie tego w pętli kilka instrukcji
wcześniej, ale na tym zaoszczędziłem niewiele. Obecny kod zmienia to
radykalnie: bufor sam się napełnia jak jest zmiana, jak jest napełniony
to czytam, jak nie to znaczy że nie było zmiany.


Quote:
Dokładnie - wszystko dopuszczalne, tylko w drugiej wersji masz chyba
jakiś błąd, bo za dużo Ki.

Tak, myślałem o:
Integral = poprzednie_Integral + błąd*Ki
i na końcu:
I = Ki * Integral



Quote:
-65535 < I < 65535

Czy to nie sa liczby zmiennoprzecinkowe u Ciebie ?


Nie, bo z obliczonego wcześniej float robię int-a.

Quote:
A jaki masz zakres wartosci wyjsciowych?
PWM ... 0-64K, czy np 0-1023


PWM jest 16-bitowe czyli 64k

A przy okazji - gość od tych filmików ma tam błąd, bo kierunek silnika
uzależnia od znaku błędu, co zwykle zadziała ale nie zawsze.
Ja ustawiam w zależności od znaku wyniku całego PID.

Quote:
Bo od tego może zależeć, jak to sensownie ograniczyć ... no i żeby się
jakaś wartość nie przekręciła na ostatnim sumowaniu.


Przychodzi mi do głowy jeszcze jedna metoda, mianowicie jeśli sterowanie
osiągnie maksimum to zerować wartość I. No bo tutaj nic nie ma do roboty
- silnik kręci się z maksymalną prędkością i nie ważne jak to długo
trwa, póki samo P daje maksimum to nie ma co zbierać błędu. Może głupie,
ale sprawdzę jak to się zachowa.



--
Mirek

J.F
Guest

Sat Jan 31, 2026 8:40 pm   



On Sat, 31 Jan 2026 17:39:50 +0100, Mirek wrote:
Quote:
W dniu 31.01.2026 o 16:50, J.F pisze:
Obsluga enkodera w Pythonie?
A jakies maksymalnej częstotliwości kroków się spodziewasz?

Ja się nie spodziewałem ale gość pokazuje, że mu działa.

Tak prawdę mówiąc, to mało pokazuje.
Jak masz np enkoder 2000 impulsów na obrót i do 200 obr/s,
to spora częstotliwość wychodzi.

A jak np 180 imp/obr i maks 5 obr/s, to może i Python sobie poradzi.

Quote:
A co się dziwisz, że analiza tekstu "in_ (x, 32)" trwa?
Szczególnie, jak jeszcze w Pythonie oprogramowane? :-)

Nie, to nie to. Wydaje mi się, że samo StateMachine.get() czyta z bufora
o ile jest napełniony, jak jest pusty to czeka.

Prawdopodobne. A jak tak długo czeka i wstrzymuje pętlę regulatora,
to cuda mogą być.
ale masz to rx_fifo().

Quote:
Natomiast czemu
StateMachine.exec("in_(x, 32)") nie napełnia go natychmiast to nie wiem.

Ja się tam jednak podejrzliwie patrzę na ten parametr.
Jego dopiero trzeba zdekodować, co może być trudne lub łatwiejsze.

Ale ale - dokumentacja podaje, że można podać int zawierający gotowy
kod instrukcji PIO.

https://github.com/micropython/micropython/blob/master/docs/library/rp2.StateMachine.rst?plain=1#id3

Quote:
Sytuację trochę poprawiło umieszczenie tego w pętli kilka instrukcji
wcześniej, ale na tym zaoszczędziłem niewiele. Obecny kod zmienia to
radykalnie: bufor sam się napełnia jak jest zmiana, jak jest napełniony
to czytam, jak nie to znaczy że nie było zmiany.

Dokładnie - wszystko dopuszczalne, tylko w drugiej wersji masz chyba
jakiś błąd, bo za dużo Ki.

Tak, myślałem o:
Integral = poprzednie_Integral + błąd*Ki
i na końcu:
I = Ki * Integral

Nadal wydaje mi sie, że masz Ki podwójnie.

Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Pytanie, czy powinieneś tam uwzględniać krok czasu, czy nie trzeba, bo
stały.

Quote:
-65535 < I < 65535
Czy to nie sa liczby zmiennoprzecinkowe u Ciebie ?

Nie, bo z obliczonego wcześniej float robię int-a.

Ale wartość całki sumujesz na float?

Ale to własnie to wewnętrzną sumę całki chcesz ograniczyc,
bo bedzie się długo utrzymywał błąd, wartość(suma) całki urośnie np do
miliona, Ty ograniczysz wyjsciową wartość I ... ale ta wewnętrzna
wartość całki bedzie potem dłuuugo wracała do sensownej wartości.

Quote:
A jaki masz zakres wartosci wyjsciowych?
PWM ... 0-64K, czy np 0-1023

PWM jest 16-bitowe czyli 64k

A przy okazji - gość od tych filmików ma tam błąd, bo kierunek silnika
uzależnia od znaku błędu, co zwykle zadziała ale nie zawsze.
Ja ustawiam w zależności od znaku wyniku całego PID.

Ogólnie tak właśnie powinieś robić.

Pytanie, czy on tam aktywnie hamuje silnikiem, bo jak unika, to może
mu działa - albo sprawia takie wrażenie.
Bo nie pokazuje wykresu błędu :-)

No i przypadek dużego obciążenia zewnętrznego - powiedzmy,
że jakiś cięzar czy sprężyna chce kręcić silnikiem w prawo,
kontroler musi podawać prąd chcący kręcic w lewo,
i to w miarę niezależnie od błędu.

Quote:
Bo od tego może zależeć, jak to sensownie ograniczyć ... no i żeby się
jakaś wartość nie przekręciła na ostatnim sumowaniu.

Przychodzi mi do głowy jeszcze jedna metoda, mianowicie jeśli sterowanie
osiągnie maksimum to zerować wartość I. No bo tutaj nic nie ma do roboty
- silnik kręci się z maksymalną prędkością i nie ważne jak to długo
trwa, póki samo P daje maksimum to nie ma co zbierać błędu. Może głupie,
ale sprawdzę jak to się zachowa.

Też bym o takim czymś myślał, bo jak silnik się kręci szybko w jedną
stronę i nie nadąża, to nie ma co błędu całkować.

Ewentualnie ... to co Janusz pisał - nie skacz zadaną pozycją o dużą
wartość, tylko zadawaj mu stosowną trajektorię - stopniowy przyrost
pozycji, i jeszcze stopniowy wzrost i zmniejszenie prędkości.
Regulator będzie to naśladował, i błąd ma szanse być niewielki ....

J.

Mirek
Guest

Sat Jan 31, 2026 9:50 pm   



W dniu 31.01.2026 o 19:40, J.F pisze:

Quote:
Ja się tam jednak podejrzliwie patrzę na ten parametr.
Jego dopiero trzeba zdekodować, co może być trudne lub łatwiejsze.

Ale ale - dokumentacja podaje, że można podać int zawierający gotowy
kod instrukcji PIO.

Możliwe, że coś to przyspieszy, ale w tym momencie już bez znaczenia dla
mnie, bo nie używam tej metody.



Quote:
Integral = poprzednie_Integral + błąd*Ki
i na końcu:
I = Ki * Integral

Nadal wydaje mi sie, że masz Ki podwójnie.


A "I = Ki*(poprzednie_I + błąd*Ki)" - lepiej?

Przecież w poprzednie_I już jest Ki
a tak ma właśnie gościu na filmiku.



Quote:
Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

No, czyli moja pierwsza wersja na dwa sposoby.


Quote:

Pytanie, czy powinieneś tam uwzględniać krok czasu, czy nie trzeba, bo
stały.


No krok jest stały, ale jeśli chcielibyśmy stosować reguły Z-N to chyba
trzeba.

Quote:

Ewentualnie ... to co Janusz pisał - nie skacz zadaną pozycją o dużą
wartość, tylko zadawaj mu stosowną trajektorię - stopniowy przyrost
pozycji, i jeszcze stopniowy wzrost i zmniejszenie prędkości.
Regulator będzie to naśladował, i błąd ma szanse być niewielki ....

Ja chcę zrobić regulator uniwersalny - chcę żeby mi to działało tak jak
na tym pierwszym filmiku, który podlinkowałem. Widziałem jeszcze lepszy
filmik, ale nie mogę go znaleźć, gdzie gość trzymał w ręce taki
niewielki silnik z takim jakby patyczkiem jakby wskazówką. Gość wytrącał
go palcem a ten wracał do pozycji jakby był na gumce i o coś uderzał.
Potem zrobił kilka obrotów palcem i to samo - patyczek po prostu
pojawiał się natychmiast na pozycji.

Paradoksalnie najgorzej mi się to zachowuje jak powoli zmieniam nastawę
- tzn, w pętli dodaję mu do pozycji stałą wartość - powinien jechać ze
stałą prędkością a "kołysze" tą prędkością jak tylko trochę zwiększę Ki.
Jest taki moment, że wydaje się, że już szybciej dociąga do celu i jest
stabilny przy gwałtownych zmianach nastawy a przy powolnych zmianach
jednak się kołysze.

--
Mirek

Janusz
Guest

Sun Feb 01, 2026 2:46 pm   



W dniu 31.01.2026 o 19:40, J.F pisze:
Quote:
On Sat, 31 Jan 2026 17:39:50 +0100, Mirek wrote:
W dniu 31.01.2026 o 16:50, J.F pisze:
Obsluga enkodera w Pythonie?
A jakies maksymalnej częstotliwości kroków się spodziewasz?

Ja się nie spodziewałem ale gość pokazuje, że mu działa.

Tak prawdę mówiąc, to mało pokazuje.
Jak masz np enkoder 2000 impulsów na obrót i do 200 obr/s,
to spora częstotliwość wychodzi.

A jak np 180 imp/obr i maks 5 obr/s, to może i Python sobie poradzi.

A co się dziwisz, że analiza tekstu "in_ (x, 32)" trwa?
Szczególnie, jak jeszcze w Pythonie oprogramowane? :-)

Nie, to nie to. Wydaje mi się, że samo StateMachine.get() czyta z bufora
o ile jest napełniony, jak jest pusty to czeka.

Prawdopodobne. A jak tak długo czeka i wstrzymuje pętlę regulatora,
to cuda mogą być.
ale masz to rx_fifo().

Natomiast czemu
StateMachine.exec("in_(x, 32)") nie napełnia go natychmiast to nie wiem.

Ja się tam jednak podejrzliwie patrzę na ten parametr.
Jego dopiero trzeba zdekodować, co może być trudne lub łatwiejsze.

Ale ale - dokumentacja podaje, że można podać int zawierający gotowy
kod instrukcji PIO.

https://github.com/micropython/micropython/blob/master/docs/library/rp2.StateMachine.rst?plain=1#id3

Sytuację trochę poprawiło umieszczenie tego w pętli kilka instrukcji
wcześniej, ale na tym zaoszczędziłem niewiele. Obecny kod zmienia to
radykalnie: bufor sam się napełnia jak jest zmiana, jak jest napełniony
to czytam, jak nie to znaczy że nie było zmiany.

Dokładnie - wszystko dopuszczalne, tylko w drugiej wersji masz chyba
jakiś błąd, bo za dużo Ki.

Tak, myślałem o:
Integral = poprzednie_Integral + błąd*Ki
i na końcu:
I = Ki * Integral

Nadal wydaje mi sie, że masz Ki podwójnie.

Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Mam pid-a na avr-a i tam jest wg 1 bo po sumowanu trzeba sprawdzić czy

nie wyszło poza zakres a potem dopiero mnożyć przez współczynnik.
Funkcja cała pid-u tak wygląda

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
*
* Initialise the variables used by the PID algorithm.
*
* \param p_factor Proportional term.
* \param i_factor Integral term.
* \param d_factor Derivate term.
* \param pid Struct with PID status.
*/
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid)
// Set up PID controller parameters
{
// Start values for PID controller
pid->sumError = 0;
pid->lastProcessValue = 0;
// Tuning constants for PID loop
pid->P_Factor = p_factor;
pid->I_Factor = i_factor;
pid->D_Factor = d_factor;
// Limits to avoid overflow
pid->maxError = MAX_INT / (pid->P_Factor + 1);
pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);
}


/*! \brief PID control algorithm.
*
* Calculates output from setpoint, process value and PID status.
*
* \param setPoint Desired value.
* \param processValue Measured value.
* \param pid_st PID status struct.
*/
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct
PID_DATA *pid_st)
{
int16_t error, p_term, d_term;
int32_t i_term, ret, temp;

error = setPoint -
processValue; //1

// Calculate Pterm and limit error overflow
if (error > pid_st->maxError){
p_term = MAX_INT;
}
else if (error < -pid_st->maxError){
p_term = -MAX_INT;
}
else{
p_term =
pid_st->P_Factor * error; //2
}

// Calculate Iterm and limit integral runaway
temp = pid_st->sumError + error; //3
if(temp > pid_st->maxSumError){
i_term = MAX_I_TERM;
pid_st->sumError = pid_st->maxSumError;
}
else if(temp < -pid_st->maxSumError){
i_term = -MAX_I_TERM;
pid_st->sumError = -pid_st->maxSumError;
}
else{
pid_st->sumError = temp; //4
i_term = pid_st->I_Factor * pid_st->sumError; //5
}

// Calculate Dterm
d_term = pid_st->D_Factor * (pid_st->lastProcessValue -
processValue);//6

pid_st->lastProcessValue = processValue; //7

ret = (p_term + i_term
+ d_term) / SCALING_FACTOR;//8
if(ret > MAX_INT){
ret = MAX_INT;
}
else if(ret < -MAX_INT){
ret = -MAX_INT;
}

return((int16_t)ret);
}

/*! \brief Resets the integrator.
*
* Calling this function will reset the integrator in the PID regulator.
*/
void pid_Reset_Integrator(pidData_t *pid_st)
{
pid_st->sumError = 0;
}

a definicja pid.h tak

#ifndef PID_H
#define PID_H

#include "stdint.h"

#define SCALING_FACTOR 128

/*! \brief PID Status
*
* Setpoints and data used by the PID control algorithm
*/
typedef struct PID_DATA{
//! Last process value, used to find derivative of process value.
int16_t lastProcessValue;
//! Summation of errors, used for integrate calculations
int32_t sumError;
//! The Proportional tuning constant, multiplied with SCALING_FACTOR
int16_t P_Factor;
//! The Integral tuning constant, multiplied with SCALING_FACTOR
int16_t I_Factor;
//! The Derivative tuning constant, multiplied with SCALING_FACTOR
int16_t D_Factor;
//! Maximum allowed error, avoid overflow
int16_t maxError;
//! Maximum allowed sumerror, avoid overflow
int32_t maxSumError;
} pidData_t;

/*! \brief Maximum values
*
* Needed to avoid sign/overflow problems
*/
// Maximum value of variables
#define MAX_INT INT16_MAX
#define MAX_LONG INT32_MAX
#define MAX_I_TERM (MAX_LONG / 2)

// Boolean values
#define FALSE 0
#define TRUE 1

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid);
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct
PID_DATA *pid_st);
void pid_Reset_Integrator(pidData_t *pid_st);

#endif


--
Janusz

J.F
Guest

Sun Feb 01, 2026 4:26 pm   



On Sun, 1 Feb 2026 13:46:07 +0100, Janusz wrote:
Quote:
W dniu 31.01.2026 o 19:40, J.F pisze:
On Sat, 31 Jan 2026 17:39:50 +0100, Mirek wrote:
[...]
Sytuację trochę poprawiło umieszczenie tego w pętli kilka instrukcji
wcześniej, ale na tym zaoszczędziłem niewiele. Obecny kod zmienia to
radykalnie: bufor sam się napełnia jak jest zmiana, jak jest napełniony
to czytam, jak nie to znaczy że nie było zmiany.

Dokładnie - wszystko dopuszczalne, tylko w drugiej wersji masz chyba
jakiś błąd, bo za dużo Ki.

Tak, myślałem o:
Integral = poprzednie_Integral + błąd*Ki
i na końcu:
I = Ki * Integral

Nadal wydaje mi sie, że masz Ki podwójnie.

Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Mam pid-a na avr-a i tam jest wg 1 bo po sumowanu trzeba sprawdzić czy
nie wyszło poza zakres a potem dopiero mnożyć przez współczynnik.

ograniczać zakres można i w wersji 2, pytanie tylko jak uwzględnic Ki
w granicach. A może nie trzeba, bo to problem użytkownika,
albo od strony wartości wyjsciowych to też jest dobre.

Quote:
Funkcja cała pid-u tak wygląda

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
*
* Initialise the variables used by the PID algorithm.
*
* \param p_factor Proportional term.
* \param i_factor Integral term.
* \param d_factor Derivate term.
* \param pid Struct with PID status.
*/
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid)
// Set up PID controller parameters
{
// Start values for PID controller
pid->sumError = 0;
pid->lastProcessValue = 0;
// Tuning constants for PID loop
pid->P_Factor = p_factor;
pid->I_Factor = i_factor;
pid->D_Factor = d_factor;
// Limits to avoid overflow
pid->maxError = MAX_INT / (pid->P_Factor + 1);
pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);

Tylko zauważ, że tu się boją, aby wynik obliczen nie wyskoczył poza
zakres INT.
Quote:
}

/*! \brief PID control algorithm.
*
* Calculates output from setpoint, process value and PID status.
*
* \param setPoint Desired value.
* \param processValue Measured value.
* \param pid_st PID status struct.
*/
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct
PID_DATA *pid_st)
{
int16_t error, p_term, d_term;
int32_t i_term, ret, temp;

error = setPoint - processValue; //1

// Calculate Pterm and limit error overflow
if (error > pid_st->maxError){
p_term = MAX_INT;
}
else if (error < -pid_st->maxError){
p_term = -MAX_INT;
}
else{
p_term = pid_st->P_Factor * error; //2
}

// Calculate Iterm and limit integral runaway
temp = pid_st->sumError + error; //3
if(temp > pid_st->maxSumError){
i_term = MAX_I_TERM;

**** wow - patrząc niżej, to strasznie wielki ten MAX_I_TERM
( (MAX_LONG / 2) ).

Quote:
pid_st->sumError = pid_st->maxSumError;
}
else if(temp < -pid_st->maxSumError){
i_term = -MAX_I_TERM;
pid_st->sumError = -pid_st->maxSumError;
}
else{
pid_st->sumError = temp; //4
i_term = pid_st->I_Factor * pid_st->sumError; //5
}

// Calculate Dterm
d_term = pid_st->D_Factor * (pid_st->lastProcessValue -processValue);//6
pid_st->lastProcessValue = processValue; //7

A tu, w członie D, jakby mniej się boją przekroczenia zakresu ...
ciekawe.

Quote:
ret = (p_term + i_term + d_term) / SCALING_FACTOR;//8
if(ret > MAX_INT){
ret = MAX_INT;
}
else if(ret < -MAX_INT){
ret = -MAX_INT;
}

return((int16_t)ret);
}

/*! \brief Resets the integrator.
*
* Calling this function will reset the integrator in the PID regulator.
*/
void pid_Reset_Integrator(pidData_t *pid_st)
{
pid_st->sumError = 0;
}

a definicja pid.h tak

#ifndef PID_H
#define PID_H

#include "stdint.h"

#define SCALING_FACTOR 128

/*! \brief PID Status
*
* Setpoints and data used by the PID control algorithm
*/
typedef struct PID_DATA{
//! Last process value, used to find derivative of process value.
int16_t lastProcessValue;
//! Summation of errors, used for integrate calculations
int32_t sumError;
//! The Proportional tuning constant, multiplied with SCALING_FACTOR
int16_t P_Factor;
//! The Integral tuning constant, multiplied with SCALING_FACTOR
int16_t I_Factor;
//! The Derivative tuning constant, multiplied with SCALING_FACTOR
int16_t D_Factor;
//! Maximum allowed error, avoid overflow
int16_t maxError;
//! Maximum allowed sumerror, avoid overflow
int32_t maxSumError;
} pidData_t;

/*! \brief Maximum values
*
* Needed to avoid sign/overflow problems
*/
// Maximum value of variables
#define MAX_INT INT16_MAX
#define MAX_LONG INT32_MAX
#define MAX_I_TERM (MAX_LONG / 2)

// Boolean values
#define FALSE 0
#define TRUE 1

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid);
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct
PID_DATA *pid_st);
void pid_Reset_Integrator(pidData_t *pid_st);

#endif

J.

Janusz
Guest

Sun Feb 01, 2026 4:56 pm   



W dniu 1.02.2026 o 15:26, J.F pisze:

Quote:
Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Mam pid-a na avr-a i tam jest wg 1 bo po sumowanu trzeba sprawdzić czy
nie wyszło poza zakres a potem dopiero mnożyć przez współczynnik.

ograniczać zakres można i w wersji 2, pytanie tylko jak uwzględnic Ki
w granicach. A może nie trzeba, bo to problem użytkownika,
albo od strony wartości wyjsciowych to też jest dobre.
Ale samo ograniczanie nie ma sensu, chodzi o tto że to jest zmienna word

czyli 16bit i żeby sie nie "przekręciła"

Quote:

Funkcja cała pid-u tak wygląda

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
*
* Initialise the variables used by the PID algorithm.
*
* \param p_factor Proportional term.
* \param i_factor Integral term.
* \param d_factor Derivate term.
* \param pid Struct with PID status.
*/
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid)
// Set up PID controller parameters
{
// Start values for PID controller
pid->sumError = 0;
pid->lastProcessValue = 0;
// Tuning constants for PID loop
pid->P_Factor = p_factor;
pid->I_Factor = i_factor;
pid->D_Factor = d_factor;
// Limits to avoid overflow
pid->maxError = MAX_INT / (pid->P_Factor + 1);
pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);

Tylko zauważ, że tu się boją, aby wynik obliczen nie wyskoczył poza
zakres INT.
Wiem o tym, o tym właśnie piszę wyżej. Mogli dać int32 ale chyba by

wtedy za duże bufory kumulował.
Stąd wpierw normalizują wynik a potem mnożą przez współczynnik.

Quote:
// Calculate Iterm and limit integral runaway
temp = pid_st->sumError + error; //3
if(temp > pid_st->maxSumError){
i_term = MAX_I_TERM;

**** wow - patrząc niżej, to strasznie wielki ten MAX_I_TERM
( (MAX_LONG / 2) ).

31 bitowy Smile zauważ że wszystkie sumy są takie, ale potem długo się
zbiera jak za duży błąd skumuluje, ja zmniejszyłem do 16 bit jak robiłem
regulację prądu prostownika na pid-ie.

Quote:
// Calculate Dterm
d_term = pid_st->D_Factor * (pid_st->lastProcessValue -processValue);//6
pid_st->lastProcessValue = processValue; //7

A tu, w członie D, jakby mniej się boją przekroczenia zakresu ...
ciekawe.

Bo tu się za bardzo nic nie kumuluje, to jest tylko całka ostatniego
kroku, więc nie ma
obaw o przekroczenie zakresu. A nikt rozsądny nie robi współczynnika na
tysiące czy więcej.

--
Janusz

J.F
Guest

Sun Feb 01, 2026 11:14 pm   



On Sun, 1 Feb 2026 15:56:24 +0100, Janusz wrote:
Quote:
W dniu 1.02.2026 o 15:26, J.F pisze:
Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Mam pid-a na avr-a i tam jest wg 1 bo po sumowanu trzeba sprawdzić czy
nie wyszło poza zakres a potem dopiero mnożyć przez współczynnik.

ograniczać zakres można i w wersji 2, pytanie tylko jak uwzględnic Ki
w granicach. A może nie trzeba, bo to problem użytkownika,
albo od strony wartości wyjsciowych to też jest dobre.
Ale samo ograniczanie nie ma sensu, chodzi o tto że to jest zmienna word
czyli 16bit i żeby sie nie "przekręciła"

IMO - ograniczanie wartości całki jak najbardziej ma sens.
Inaczej to np każesz wykonac dużo obrotów, i zanim tam dojedzie, to
całka zakumuluje sporą wartość, a potem będzie długo przeszkadzać.
Tu Mirek ma rację - przerwać czy nawet zerować całkę przy osiągnięciu
maksymalnej prędkości, a moze nawet przy zbyt dużym błędzie,
czy choćby ograniczyć jej możliwą wartość do sensownych wartosci.

Quote:
Funkcja cała pid-u tak wygląda

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
*
* Initialise the variables used by the PID algorithm.
*
* \param p_factor Proportional term.
* \param i_factor Integral term.
* \param d_factor Derivate term.
* \param pid Struct with PID status.
*/
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor,
struct PID_DATA *pid)
// Set up PID controller parameters
{
// Start values for PID controller
pid->sumError = 0;
pid->lastProcessValue = 0;
// Tuning constants for PID loop
pid->P_Factor = p_factor;
pid->I_Factor = i_factor;
pid->D_Factor = d_factor;
// Limits to avoid overflow
pid->maxError = MAX_INT / (pid->P_Factor + 1);
pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);

Tylko zauważ, że tu się boją, aby wynik obliczen nie wyskoczył poza
zakres INT.
Wiem o tym, o tym właśnie piszę wyżej. Mogli dać int32 ale chyba by
wtedy za duże bufory kumulował.
Stąd wpierw normalizują wynik a potem mnożą przez współczynnik.

// Calculate Iterm and limit integral runaway
temp = pid_st->sumError + error; //3
if(temp > pid_st->maxSumError){
i_term = MAX_I_TERM;

**** wow - patrząc niżej, to strasznie wielki ten MAX_I_TERM
( (MAX_LONG / 2) ).

31 bitowy Smile zauważ że wszystkie sumy są takie, ale potem długo się
zbiera jak za duży błąd skumuluje, ja zmniejszyłem do 16 bit jak robiłem
regulację prądu prostownika na pid-ie.

// Calculate Dterm
d_term = pid_st->D_Factor * (pid_st->lastProcessValue -processValue);//6
pid_st->lastProcessValue = processValue; //7

A tu, w członie D, jakby mniej się boją przekroczenia zakresu ...
ciekawe.

Bo tu się za bardzo nic nie kumuluje, to jest tylko całka ostatniego
kroku, więc nie ma
obaw o przekroczenie zakresu. A nikt rozsądny nie robi współczynnika na
tysiące czy więcej.

Może o to chodzi.
Ale ... lastProcessValue-processValue teoretycznie może przekroczyc
MAX_INT.
No dobra - w realu absolutnie nie powinna, bo to znaczy, że obiekt
przeskoczył w jednej skrajnej wartosci w drugą, w jeden cykl.
A to się nie powinno zdarzyć.
Choć jak piszesz o regulowaniu prądu .. może. Prąd ma małą bezwładnosć
:-)

J.

J.F
Guest

Mon Feb 02, 2026 12:31 pm   



On Sun, 1 Feb 2026 22:14:13 +0100, J.F wrote:
Quote:
On Sun, 1 Feb 2026 15:56:24 +0100, Janusz wrote:
W dniu 1.02.2026 o 15:26, J.F pisze:
Werja 1
calka = calka+blad
I = Ki* calka

wersja 2
calka - calka+blad*Ki
I = calka

Mam pid-a na avr-a i tam jest wg 1 bo po sumowanu trzeba sprawdzić czy
nie wyszło poza zakres a potem dopiero mnożyć przez współczynnik.

ograniczać zakres można i w wersji 2, pytanie tylko jak uwzględnic Ki
w granicach. A może nie trzeba, bo to problem użytkownika,
albo od strony wartości wyjsciowych to też jest dobre.
Ale samo ograniczanie nie ma sensu, chodzi o tto że to jest zmienna word
czyli 16bit i żeby sie nie "przekręciła"

IMO - ograniczanie wartości całki jak najbardziej ma sens.
[...]



Hm, tak sobie zacząłem sprawę analizować, i wychodzi mi, że
przesterowanie jest niemal nie do uniknięcia.
Mirku - jak to u Ciebie było - "powoli dochodził do zadanej pozycji",
ale od pierwotnej strony, czy powoli wracał po przekroczeniu?

J.

Mirek
Guest

Mon Feb 02, 2026 9:58 pm   



W dniu 2.02.2026 o 11:31, J.F pisze:

Quote:
Hm, tak sobie zacząłem sprawę analizować, i wychodzi mi, że
przesterowanie jest niemal nie do uniknięcia.
Mirku - jak to u Ciebie było - "powoli dochodził do zadanej pozycji",
ale od pierwotnej strony, czy powoli wracał po przekroczeniu?


Zwykle jest lekki przerzut, czasem się zdarza że prawie trafi albo jest
tak mały że go nie zdążę zauważyć. Nie mogę za CHRL uzyskać takiego
przetłumienia, żeby hamował dużo wcześniej i potem dochodził. Tzn, mogę
to zrobić z zerowym Ki i bardzo małym wzmocnieniem, tak że dochodzi
łagodnie ale nie osiąga celu, błąd jest na końcu ogromny.

Co do integral, zrobiłem tak jak tu gość tłumaczy:
https://youtu.be/NVLXCwc8HzM?t=492
Czyli przestaje dodawać błąd jak wynik jest nasycony i znak błędu i
wyniku jest zgodny. Generalnie po strojeniu działa to nieźle, w miarę
szybko kasuje błąd do zera, ale czasem występują denerwujące szarpnięcia
- bardziej je słychać niż widać po silniku. Wygląda na to, że
odpowiedzialny za nie jest człon D. Jak zmniejszę Kd, szarpnięcia
zanikają, ale zaczynają się oscylacje.
Najciekawsze jest to, że jeśli robię jazdę np. od 0 do 50000, to jeśli
zwiększam w tempie 1 na 100uS - jedzie gładko. 2- gładko... aż tak do 7-
zaczynają się szarpania, przy 10 jest koszmar, a tak od 15 znowu
szarpania zaczynają zanikać. Coś mam jeszcze nie tak.
Próbowałem też filtr na samo D - efekt jest podobny, albo nie ma wpływu
albo wprowadza niestabilność.


--
Mirek

J.F
Guest

Tue Feb 03, 2026 1:14 am   



On Mon, 2 Feb 2026 20:58:18 +0100, Mirek wrote:
Quote:
W dniu 2.02.2026 o 11:31, J.F pisze:
Hm, tak sobie zacząłem sprawę analizować, i wychodzi mi, że
przesterowanie jest niemal nie do uniknięcia.
Mirku - jak to u Ciebie było - "powoli dochodził do zadanej pozycji",
ale od pierwotnej strony, czy powoli wracał po przekroczeniu?


Zwykle jest lekki przerzut, czasem się zdarza że prawie trafi albo jest
tak mały że go nie zdążę zauważyć. Nie mogę za CHRL uzyskać takiego
przetłumienia, żeby hamował dużo wcześniej i potem dochodził. Tzn, mogę
to zrobić z zerowym Ki i bardzo małym wzmocnieniem, tak że dochodzi
łagodnie ale nie osiąga celu, błąd jest na końcu ogromny.

No własnie tak sobie uzmysłowiłem.
Załóżmy, że silnik ma pozycję 0 - zadaną i rzeczywistą.
I zmieniamy nagle nastawę na 100.

cykl 1:
eror E=100 pochodna R=0 całka C=0

celowo abstrahuję od konkretnych nastaw Kx - ale wyjscie powinno się
pojawic jakies dodatnie, i ruszyc silnik we własciwą stronę.

Cykl 2:
powiedzmy, że pozycja S wynosi 5.
A więc E=95, R=-5 C=95
pochodną dałem ujemną, bo ona ma działać w przeciwną stronę na
wyjsciu - im szybciej się silnik porusza, tym słabiej jest
sterowany.
Całka ... do dyskusji czy to ma być dodane 95 z biezącego błędu, czy
100 z poprzedniego, to mało istotne.

Wyjscie znow zależy od konkretnych nastaw Kx, ale generalnie dalej
ma być dodatnie bo do zadanej pozycji daleko.
Człon P mniejszy niż poprzednio, człon D ujemny człon I większy niż
poprzednio

Cykl 3:
powiedzmy że pozycja wynosi 15
E=85 R=-10 C=180
opis - jak poprzednio. P mniejszy, I większy, D bardziej ujemny

Cykl 4: niech pozycja wynosi 25
E=75 R=-10 C=255
opis prawie jak wyżej

no i powiedzmy, że podobnych cykli będzie kilka i mamy np
Cykl 7: niech pozycja = 81
E=20 R=-10 C= ... mniejsza o to, ile dokładnie, powiedzmy 500 dla
równego rachunku
na wyjsciu - człon P już znacznie mniejszy,
człon I ... załóżmy, że Ki niezbyt duże, więc i człon I niezbyt
duży, człon D ujemny ... ale chyba nie na tyle, żeby zmienić wyjscie
na ujemne. Tym niemniej suma mniejsza niz poprzednio i silnik zwalnia
.... haha - zwalnia, jak są straty energii, bo jak ciągle dostarczamy
energię, to nie zwalnia, tylko się słabiej rozpędza.
Ale niech zwalnia

Cykl 8: niech pozycja = 88
E=12 R=-7 C=512
na wyjsciu P mniej niż poprzednio, D - mniej ujemne niż poprzenio,
I - ciut większe.
Razem ... niech będzie, że nadal zwalnia, Kd przydało by się duże

Cykl 9: niech pozycja = 93
E=7 R=-5 C=519
opis niech będzie jak wyżej - no bo chcielibyśmy, aby zwalniał,
aczkolwiek to od nastaw K zalezy.


Cykl 10: niech pozycja =96
E=4, R=-3 C=523
no i i po tym przydługim wstępie wyłania sie clou imprezy:
Człon P będzie dochodził do zera, bo błąd się zmniejsza.
Człon D będzie dochodził do zera (minus) bo prędkośc spada.
Ale człon I bedzie dodatni, bo całka całkiem spora i dodatnia.

i dopiero trzeba przesterowania, aby całka zaczęła spadać.

czyli przesterowanie jest w zasadzie nie do unikniecia.

Jedyna potencjalna możliwośc, to tak małe Ki,
żeby ta spora całka dawała na tyle małe wyjscie, że silnik się
jednak nie ruszy, z powodu tarcia. Ale chyba nie o to chodzi.

A Kd jak widac - musi być na tyle duże, żeby silnik zaczął zwalniać
odpowiednio wczesnie przed końcem.


pozostają jeszcze dwie alternatywy:
a) wyłączać całkowanie, gdy prędkość ruchu jest większa niż pewne
minimum - niech dopiero na koncu zajmie się wyzerowaniem błędu.

b) jak Janusz pisze - nie zmieniac skokowo zadanej pozycji,
tylko płynie. Z uwzględnieniem maksymalnej prędkosci napedu, i
możliwych przyspieszeń.
Wtedy regulator będzie miał możliwośc osiągać zadaną pozycję,
i całka wprowadzi tylko drobne poprawki.

I to b) wydaje mi się prawidłowym rozwiązaniem.


I jeszcze jedna sprawa - czy masz tylko jeden silnik/jedną oś ruchu?

Bo weźmy np frezarkę czy ploter XY, chcemy np od pozycji (100, 100)
przejsc do pozycji (200, 300), czyli o wektor (100, 200),
i tu nam nie wystarczy, że osiągniemy zadaną pozycję - często chodzi o
to, żeby poruszało się to po linii prostej, i os Y ruszała się dwa
razy szybciej niż X (przy tych wartościach).

J.

Mirek
Guest

Wed Feb 04, 2026 9:38 pm   



W dniu 3.02.2026 o 00:14, J.F pisze:

Quote:
No własnie tak sobie uzmysłowiłem.
[ciach]


Na razie nie odniosę się do tego, bo walczyłem z innym problemem.
Mianowicie nurtowało mnie, że przy wolnych jazdach, kiedy dodaję do
zadanej pozycji np. 5 kroków na cykl obliczania PID, słychać jakby
regularne stukanie - silnik się kręci w miarę równo, ale coś stuka.
Po całej serii testów, wychodzi na to, że winny jest tu odczyt z
enkodera i np. mamy w kolejnych odczytach przyrost:
5,5,4,6,4,5,5,5,4,5,6,4,5,6,5,5,56,4,5,6,5,5...
dziwne jest to 56 prawda?
Tak samo jak dodaję 7 kroków mamy:
7,7,6,6,8,7,6,7,6,7,6,87,7,6,8,6,7,7,7,7,6,7,7...
Co jest do cholery grane?
Pozycja enkodera to 32-bitowy signed int. Nie sądzę, żeby przy błędzie
konwersji albo jakichś błędach bitów można było uzyskać taki efekt.
Ta wartość wygląda na skumulowany zapis z dwóch odczytów - jakby ktoś
nie zauważył.
Podobnie dzieje się w drugą stronę tylko znak się zmienia. Ta błędna
wartość wygląda na to, że występuje raz na ileś tam kroków - przy
jeździe 5 kroków na 1ms pyka tak mniej-więcej 2 razy na sekundę. przy
szybszej szybciej. Nie jest to wielokrotność obrotu enkodera. Co ciekawe
również nie udało mi się zauważyć błędu (rozjechania się pozycji 0)
enkodera. Pętla odczytująca też w miarę równo chodzi - testowałem ją
przy pomocy micros() i odczyty są w granicach 997-1003us i nie ma
jakichś anomalii przy tych zawyżonych odczytach.


--
Mirek

J.F
Guest

Wed Feb 04, 2026 11:01 pm   



On Wed, 4 Feb 2026 20:38:17 +0100, Mirek wrote:
Quote:
W dniu 3.02.2026 o 00:14, J.F pisze:
No własnie tak sobie uzmysłowiłem.
[ciach]
Na razie nie odniosę się do tego, bo walczyłem z innym problemem.
Mianowicie nurtowało mnie, że przy wolnych jazdach, kiedy dodaję do
zadanej pozycji np. 5 kroków na cykl obliczania PID, słychać jakby
regularne stukanie - silnik się kręci w miarę równo, ale coś stuka.
Po całej serii testów, wychodzi na to, że winny jest tu odczyt z
enkodera i np. mamy w kolejnych odczytach przyrost:
5,5,4,6,4,5,5,5,4,5,6,4,5,6,5,5,56,4,5,6,5,5...
dziwne jest to 56 prawda?
Tak samo jak dodaję 7 kroków mamy:
7,7,6,6,8,7,6,7,6,7,6,87,7,6,8,6,7,7,7,7,6,7,7...
Co jest do cholery grane?

Dziwne jest, że to się tak składa z cyfr, które w ciagu występują.

Quote:
Pozycja enkodera to 32-bitowy signed int. Nie sądzę, żeby przy błędzie
konwersji albo jakichś błędach bitów można było uzyskać taki efekt.
Ta wartość wygląda na skumulowany zapis z dwóch odczytów - jakby ktoś
nie zauważył.

No własnie - moze to nie bląd enkodera, ani jakas kumulacja odczytów,
bo chyba dane są binarne, tylko np błąd raportowania - gdzieś Ci znika
separator, i zamiast 5, 6, jest 56.
Albo jakies zajechanie/nakładanie buforów w pamięci ...

Quote:
Podobnie dzieje się w drugą stronę tylko znak się zmienia. Ta błędna
wartość wygląda na to, że występuje raz na ileś tam kroków - przy
jeździe 5 kroków na 1ms pyka tak mniej-więcej 2 razy na sekundę. przy
szybszej szybciej. Nie jest to wielokrotność obrotu enkodera. Co ciekawe
również nie udało mi się zauważyć błędu (rozjechania się pozycji 0)
enkodera. Pętla odczytująca też w miarę równo chodzi - testowałem ją
przy pomocy micros() i odczyty są w granicach 997-1003us i nie ma
jakichś anomalii przy tych zawyżonych odczytach.

J.

Mirek
Guest

Wed Feb 04, 2026 11:24 pm   



W dniu 4.02.2026 o 22:01, J.F pisze:

Quote:
No własnie - moze to nie bląd enkodera, ani jakas kumulacja odczytów,
bo chyba dane są binarne, tylko np błąd raportowania - gdzieś Ci znika
separator, i zamiast 5, 6, jest 56.

To bym nie miał stukania. Stukanie brało się z tego, że działało Kd.
Później zacząłem szukać zacząłem wypluwać na terminal różne dane. Kiedy
wyplułem if DeltaError > 10: Print(DeltaError) wszystko stało się jasne
- zaczęło wypisywać w rytm stukania. Przy Kd=0 nie stuka, ale wypluwa
rytmicznie dalej - czyli jest to przyczyna a nie skutek.


Quote:
Albo jakies zajechanie/nakładanie buforów w pamięci ...

No ciężko mi to sobie wyobrazić jak by to miało działać.
Jutro jak będę miał na to czas, to spróbuję wyłapać surowe odczyty a nie
przyrosty i sprawdzę dokładnie czy nie przekłamuje enkoder przy kręceniu
tylko w jedną stronę. Bo przy kręceniu tam i z powrotem błędy mogą się
niwelować.

--
Mirek

J.F
Guest

Thu Feb 05, 2026 9:38 am   



On Wed, 4 Feb 2026 22:24:20 +0100, Mirek wrote:
Quote:
W dniu 4.02.2026 o 22:01, J.F pisze:
No własnie - moze to nie bląd enkodera, ani jakas kumulacja odczytów,
bo chyba dane są binarne, tylko np błąd raportowania - gdzieś Ci znika
separator, i zamiast 5, 6, jest 56.

To bym nie miał stukania. Stukanie brało się z tego, że działało Kd.
Później zacząłem szukać zacząłem wypluwać na terminal różne dane. Kiedy
wyplułem if DeltaError > 10: Print(DeltaError) wszystko stało się jasne
- zaczęło wypisywać w rytm stukania. Przy Kd=0 nie stuka, ale wypluwa
rytmicznie dalej - czyli jest to przyczyna a nie skutek.

Ale przy takim błędzie, człon P też powinieni stukać.

Quote:
Albo jakies zajechanie/nakładanie buforów w pamięci ...

No ciężko mi to sobie wyobrazić jak by to miało działać.
Jutro jak będę miał na to czas, to spróbuję wyłapać surowe odczyty a nie
przyrosty i sprawdzę dokładnie czy nie przekłamuje enkoder przy kręceniu
tylko w jedną stronę. Bo przy kręceniu tam i z powrotem błędy mogą się
niwelować.

Nie wiem, na ile dobrze masz zrobioną obslugę enkodera w PIO,
ale potrafię sobie wybrazić, że jak jest gdzieś na granicy, to może
jeden sygnał dużo drgań wytwarzać, a kiepski program do enkodera
(inkrementalnego?)sobie z tym nie radzi. Ale wtedy:
-błędy pozycji byłyby, a nie masz,
-błędy byłyby jakieś losowe, a nie tak 5, 6, 5, 56 ..

Jak Ty go sterujesz? Wysyłasz mu nową pozycję, on ma dojsc i stać,
a pod koniec generuje taki ciąg błędów?

Czy mu wysyłasz narastające pozycje, 5, 10, 15, 20, 25, ... i patrzysz
co się dzieje.

Czy może mu wysłasz polecenie zmiany: "+5" .... "+5" ...."-7" ?

Bo może problem jest gdzies tu od strony zadawania- wysyłasz
20 25 30 35 40 45
cos ginie, czy przekłamuje, RPI odbiera np
20 25 90 35 40

i bład 55 przez chwile jest.
Ale to raczaj znów nie pasuje, to tego co pisałes.


A to "Print(DeltaError)" to jak pokazuje? port COM symulowany na USB?
Może tu jest jakiś problem - nie nadąża wypluc, i gdzies z dwóch robi
się jeden komunikat ... ale chyba nie - bo małych błędów nie
raportujesz.

No i jeszcze jedno może to "drukowanie" czasem czeka na transmisję,
zatrzymuje pętlę regulatora, silnik się dalej kręci, i za chwilę
regulator wypluje duży błąd ... ale znow jakiś bardziej losowy
powinien być ...

J.

Goto page Previous  1, 2, 3, 4, 5  Next

elektroda NewsGroups Forum Index - Elektronika Polska - Optymalne strojenie PID dla silnika z enkoderem na Pi Pico zjawiska oscylacji?

NOWY TEMAT

Regulamin - Zasady uzytkowania Polityka prywatnosci Kontakt RTV map News map