Đo tốc độ động cơ dùng encoder với Arduino

Encoder là gì?

Đo vận tốc động cơ dùng encoder, tín hiệu từ encoder tạo ra những dạng xung vuông có tần số thay đôi phụ thuộc vào vào vận tốc động cơ. Do đó những xung vuông này được đưa vào bộ vi giải quyết và xử lý để đếm số xung trong khoảng chừng thời hạn được cho phép từ đó ta hoàn toàn có thể tính được giá trị tốc độ của động cơ .

Nguyên tắc hoạt động của Encoder

Nguyên lý cơ bản của encoder, đó là một đĩa tròn xoay, quay quanh trục. Trên đĩa có những lỗ ( rãnh ). Người ta dùng một đèn led để chiếu lên mặt đĩa. Khi đĩa quay, chỗ không có lỗ ( rãnh ), đèn led không chiếu xuyên qua được, chỗ có lỗ ( rãnh ), đèn led sẽ chiếu xuyên qua. Khi đó, phía mặt bên kia của đĩa, người ta đặt một con mắt thu. Với những tín hiệu có, hoặc không có ánh sáng chiếu qua, người ta ghi nhận được đèn led có chiếu qua lỗ hay không. Số xung đếm được và tăng lên nó tính bằng số lần ánh sáng bị cắt .
Như vậy là encoder sẽ tạo ra những tín hiệu xung vuông và những tín hiệu xung vuông này được cắt từ ánh sáng xuyên qua lỗ. Nên tần số của xung đầu ra sẽ phụ thuộc vào vào vận tốc quay của tấm tròn đó .

Các loại cơ bản của Encoder là gì?

Gồm 2 loại chính: Encoder tuyệt đối và Encoder tương đối.

Encoder tuyệt đối (adsolute encoder): Đã gọi là tuyệt đối thì tức là tín hiệu ta nhận được từ Encoder cho biết chính xác vị trí của Encoder mà người sử dụng không phải xử lý thêm gì cả.

Encoder kiểu tuyệt đối có cấu trúc gồm những phần sau : Bộ phát ánh sáng ( LED ), đĩa mã hóa ( có chứa dải băng mang tín hiệu ), một bộ thu ánh sáng nhạy với ánh sáng phát ra ( photosensor ). Đĩa mã hóa ở encoder tuyệt đối được sản xuất từ vật tư trong suốt, người ta chia mặt đĩa thành những góc đều nhau và những đường tròn đồng tâm .
Các đường tròn đồng tâm và nửa đường kính số lượng giới hạn những góc hình thành những phân tố diện tích quy hoạnh. Tập hợp những phân tố diện tích quy hoạnh cùng số lượng giới hạn bởi 2 vòng tròn đồng tâm gọi là dải băng. Số dải băng tùy thuộc vào công nghệ tiên tiến sản xuất ( chủng loại loại sản phẩm ), ứng với một dải băng ta có một đèn LED và một bộ thu .

Ưu điểm: giữ được giá trị tuyệt đối khi Encoder mất nguồn.

Nhược điểm: giá thành cao vì chế tạo phức tạp, đọc tín hiệu khó.

Encoder tương đối

Về cơ bản thì Encoder kiểu tương đối đều giống nhau, chỉ khác ở đĩa mã hóa. Ở encoder tương đối thì đĩa mã hóa gồm 1 dải băng tạo xung. Ở dải băng này được chia làm nhiều lỗ bằng nhau và cách đều nhau ( hoàn toàn có thể vật liệu trong suốt để ánh sáng chiếu qua ) .
– Encoder tương đối ( incremental encoder ) : phát ra tín hiệu tăng dần hoặc theo chu kỳ luân hồi
+ Đĩa mã hóa gồm có một dãi băng tạo xung, thường được chia thành nhiều lỗ bằng nhau và được cách đều nhau .
+ Chất liệu hoàn toàn có thể là trong suốt để giúp ánh sáng chiếu qua .

+ Là Encoder chỉ có 1,2 hoặc tối đa 3 vòng lỗ, và thường có thêm một lỗ định vị.

+ Ưu điểm: giá thành rẻ, chế tạo đơn giản, xử lý tín hiệu trả vềdễ dàng.

Nhược điểm: dễ bị sai lệch về xung khi trả về. Sẽ tích lũy sai số khi hoạt động lâu dài.

Cách thức xác định chiều quay của encoder

Encoder thường có 3 kênh ( 3 ngõ ra ) gồm có kênh A, kênh B và kênh I ( Index ). Trong hình 2 bạn thấy hãy quan tâm một lỗ nhỏ bên phía trong của đĩa quay và một cặp phat-thu dành riêng cho lỗ nhỏ này. Đó là kênh I của encoder. Cứ mỗi lần motor quay được một vòng, lỗ nhỏ Open tại vị trí của cặp phát-thu, hồng ngoại từ nguồn phát sẽ xuyên qua lỗ nhỏ đến cảm ứng quang, một tín hiệu Open trên cảm ứng. Như thế kênh I Open một “ xung ” mỗi vòng xoay của motor .
Bên ngoài đĩa quay được chia thành những rãnh nhỏ và một cặp thu-phát khác dành cho những rãnh này. Đây là kênh A của encoder, hoạt động giải trí của kênh A cũng tựa như kênh I, điểm khác nhau là trong 1 vòng xoay của motor, có N “ xung ” Open trên kênh A. N là số rãnh trên đĩa và được gọi là độ phân giải ( resolution ) của encoder. Mỗi loại encoder có độ phân giải khác nhau, có khi trên mỗi đĩa chĩ có vài rãnh nhưng cũng có trường hợp đến hàng nghìn rãnh được chia. Để điều khiển động cơ, bạn phải biết độ phân giải của encoder đang dùng. Độ phân giải ảnh hưởng tác động đến độ đúng mực điều khiển và cả chiêu thức điều khiển .
Trên những encoder còn có một cặp thu phát khác được đặt trên cùng đường tròn với kênh A nhưng lệch một chút ít, đây là kênh B của encoder. Với 2 tín hiệu xung A và B giúp tất cả chúng ta xác lập chiều quay của động cơ. Tín hiệu xung từ kênh B có cùng tần số với kênh A nhưng lệch sóng 90 độ .
Khi cảm ứng A khởi đầu bị che thì cảm ứng B trọn vẹn nhận được hồng ngoại xuyên qua, và ngược lại. Hình trên là dạng xung ngõ ra trên 2 kênh. Xét trường hợp motor quay cùng chiều kim đồng hồ đeo tay, tín hiệu “ đi ” từ trái sang phải. Bạn hãy quan sát lúc tín hiệu A chuyển từ mức cao xuống thấp ( cạnh xuống ) thì kênh B đang ở mức thấp. trái lại, nếu động cơ quay ngược chiều kim đồng hồ đeo tay, tín hiệu “ đi ” từ phải qua trái. Lúc này, tại cạnh xuống của kênh A thì kênh B đang ở mức cao. Như vậy, bằng cách phối hợp 2 kênh A và B tất cả chúng ta không những xác lập được góc quay ( trải qua số xung ) mà còn biết được chiều quay của động cơ ( trải qua mức của kênh B ở cạnh xuống của kênh A ) .

Đo tốc độ động cơ với Encoder 1 kênh

Sử dụng Encoder 20 xung ( trên 1 vòng xoay của động cơ )
Arduino UnoLCD16x02L298N ModuleEncoder8RS3ENGNDRW7D46D55D64D712IN113IN29ENA2D05V5VVCCGNDGNDGND
Code :

#include 
 
int motorIn1 = 12;
int motorIn2 = 13;
int motorEnA = 9;
 
int encoder = 2;
 
volatile unsigned int counter;
int rpm;
 
const int rs = 8, en = 3, d4 = 7, d5 = 6, d6 = 5, d7 = 4;
 
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
 
void setup() {
  pinMode(motorIn1, OUTPUT);
  pinMode(motorIn2, OUTPUT);
  pinMode(motorEnA, OUTPUT);
  pinMode(encoder, INPUT);
 
  digitalWrite(encoder, HIGH);
  digitalWrite(motorIn1, HIGH);
  digitalWrite(motorIn2, LOW);
  analogWrite(motorEnA, 100);
 
  attachInterrupt(0,countpulse,RISING);
  lcd.begin(16, 2);
}
 
void countpulse(){
        counter++;
}
 
void loop() {
  static uint32_t previousMillis;
  if (millis() - previousMillis >= 1000) {
            rpm = (counter/20)*60;          
            counter = 0;
            previousMillis += 1000;
  }
 
  lcd.setCursor(0,0);
  lcd.print("Speed: ");
  lcd.setCursor(7,0);
  lcd.print(rpm);
  lcd.print(" rps");
  delay(1);
}

Code khác dùng cho Encode 1 kênh :

int encoder_pin = 2;  // The pin the encoder is connected           
unsigned int rpm;     // rpm reading
volatile byte pulses;  // number of pulses
unsigned long timeold; 
// The number of pulses per revolution
// depends on your index disc!!
unsigned int pulsesperturn = 20;

 void counter()
 {
    //Update count
      pulses++;    
 }

void setup()
 {
   Serial.begin(9600);
     //Use statusPin to flash along with interrupts
   pinMode(encoder_pin, INPUT);
   
   //Interrupt 0 is digital pin 2, so that is where the IR detector is connected
   //Triggers on FALLING (change from HIGH to LOW)
   attachInterrupt(0, counter, FALLING);
   // Initialize
   pulses = 0;
   rpm = 0;
   timeold = 0;

 }

 void loop()
 {
   if (millis() - timeold >= 1000){  /*Uptade every one second, this will be equal to reading frecuency (Hz).*/
 
  //Don't process interrupts during calculations
   detachInterrupt(0);
   //Note that this would be 60*1000/(millis() - timeold)*pulses if the interrupt
   //happened once per revolution
   rpm = (60 * 1000 / pulsesperturn )/ (millis() - timeold)* pulses;
   timeold = millis();
   Serial.print(pulses);
   pulses = 0;
   
   //Write it out to serial port
   
   Serial.print(" RPM = ");
   Serial.println(rpm,DEC);
   //Restart the interrupt processing
   attachInterrupt(0, counter, FALLING);
   }
  }

Lập trình hiển thị tốc độ động cơ với Rotary Encoder 2 kênh

Thông số kỹ thuật:
       • Điện áp cung cấp: 3.3V
       • Độ phân giải 20 xung/vòng.

       • Các chân tín hiệu:
          +      : chân cấp nguồn dương
         GND: chân cấp nguồn âm
         CLK:  phase A
         DT:    phase B
         SW:   button.

Cách đọc encoder bằng Arduino

Sử dụng ngắt ngoài: đây là phương pháp dễ nhưng chính xác để đọc encoder và cũng là phương pháp được dùng trong bài học này. Ý tưởng của phương pháp rất đơn giản, chúng ta nối kênh A của encoder với 1 ngắt ngoài (INT2 chẳng hạn) và kênh B với một chân nào đó bất kỳ (không phải chân ngắt). Cứ mỗi lần ngắt ngoài xảy ra, tức có 1 xung xuất hiện trên ở kênh A thì trình phục vụ ngắt ngoài tự động được gọi. Trong trình phục vụ ngắt này chúng ta kiểm tra mức của kênh B, tùy theo mức của kênh B chúng ta sẽ tăng biến đếm xung lên 1 hoặc giảm đi 1. Tuy nhiên, bạn cần phải tính toán rất cẩn thận khi sử dụng phương pháp này. Trong bài này, chúng ta chọn độ phân giải của encoder là 20 (20 xung trên mỗi vòng quay, loại encoder đơn giản nhất).

Code trên Arduino

Thư viện tiếp xúc I2C cho LCD16x02, những bạn hoàn toàn có thể tải tại đây

#include 
#include 
LiquidCrystal_I2C lcd(0x3F,16,2);
// Cách nối các chân trên Encoder quay
#define  encoderPinA  2   // Tương ứng chân DT trên Encoder quay
#define  encoderPinB  3   // Tương ứng chân CLK trên Encoder quay
// Chân + nối nguồn 3.3V và chân GND nối cực âm
volatile  int encoderPos = 0;  // Cho vị trí đầu bằng 0
int lastReportedPos = 1;   // Vị trí cuối bằng 1
static boolean rotating=false;      // Quản lý debounce (giống như là chống nhiễu)

// các biến cho trình phục vụ ngắt interrupt service routine vars
boolean A_set = false;             
boolean B_set = false;

//Đo tốc độ
 int newposition;
 int oldposition=0;
 long newtime;
 long oldtime=0;
 int vantoc = 0;
 int ganvantoc = 0;
 int ganxung = 0;
 int sovong = 0;

 void setup() {
  lcd.begin(20,4);  
  lcd.init();                      // initialize the lcd

  lcd.backlight();
  pinMode(encoderPinA, INPUT_PULLUP); // INPUT-PULLUP tương đương Mode INPUT và tự động nhận trạng thái HIGH hoặc LOW
  pinMode(encoderPinB, INPUT_PULLUP);
 // Chân encoder trên ngắt 0 (chân 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// Chân encoder trên ngắt 1 (chân 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(9600);  // chuyển dữ liệu lên cổng Serial Port
  lcd.setCursor(0,1);
  lcd.print("SO VONG = ");
   }
// Vòng lặp chính, công việc được thực hiện bởi trình phục vụ ngắt
void loop() {
   delay(1000);
   rotating = true;  // Khởi động bộ debounce (có thể hiểu như bộ chống nhiễu)
   newtime=millis();
   newposition=encoderPos;
   detachInterrupt(0);
   detachInterrupt(1);
   vantoc = (newposition-oldposition)*60/20;
    Serial.print("vantoc=");
    Serial.println(vantoc,DEC);
    oldposition=newposition;
    oldtime=newtime;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("TOC DO  = ");
    lcd.setCursor(11,0);
    lcd.print(vantoc);
    lcd.print(" RPM");
    lcd.setCursor(0,1);
    lcd.print("SO VONG = ");
    lcd.setCursor(11,1);
    lcd.print(sovong,DEC);
    attachInterrupt(0, doEncoderA, CHANGE);
    attachInterrupt(1, doEncoderB, CHANGE); 
}
// Ngắt khi chuyển trạng thái của A
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // Chờ 1 chút
  // Kiểm tra việc chuyển đổi trạng thái, xem có thật sự thay đổi trạng thái chưa
  if( digitalRead(encoderPinA) != A_set ) {  // debounce một lần nữa
    A_set = !A_set;
    // Cộng 1 nếu có tín hiệu A rồi có tín hiệu B       
    if ( A_set && !B_set )
      encoderPos += 1;
      ganxung += 1;
      sovong=encoderPos/20;
    if (ganxung == 42){ganxung=0;}

    rotating = false;  // Không cần debounce nữa cho đến khi được nhấn lần nữa
      }
  }
// Ngắt khi thay đổi trạng thái ở B, tương tự như ở A

void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  Trừ 1 nếu B rồi đến A
    if( B_set && !A_set )
      encoderPos -= 1;
      rotating = false;
  }
}

Tham khảo : TDHShop, AVR, https://www.teachmemicro.com/

Alternate Text Gọi ngay