Cyfrowy miernik kąta z magnesami neodymowymi na Arduino

Cyfrowy miernik kąta z magnesami neodymowymi na Arduino

Magnesy neodymowe są łatwo dostępne i tanie. W artykule opisano, jak za pomocą 10 takich magnesów i dwóch czujników Halla wykonać enkoder. Dokładność pomiaru kąta za pomocą tego urządzenia wynosi około 1°.

Precyzyjny pomiar kąta przydatny jest w wielu urządzeniach – ploterach, maszynach CNC, robotach i wielu innych. Konstrukcja ta jest jednocześnie na tyle prosta, że wszystkie komponenty urządzenia mogą być bez problemu osadzone w przegubach robota czy przy serwomotorze sterującym urządzeniem.

Szacunkowy koszt tego enkodera, bez modułu Arduino, to poniżej 10 dolarów (około 40 zł).

Potrzebne elementy

Do zbudowania układu potrzebne:

  • 10 magnesów neodymowych o średnicy 6 mm i wysokości 3 mm,
  • dwa liniowe czujniki Halla 49E/AH49E,
  • śruba M4×20 mm,
  • nakrętka M4,
  • goldpiny,
  • przewody połączeniowe,
  • elementy wydrukowane w 3D (opisane dalej).

Hallotrony AH49E to proste elementy analogowe, dostarczane w obudowach TO-92 i SOT-23-3. Jeśli mamy problem z ich zakupem, a mamy dostęp do modułów KY-024, to można hallotrony pozyskać z tych płytek (są one znacznie częściej dostępne w sklepach dla hobbystów niż sensory Halla, jako elementy dyskretne).

Układ elektroniczny

Schemat obwodu enkodera pokazano na rysunku 1. Jest on bardzo prosty – dwa sensory pola magnetycznego podłączone są do masy (linia czarna) i zasilania – 5 V (linia czerwona). Sygnały wyjściowe z sensorów (linia niebieska i fioletowa) podłączone są do wejść analogowych A0 i A1 modułu Arduino.

Rysunek 1. Schemat układu elektronicznego

W przypadku braku pola magnetycznego napięcia wyjściowe na pinach A0 i A1 z każdego sensora wynoszą Vcc/2 = 2,5 V, w przypadku zasilania układu z linii 5 V (maksymalne napięcie zasilania sensora to 8 V). Odpowiada to odczytowi przetwornika analogowo-cyfrowego (ADC) w Arduino równemu 512 (1023/2 – przetwornik w module Arduino jest 10-bitowy).

Zasada działania

Na rysunku 2 pokazano 10 magnesów ułożonych w okrąg w jednym z wydrukowanych elementów.

Rysunek 2. Ułożenie magnesów w sensorze

Bieguny magnesów skierowane są w osi góra–dół. Ułożone są naprzemiennie. Pole magnetyczne tych magnesów pokazane jest na rysunku 3, gdzie nad magnesami umieszczono wizualizator pola magnetycznego w postaci cienkiej płytki ze specjalnym materiałem.

Rysunek 3. Rozkład pola magnetycznego w sensorze oraz miejsca instalacji sensorów Halla

Żółty kolor pokazuje obszary z najsłabszym polem, gdzie zmiana wartości na hallotronie będzie najsłabsza. Kolor niebieski reprezentuje z kolei maksimum pola, czyli miejsca, gdzie czujnik Halla wskazywać będzie najwyższą amplitudę. Powodem, dla którego żółte obszary są okrągłe, jest to, że linie pola z każdego z północnych biegunów magnetycznych wychodzą we wszystkich kierunkach. Płytka do wizualizacji pola magnetycznego nie jest wymagana podczas konstruowania tego układu.

Zastosowany układ magnesów skutkuje podzieleniem okręgu na 10 segmentów, każdy po 36°. Gdy jeden czujnik trafi na środek segmentu, drugi jest pomiędzy nimi. Dzięki temu, w czasie obrotu, jeśli jeden z sensorów na wyjściu będzie miał przebieg sinusoidalny, to na wyjściu drugiego zaobserwujemy cosinus. Zobrazowano to na rysunku 4.

Rysunek 4. Schematycznie omówiona zasada działania urządzenia

Podsumowując:

  • w układzie jest parzysta liczba magnesów,
  • każde 2 naprzemiennie ułożone magnesy wytwarzają 1 falę sinusoidalną,
  • przy 10 magnesach otrzymujemy 5 pełnych przebiegów sinusoidalnych na obrót wału,
  • każda fala sinusoidalna reprezentuje obrót wału o 360/5=72°,
  • każda połowa cyklu to 72/2=36°,
  • odległość między maksimum a minimum wynosi 36/2=18°, co oznacza, że czujniki muszą być oddalone od siebie o N*36+18°. W projektowanym enkoderze zadano N = 1, więc czujniki są oddalone od siebie o 54°. Ustawienie N=0 nie było możliwe ze względu na wielkość czujników.

Wzór na obliczenie kąta wału jest zatem następujący:

gdzie:

  • n – liczba ukończonych fal sinusoidalnych,
  • a – wielkość przebiegu sinusoidalnego w dowolnym momencie (amplituda mierzona przez sensor Halla),
  • b – wielkość przebiegu cosinus w dowolnej chwili (amplituda mierzona przez sensor Halla),
  • 5 – liczba fal sinusoidalnych w 360°, w tym przypadku 10 magnesów podzielone na 2. Dla innej liczby magnesów możemy uzyskać inny parametr.

Uwagi praktyczne

Pomiędzy magnesami a sensorami z efektem Halla wymagana jest przekładka, która zapobiegnie nasyceniu się hallotronów. Optymalny odstęp występuje wtedy, gdy odczyty z ADC ciągle się zmieniają (tj. nigdy nie pozostają na stałym poziomie) podczas obracania magnesów. Grubość elementu dystansowego na poziomie 2,7 mm okazała się optymalna dla magnesów, z jakich korzystał autor.

Liczba cykli sinusa musi być śledzona w oprogramowaniu. Możliwe są inne kombinacje magnesów, pod warunkiem że pary magnetyczne podzielą się równomiernie na 360°. Na przykład 12 magnesów wytworzy 6 cykli, z których każdy reprezentuje obrót wału o 60°, z przejściami przez zero, co 30°. W tym przypadku odległość między maksimum a minimum wynosi 30/2=15°, co oznacza, że czujniki muszą być oddalone od siebie o N*30+15°. W tym przykładzie kąt wału będzie wynosił:

Uwagi

Maksymalna i minimalna zmiana wyjścia sensora Halla zależy od orientacji struktury czujnika Halla względem kierunku pola magnetycznego. W przypadku tego projektu sensory leżą płasko w drugiej części z druku 3D, co oznacza, że największą zmianę sygnału wyjściowego obserwuje się, gdy znajdują się one bezpośrednio nad magnesami.

Jeśli element z efektem Halla zostanie obrócony o 90°, wówczas minimalna zmiana napięcia wyjściowego nastąpi nad magnesami, a maksymalny sygnał pomiędzy nimi.

W przypadku braku pola magnetycznego, odczyty Arduino ADC dla A0 i A1 będą bliskie 512. Odczyty te będą odchylać się w czasie ruchu magnesów po obu stronach wartości 512 o około ±300 (w przybliżeniu, w zależności od odległości i użytych magnesów). Idealnie by było, aby dynamika osiągała ±500.

Elementy z druku 3D

W technologii druku 3D wykonano trzy elementy: dwa ramiona (pokazane na rysunku 5) oraz przekładkę.

Rysunek 5. Projkt ramion do wydrukowania w technice 3D

Projekty wszystkich elementów udostępnione są na stronie z projektem w formacie plików STL. Każda część została wydrukowana na drukarce 3D Voxelab Aquila przy użyciu filamentu PLA o średnicy 1,75 mm. Rozmiar zastosowanej dyszy to 0,4 mm przy wysokości warstwy 0,2 mm.

Oprogramowanie

Listing kodu Arduino neodymium_angle_encoder.ino pokazano na listingu 1. Szkic kopiujemy do Arduino IDE, kompilujemy i wgrywamy do Arduino. Od teraz, po uruchomieniu moduł mierzy wartości i oblicza kąt. Wyniki przesyłane są do komputera przez interfejs szeregowy w czytelnym dla człowieka formacie. Można zmodyfikować ten fragment programu, jeśli chcemy, aby moduł współpracował z innym systemem.

Listing 1. Kod programu czujnik kąta

#define RAD_TO_DEG 57.295779513082320876798154814105

const byte ADC0 = A0;
const byte ADC1 = A1;
const byte Switch = 8;
float A, Amax, Amin, Amid, Apeak;
float B, Bmax, Bmin, Bmid, Bpeak, Bnormal;
// 360 dstopni odpowiada obrotowi osi o 72 stopnie
float RawAngle;

// identyfikacja ćwiartki
int QuadrantNumber = 0;
// zmienna do śledzenia liczby obrotów
int PreviousQuadrantNumber = 0;
// liczba okresów sinusa
int   N = 0;
// kąt początkowy
float StartAngle = 0;
// kąt wytarowany w oparciu o kąt początkowy
float TaredAngle = 0;
// kąt całkowity
float ShaftAngle = 0;
unsigned long Timer;
const unsigned long Next_sample = 1000L;

void setup() {
 Serial.begin(115200);
 Serial.flush();
 while (Serial.available()) Serial.read();
 pinMode (Switch, INPUT_PULLUP);
 pinMode(ADC0, INPUT);
 pinMode(ADC1, INPUT);
 // Jesli pin 8 na masie, uruchamia kalibracje
 if (!digitalRead(Switch)) {
   calibrate();
 }
 // Pobiera surowy kąt
 StartAngle = raw_angle();
 Timer = millis() + Next_sample;
}

void loop() {
 RawAngle = raw_angle();
 TaredAngle = tare(RawAngle);
 // Zlicza liczbę skompletowanych okresów
 N = numberOfSinewaves();
 ShaftAngle = N * 72 + TaredAngle/5;
 if (millis() >= Timer) {
   Timer += Next_sample;
   Serial.print("Shaft Angle: ");
   Serial.print(ShaftAngle);
   Serial.println(" degrees");
 }
}

// Mierzy kąt w zakresie 0..72 w segmencie
float raw_angle() {
 float adc0;
 float adc1;
 adc0 = (float)analogRead(ADC0);
 adc1 = (float)analogRead(ADC1);
 A = adc0 – Amid;
 B = (adc1 – Bmid) * Bnormal;
 float angle = atan2(B, A) * RAD_TO_DEG;
 // Dla kąta pomiędzy 180 a 360
 // potrzebna jest negacja atan2
 if (angle < 0) angle += 360;
 return angle;
}

float tare(float angle) {
 // This tares the position
 float taredAngle = angle – StartAngle;
 // If the calculated angle is negative,
 // we need to "normalize" it
 if (taredAngle < 0) {
   // correction for negative numbers
   // (i.e. -15 becomes +345)
   taredAngle = taredAngle + 360;
 }
 return taredAngle;
}

int numberOfSinewaves() {  // Zliczanie okresów sinusa
 static int sinewaves = 0;
 // I ćwiartka
 if (TaredAngle >= 0 && TaredAngle <= 90) {
   QuadrantNumber = 1;
 }
 // II ćwiartka
 if (TaredAngle > 90 && TaredAngle <= 180) {
   QuadrantNumber = 2;
 }
 // III ćwiartka
 if (TaredAngle > 180 && TaredAngle <= 270) {
   QuadrantNumber = 3;
 }
 // IV ćwiartka
 if (TaredAngle > 270 && TaredAngle < 360) {
   QuadrantNumber = 4;
 }
 // Czy nastąpiła zmiana ćwiartki
 if (QuadrantNumber != PreviousQuadrantNumber) {
   if (QuadrantNumber == 1 && PreviousQuadrantNumber == 4) {
     sinewaves++;
   }
   if (QuadrantNumber == 4 && PreviousQuadrantNumber == 1) {
     sinewaves--;
   }
   // Aktuializacja ćwiartki
   PreviousQuadrantNumber = QuadrantNumber;
 }
 return sinewaves;
}

void calibrate() {  // Kalibracja
 Amax = -10000.0;
 Amin = 10000.0;
 Bmax = -10000.0;
 Bmin = 10000.0;
 while (!digitalRead(Switch)) {
   A = (float)analogRead(ADC0);
   B = (float)analogRead(ADC1);
   Amax = max(Amax, A);
   Amin = min(Amin, A);
   Bmax = max(Bmax, B);
   Bmin = min(Bmin, B);
   Amid = (Amax + Amin)/2;
   Apeak = Amax – Amid;
   Bmid = (Bmax + Bmin)/2;
   Bpeak = Bmax – Bmid;
   Bnormal = Apeak/Bpeak;
   Serial.print("   Amax: "); Serial.println(Amax);
   Serial.print("   Amin: "); Serial.println(Amin);
   Serial.print("   Amid: "); Serial.println(Amid);
   Serial.print("  Apeak: "); Serial.println(Apeak);
   Serial.print("   Bmax: "); Serial.println(Bmax);
   Serial.print("   Bmin: "); Serial.println(Bmin);
   Serial.print("   Bmid: "); Serial.println(Bmid);
   Serial.print("  Bpeak: "); Serial.println(Bpeak);
   Serial.print("Bnormal: "); Serial.println(Bnormal);
   Serial.println(" ");
   delay(100);
 }
}

Jednak, aby układ poprawnie i dokładnie mierzył kąt, należy go najpierw skalibrować. W tym celu wykonujemy następujące czynności:

  • podłączamy przewód między wyprowadzenie nr 8 Arduino a masę,
  • obracamy ramię magnesu kilka razy, aż odczyty na wyświetlaczu będą stałe,
  • kopiujemy odczyty wyświetlacza do pasujących zmiennych w nagłówku Arduino,
  • zdejmujemy zworkę,
  • ponownie kompilujemy kod i przesyłamy go do Arduino.

Dalsza kalibracja nie powinna być konieczna, wszystkie kąty odnoszą się do pozycji ramion enkodera. Na rysunku 6 pokazano odczyty przy kalibracji oraz przy pomiarach kąta.

Rysunek 6. Odczyty przy kalibracji oraz przy pomiarach kąta

Przejście następuje, gdy przewód połączeniowy z wyprowadzenia nr 8 Arduino do masy zostanie usunięty. Na rysunku 7 pokazano konfigurację testową, jaką autor przygotował do pomiaru dokładności enkodera. Linie radialne są rozmieszczone co 10°.

Rysunek 7. Konfiguracja testowa, jaką autor przygotował do pomiaru dokładności enkodera

Na rysunku 8 pokazano wykres zależności błędu odczytu kąta od mierzonego kąta. Wszystkie zmierzone wartości mieszczą się w zakresie 1° od rzeczywistego kąta.

Rysunek 8. Wykres zależności błędu odczytu kąta od mierzonego kąta

Podsumowanie

Zaprezentowany projekt pokazuje, w jaki sposób zbudować relatywnie precyzyjny enkoder kątowy przy użyciu jedynie 10 magnesów neodymowych i dwóch sensorów z efektem Halla. Zmierzona dokładność enkodera mieści się w granicach 1°. Łatwo można ją podnieść, dodając więcej magnesów lub zmieniając przetwornik analogowo-cyfrowy w układzie na zewnętrzny, o większej rozdzielczości.

Konstrukcja została zaprojektowana tak, aby komponenty mogły być łatwo osadzone np. w przegubach ramienia robota czy w serwomotorze plotera. Wykonanie takiego enkodera jest bardzo tanie i nie powinno przekraczać 40 zł (z wyłączeniem modułu Arduino). Po zmianach w kodzie mikrokontroler może jednocześnie obsługiwać większą liczbę takich enkoderów – jest to ograniczone jedynie liczbą wejść analogowych.

Nikodem Czechowski, EP

Źródło:
https://www.instructables.com/Neodymium-Angle-Encoder

Artykuł ukazał się w
Elektronika Praktyczna
sierpień 2023

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio maj - czerwiec 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje kwiecień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich maj 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów