Tutorial I: Motor With Encoder Control Demo

From Waveshare Wiki
Jump to: navigation, search

Modules Usage Tutorial

Motor With Encoder

A motor with an encoder can obtain feedback for closed-loop speed control. The following provides a demo for reading the speed of the encoder motor.

Demo

Upload Demo

After downloading the zip package, open speedget.ino, use the USB cable to connect the multifunctional driver board and the computer (here inserted into the USB Type-C port of the multifunctional driver board), click on "Tools" → "Ports", and then click on the newly appeared COM port.
UGV1 doenload03EN.png
In Arduino IDE, click "Tools" → "Development Board" → "ESP32" → "ESP32 Dev Module", select the development board and the port and then upload the demo. After uploading the demo, connect the encoder motor and the motor interface PH2.0 6P on the driver board, connect the XH2.54 power supply interface to the power supply and run the demo, and then open the serial monitor of Arduino IDE to read the speed of the left and right motors.
UGV01 tutorial II01.pngUGV01 Tutoria2.png

Demo Analysis

// === === === MOTOR PIN DEFINITION Motor Related Pin Definition === === ===

// Define the pins for the A/B encoders. Each encoder is feedback by two signal wires, which are connected to the corresponding Hall component.
// For one encoder alone: the motor speed is calculated by detecting the frequency of one of the Hall components, and the direction of rotation is determined by judging the status of the other Hall sensor.
// The direction of rotation is determined by judging the high and low level status of the other Hall sensor.

// Encoder A pin definition
const uint16_t AENCA = 35;        // Encoder A input A_C2(B)
const uint16_t AENCB = 34;        // Encoder A input A_C1(A)

// Encoder B pin definition
const uint16_t BENCB = 16;        // Encoder B input B_C2(B)
const uint16_t BENCA = 27;        // Encoder B input B_C1(A)

// Used to calculate the number of level changes of one of the Hall sensors of the encoder during the "interval" time (in ms).
// Since RISING is used later to initialize the interrupt, it is specifically the number of times the low level changes to a high level.
volatile long B_wheel_pulse_count = 0;
volatile long A_wheel_pulse_count  = 0;

// the cycle time of the speed calculation, and the unit is ms
int interval = 100;

// the current time
long currentMillis = 0;

// to store the rotation speed of the left and right sides
double rpm_B = 0;
double rpm_A = 0;

// The reduction ratio of the motor, the motor speed of the geared motor and the output shaft speed are not the same
// For example, in the case of the DCGM3865 motor, the reduction ratio is 1:42, which means that the motor makes 42 revolutions and the output shaft makes 1 revolution.
// Corresponding to one revolution of the output shaft, the more revolutions the motor needs to make, the greater the reduction ratio and usually the greater the torque
// The following takes the DCGM3865 motor as an example (the reduction ratio is 1:42)
double reduction_ratio = 42;

// Number of encoder lines, one revolution of the motor, the number of level changes of one Hall sensor of the encoder
int ppr_num = 11;

// Number of level changes of one Hall sensor of the encoder for one revolution of the output shaft
double shaft_ppr = reduction_ratio * ppr_num;

// IRAM_ATTR is a macro definition for modifying functions and variables.
// Indicates that the function or variable will be placed in IRAM (Instruction RAM), which is the instruction memory of the ESP32 chip.
// Functions and variables modified with IRAM_ATTR will be placed in IRAM by the compiler.
// This can increase the execution speed of these functions and variables, especially for code segments that need to be executed frequently.
// This can be useful in real-time demanding applications.

// The callback function for the interrupt function, refer to the attachInterrupt() function later, is called when the level of a particular Hall encoder
// function is called when the level of a particular Hall encoder changes from low to high.
// In this function, the levels of another Hall sensor along the way are judged to determine the direction of rotation.

void IRAM_ATTR B_wheel_pulse() {
  if(digitalRead(BENCA)){
    B_wheel_pulse_count++;
  }
  else{
    B_wheel_pulse_count--;
  }
}

void IRAM_ATTR A_wheel_pulse() {
  if(digitalRead(AENCA)){
    A_wheel_pulse_count++;
  }
  else{
    A_wheel_pulse_count--;
  }
}

void setup(){
  // Setting the operating mode of the encoder-related pins
  // When using encoders, it is often necessary to enable pull-up resistors on their pins. This is because encoders usually use open-drain outputs or outputs that are passive (open collector).

  // When the encoder pins are configured in input mode, if no external pull-up resistor or internal pull-up resistor (using the INPUT_PULLUP option) is connected to the pins.
  // The level of the pin may drift or be in an indeterminate state. This may result in errors or instability when reading the encoder signals.

   // By using the INPUT_PULLUP option, you can enable an internal pull-up resistor on the pin to set the default level of the pin to high (logic 1).
  // This effectively prevents the pin level from drifting and ensures that the pin stays in the defined state when there is no external signal. When the encoder generates a signal
  // the level of the pin will change and the state change can be detected by means of an interrupt or polling.

  // Therefore, it is common practice to use the INPUT_PULLUP option when configuring encoder pins to improve the reliability and stability of the encoder signals.
  pinMode(BENCB , INPUT_PULLUP);
  pinMode(BENCA , INPUT_PULLUP);

  pinMode(AENCB , INPUT_PULLUP);
  pinMode(AENCA , INPUT_PULLUP);
 
  //  Set the interrupt and the corresponding callback function to call the B_wheel_pulse function when BEBCB changes from low to high (RISING).
  attachInterrupt(digitalPinToInterrupt(BENCB), B_wheel_pulse, RISING);
  // Set the interrupt and the corresponding callback function to call the A_wheel_pulse function when AEBCB changes from low to high (RISING).
  attachInterrupt(digitalPinToInterrupt(AENCB), A_wheel_pulse, RISING);

  // Initialize the serial port, and the baud rate is 115200
  Serial.begin(115200);
  // Wait for serial port initialization to complete
  while(!Serial){}
}

void loop(){
  // Calculate the speed of the output shaft of the B-channel motor in revolutions per minute.
  rpm_B = (float)((B_wheel_pulse_count / shaft_ppr) * 60 * (1000 / interval));
  B_wheel_pulse_count = 0;

  // Calculate the speed of the output shaft of the A-channel motor in revolutions per minute.
  rpm_A = (float)((A_wheel_pulse_count / shaft_ppr) * 60 * (1000 / interval));
  A_wheel_pulse_count = 0;

  Serial.print("RPM_A: ");Serial.print(rpm_A);Serial.print("   RPM_B: ");Serial.println(rpm_B);
  Serial.println("--- --- ---");

  delay(interval);
}

Resources