Arduino控制带编码器的直流电机速度

news2024/9/20 22:45:47

Arduino DC Motor Speed Control with Encoder, Arduino DC Motor Encoder

作者

  • How to control dc motor with encoder:
  • DC Motor with Encoder + Arduino, Circuit Diagram:
  • Driving the Motor with Encoder and Arduino:
  • Control DC motor using Encoder feedback loop:

How to control dc motor with encoder:

Arduino DC Motor Speed Control with Encoder- I have been using different types of stepper motors, Servo motors, and DC Motors for quite a long time in different intermediate and advanced level projects. DC motors are more frequently used than Stepper Motors and Servo Motors. If you have watched my videos and read my articles then you should know DC motors are quite different from Stepper Motors and servo Motors. These three types of motors have a different construction. The stepper motors and servo motors are designed in a way that we can control their position. We can control the steps in the forward and reverse directions. Servos can move from 0 to 180 degrees, so you can move to any position between 0 and 180. Likewise, in stepper motors, you can control the steps precisely and this is the reason stepper motors are used in CNC machines, 3d printers, etc.
使用编码器控制 Arduino 直流电机速度–我在不同的中高级项目中使用不同类型的步进电机、伺服电机和直流电机已经有很长一段时间了。 直流电机比步进电机和伺服电机更常用。 如果你看过我的视频和文章,就应该知道直流电机与步进电机和伺服电机有很大不同。 这三种电机的结构各不相同。 步进电机和伺服电机的设计方式使我们可以控制它们的位置。 我们可以控制步进的前进和后退方向。 舵机可以移动到 0 至 180 度之间的任何位置。 同样,步进电机可以精确控制步数,这也是步进电机被用于数控机床、3d 打印机等的原因。
On the other hand, when a dc motor is powered up it immediately starts rotating, it continuously rotates, you can’t exactly control its position until you use a specific technique. You can’t 100% control a DC motor like Stepper motor and Servo, but if you add an encoder it can really change the whole game. With an encoder being added, you can keep track of the motor revolutions, the amount of distance it has covered, and this way you can make a nice feedback system that can be used to control the DC motor. Then you can stop the DC motor at the position where you want it to stop. The control of a DC motor using an encoder is not that simple, you just simply can’t start by adding an encoder with the DC Motor and start controlling the DC motor, to use an encoder you will need a controller, the controller will read the encoder and then will accordingly control the DC motor as per the pre-defined instructions written by the programmer. for this project, you will need a microcontroller board like the Arduino Uno or Arduino Nano, or Arduino Mega, or Arduino pro mini, etc. I know beginners are more comfortable with Arduino Uno, Arduino mega, and Arduino Nano, so I will start with the Arduino Uno, the same connections and programs you can also try on Arduino Nano and Arduino Mega.

另一方面,直流电机通电后会立即开始旋转,并持续旋转,除非使用特定技术,否则无法准确控制其位置。 你无法像控制步进电机和伺服电机那样百分之百地控制直流电机,但如果添加一个编码器,就能真正改变整个游戏规则。 有了编码器,你就可以跟踪电机的转数和运行距离,这样你就可以建立一个很好的反馈系统,用来控制直流电机。 然后,你就可以让直流电机停在你希望它停的位置。 使用编码器控制直流电机并不那么简单,你不能一开始就在直流电机上添加一个编码器,然后开始控制直流电机,要使用编码器,你需要一个控制器,控制器将读取编码器,然后根据程序员编写的预定义指令相应地控制直流电机。 我知道初学者更喜欢 Arduino Uno、Arduino mega 和 Arduino Nano,所以我将从 Arduino Uno 开始,同样的连接和程序你也可以在 Arduino Nano 和 Arduino Mega 上尝试。

To get started, you will need Arduino Uno, a Motor driver, a DC Motor, and of course an Encoder. To read the Encoder, we will connect the encoder output pins with Arduino’s pins 2 and 3 which are the interrupt pins. The power wires of the encoder will be connected with the Arduino’s 5V and GND. To keep things simpler, I will start with the simple example code in which I will use pins 2 and 3 as the normal digital pins, we won’t activate the interrupts and then in the second example code, we will use the interrupts. Without any further delay, let’s get started!!!

要开始使用,您需要 Arduino Uno、一个电机驱动器、一个直流电机,当然还有一个编码器。 为了读取编码器,我们将把编码器的输出引脚与 Arduino 的中断引脚 2 和 3 连接起来。 编码器的电源线将与 Arduino 的 5V 和 GND 连接。 为了简化操作,我将从简单的示例代码开始,其中我将把引脚 2 和 3 用作普通的数字引脚,我们不会激活中断,然后在第二个示例代码中,我们将使用中断。 事不宜迟,让我们开始吧

DC Motor with Encoder + Arduino, Circuit Diagram:

The type of the DC motor as you can see in the circuit diagram given below has a built-in encoder. So, it’s totally up to you whether you want to use it as the simple dc motor or you want to use the encoder, or you can use the motor and encoder together at the same time. As you can see in the circuit diagram, the power wires which are the Red and Black wires are not connected but the encoder wires are connected. For this first example, we will only be using the encoder to understand the basics this way you will easily understand how an encoder works. So, we will rotate the motor shaft by hand to see the trigger signals.

从下面的电路图中可以看到,这种直流电机内置了编码器。 因此,你完全可以决定是将其用作简单的直流电机,还是使用编码器,或者同时使用电机和编码器。 如电路图所示,电源线(红线和黑线)没有连接,但编码器线已经连接。 在第一个示例中,我们将只使用编码器来了解基础知识,这样你就会很容易理解编码器的工作原理。 因此,我们将用手旋转电机轴来查看触发信号。
在这里插入图片描述

An encoder works by observing changes to the magnetic field created by a magnet attached to the motor shaft, as the motor rotates the encoder outputs will trigger periodically. When the magnet spins clockwise, output “a” will trigger first, and when rotated counterclockwise on the other hand output “b” will trigger. This way you know exactly which way the motor shaft is rotating. This can be quite handy in situations where you need to control the forward and reverse movement of the DC Motor.

编码器的工作原理是观察连接在电机轴上的磁铁所产生的磁场变化,当电机旋转时,编码器输出将周期性地触发。 当磁铁顺时针旋转时,输出端 "a "将首先触发,反之,当逆时针旋转时,输出端 "b "将触发。 这样,您就能准确知道电机轴的旋转方向。 在需要控制直流电机正反转的情况下,这将非常方便。
Arduino DC Motor Encoder

Let’s write a very simple program to understand how an encoder works and how to read the encoder outputs.
让我们编写一个非常简单的程序,了解编码器的工作原理以及如何读取编码器的输出。

Arduino Encoder Code:

#define ENCA 2 // pin2 of the Arduino
#define ENCB 3 // Pin3 of the Arduino
int ENCA_DATA;
int ENCB_DATA;

void setup() {
  Serial.begin(9600); // Activates Serial communication
  pinMode(ENCA,INPUT); // sets pin2 as the input
  pinMode(ENCB,INPUT); // sets pin3 as the input
}

void loop() {
  ENCA_DATA = digitalRead(ENCA); 
// We simply read Pin2 of the Arduino and store the result in variable ENCA_DATA
  ENCB_DATA = digitalRead(ENCB); 
// We simply read Pin3 of the Arduino and store the result in variable b
  Serial.print(ENCA_DATA*5); 
  Serial.print(" ");
  Serial.print(ENCB_DATA*5);
  Serial.println();
}
Arduino Encoder Code Explanation:

We don’t need any libraries for this basic program. First of all, I started off by defining the Arduino pins 2 and 3. I will call these pins the ENCA and ENCB. These two pins will be connected with the Encoder outputs.
Next, I defined two variables ENCA_DATA and ENCB_DATA.

这个基本程序不需要任何库。 首先,我定义了 Arduino 的 2 号和 3 号引脚。 我将这两个引脚称为 ENCA 和 ENCB。 接下来,我定义了两个变量 ENCA_DATA 和 ENCB_DATA。

#define ENCA 2
#define ENCB 3

int ENCA_DATA;
int ENCB_DATA;

Next, we will need to tell the Arduino whether we want to use the Serial communication? And the pins we just defined are going to be used as the inputs or outputs? All this is done in the void setup() function.

接下来,我们需要告诉 Arduino 是否要使用串行通信? 我们刚刚定义的引脚将用作输入还是输出? 所有这些都将在 void setup() 函数中完成。

void setup() {

Serial.begin(9600);

pinMode(ENCA,INPUT);

pinMode(ENCB,INPUT);

}

In the void setup() function, I simply activated the serial communication and I selected 9600 as the Baud rate. Next, I set the two pins ENCA and ENCB as the inputs using the pinMode() functions.

The void setup() function executes only once with the Arduino board is first turned ON. The actual code is placed inside the loop() function which executes repeatedly.

在 void setup() 函数中,我只需激活串行通信,并选择 9600 作为波特率。 接下来,我使用 pinMode() 函数将两个引脚 ENCA 和 ENCB 设置为输入。 void setup() 函数只在 Arduino 板首次打开时执行一次。 实际代码放在loop()函数中重复执行。

void loop() {

ENCA_DATA = digitalRead(ENCA);

ENCB_DATA = digitalRead(ENCB);

I simply defined a variable “ENCA_DATA” which is of the type integer. Then we read the ENCA pin using the digitalRead() function and store the value in variable ENCA_DATA, exactly the same thing I did for the ENCB.

我们使用 digitalRead() 函数读取 ENCA 引脚,并将值存储到变量 ENCA_DATA,在 ENCB 上所做的完全相同。

Serial.print(ENCA_DATA*5);

Serial.print(” “);

Serial.print(ENCB_DATA*5);

Serial.println();

}

Next, to write outputs to the computer screen I used the Serial.print() and Serial.println() functions. Inside the parenthesis, you can see I am multiplying the values by 5 to make the plot easier to read.

All you need is to compile the code first to check if any keywords are misspelled, and then you can upload the code.

After the code has been uploaded, you can go ahead and open the Serial Monitor and select the 9600 Baud Rate. Now, you can start by rotating the DC Motor shaft which has the encoder. The Encode signal changes as you rotate the motor shaft, these changes are easier to understand with the serial plotter. So, output “a” is triggered when you rotate the shaft Clockwise and similarly, the output “b” is triggered when you rotate the shaft anti-clockwise. This code has nothing to do with the motor shaft position, the purpose of this code was just to help you understand how these two outputs “a” and “b” are triggered.

Now let’s measure the position of the DC motor shaft. Nothing is changed on the hardware side. We are using the same connections.

接下来,为了将输出写入电脑屏幕,我使用了 Serial.print() 和 Serial.println() 函数。 在括号内,你可以看到我将数值乘以 5,以便更容易读取图表。 你只需要先编译代码,检查是否有关键字拼错,然后就可以上传代码了。 代码上传后,你可以继续打开串行监视器,选择 9600 波特率。 现在,你可以开始旋转带有编码器的直流电机轴。 编码器信号会随着电机轴的旋转而变化,使用串行绘图仪更容易理解这些变化。 因此,顺时针旋转电机轴时,输出 "a "被触发;同样,逆时针旋转电机轴时,输出 "b "被触发。 这段代码与电机轴的位置无关,其目的只是帮助你理解这两个输出 "a "和 "b "是如何触发的。 现在我们来测量直流电机轴的位置。 硬件方面没有任何变化。 我们使用的是相同的连接。

Position of the encoder Arduino Code:

#include <Arduino.h>

#define Encoder_output_A 2 // pin2 of the Arduino
#define Encoder_output_B 3 // pin 3 of the Arduino
// these two pins has the hardware interrupts as well.

int Count_pulses = 0;

void DC_Motor_Encoder();
void setup()
{
  Serial.begin(9600);               // activates the serial communication
  pinMode(Encoder_output_A, INPUT); // sets the Encoder_output_A pin as the input
  pinMode(Encoder_output_B, INPUT); // sets the Encoder_output_B pin as the input
  attachInterrupt(digitalPinToInterrupt(Encoder_output_A), DC_Motor_Encoder, RISING);
}

void loop()
{
  Serial.println("Result: ");
  Serial.println(Count_pulses);
}

void DC_Motor_Encoder()
{
  int b = digitalRead(Encoder_output_B);
  if (b > 0)
  {
    Count_pulses++;
  }
  else
  {
    Count_pulses--;
  }
}
Position of the encoder Arduino Code Explanation:

This code is the modified version of the code that I just explained above. I made some changes, which I am sure you can clearly see. Let’s talk about these changes in detail. The pins 2 and 3 connections remain exactly the same.

这段代码是我上面解释过的代码的修改版。 做了一些改动,让我们来详细谈谈这些改动。 引脚 2 和引脚 3 的连接方式保持不变。

#define Encoder_output_A 2 // pin2 of the Arduino
#define Encoder_output_B 3 // pin 3 of the Arduino
// these two pins has the hardware interrupts as well.

I defined a global variable Count_pulses and initially stored a value of 0 in it. As this is a global variable so I can access this variable from anywhere inside my Arduino code.

我定义了一个全局变量 Count_pulses,初始值为 0。 由于这是一个全局变量,因此我可以从 Arduino 代码的任何地方访问该变量。
int Count_pulses = 0;

Inside the void setup() function, you can clear see the first three lines of code are exactly the same.

void setup() {
Serial.begin(9600); // activates the serial communication
pinMode(Encoder_output_A,INPUT); // sets the Encoder_output_A pin as the input
pinMode(Encoder_output_B,INPUT); // sets the Encoder_output_B pin as the input
attachInterrupt(digitalPinToInterrupt(Encoder_output_A),DC_Motor_Encoder,RISING);
}

This time I added this attachInterrupt(digitalPinToInterrupt(Encoder_output_A),DC_Motor_Encoder,RISING); this line of code. The attachinterrupt() function is used to activate the hardware interrupt. The attachinterrupt() function takes three arguments as the input. The first one is the pin digitalPinToInterrupt(Encoder_output_A), the second argument is the function name, this function will execute each time when an interrupt happens on the Encoder_output_A pin of the Arduino which is pin 2. The 3rd argument is to tell whether to take action on the rising edge or the falling edge. So, our interrupt setting is completed.

This time inside the loop function many things are changed. This time inside the loop() function we are only using 2 lines of code, the first line of code prints the text Result and the “Serial.println(Count_pulses);” which prints the value stored in the variable Count_pulses.

这次我添加了attachInterrupt(digitalPinToInterrupt(Encoder_output_A),DC_Motor_Encoder,RISING);这行代码。 attachinterrupt() 函数用于激活硬件中断。 attachinterrupt() 函数将三个参数作为输入。 第一个参数是引脚 digitalPinToInterrupt(Encoder_output_A),第二个参数是函数名称,每次当 Arduino 的Encoder_output_A引脚(即引脚 2)发生中断时,该函数就会执行。 第三个参数是告诉我们是在上升沿还是下降沿采取行动。 这样,我们的中断设置就完成了。我们在 loop() 函数中只使用了两行代码,第一行代码打印文本 Result 和 “Serial.println(Count_pulses);”,后者打印存储在变量 Count_pulses 中的值。

void loop() {
Serial.println(“Result:);
Serial.println(Count_pulses);
}

DC_Motor_Encoder() function is a user-defined function it has no return type and does not take any arguments as the input. This is the function that is executed when an interrupt happens on pin2 of the Arduino. So, inside this function we are simply reading the Encoder_output_B pin, the value is stored in variable b. Next, we use an if condition to check if a signal is detected then increment the Count_pulses by 1 or else decrement the Count_pulses.

DC_Motor_Encoder() 函数是一个用户自定义函数,它没有返回类型,也不将任何参数作为输入。 当 Arduino 的引脚 2 发生中断时,该函数将被执行。 因此,在该函数中,我们只需读取 Encoder_output_B 引脚的值,并将其存储在变量 b 中。接下来,我们使用 if 条件来检查是否检测到信号,然后将 Count_pulses 递增 或递减 。

void DC_Motor_Encoder(){
int b = digitalRead(Encoder_output_B);
if(b > 0){
Count_pulses++;
}
else{
Count_pulses–;
}
}

Upload the code, open the serial monitor, and start rotating the encoder. Rotate the motor shaft in the clockwise direction and also in the anti-clockwise direction. In one direction the value will increase and in the other direction the value will decrease.
上传代码,打开串行监视器,开始旋转编码器。 顺时针和逆时针旋转电机轴。 在一个方向上,数值会增加,在另一个方向上,数值会减少。

Driving the Motor with Encoder and Arduino:

Now, I am sure you have completely understood how an Encoder works, how the Encoder outputs are triggered, and then how to write a simple code to count the pulses. So far we were manually rotating the DC motor shaft to trigger the Encoder outputs, now to do everything automatically it’s time to connect a motor driver so that we can read the position measurements from the Encoder. Start by connecting the DC motor wires to the outputs of the motor driver circuit. The motor driver also needs an appropriate power supply, which you will need to select as per your DC motor specs. Next, connect the Motor driver ground to the Arduino ground, the PWM input of the motor driver should be connected to an Arduino PWM pin here I have used pin 5.

现在,我相信你已经完全了解了编码器的工作原理、编码器输出的触发方式以及如何编写简单的代码来计数脉冲。 到目前为止,我们都是通过手动旋转直流电机轴来触发编码器输出,现在要想自动完成所有操作,就需要连接电机驱动器,以便读取编码器的位置测量值。 首先将直流电机导线连接到电机驱动器电路的输出端。 电机驱动器还需要适当的电源,您需要根据直流电机的规格来选择。 接下来,将电机驱动器的地线连接到 Arduino 的地线上,电机驱动器的 PWM 输入应连接到 Arduino 的 PWM 引脚上,这里我使用的是第 5 引脚。

在这里插入图片描述

The other two motor driver pins can be connected to any of the remaining Arduino digital pins. Before writing the control algorithm, Let’s test the motor driver start by defining the pins that you connected to the motor driver.

另外两个电机驱动器引脚可以连接到 Arduino 其余的任何数字引脚上。 在编写控制算法之前,让我们先测试一下电机驱动器,定义连接到电机驱动器的引脚。

Driving the Motor with Encoder, Arduino Code:

#include <Arduino.h>

#define ENCA 2
#define ENCB 3
#define PWM 5
#define IN2 6
#define IN1 7

int pos = 0;

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2);
// void setMotor(int, int, int, int, int);
void readEncoder();

void setup()
{
  Serial.begin(9600);
  pinMode(ENCA, INPUT);
  pinMode(ENCB, INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA), readEncoder, RISING);
}

void loop()
{
  setMotor(1, 25, PWM, IN1, IN2);
  delay(200);
  Serial.println(pos);
  setMotor(-1, 25, PWM, IN1, IN2);
  delay(200);
  Serial.println(pos);
  setMotor(0, 25, PWM, IN1, IN2);
  delay(20);
  Serial.println(pos);
}

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2)
{
  analogWrite(pwm, pwmVal);
  if (dir == 1)
  {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  }
  else if (dir == -1)
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  }
  else
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
}

void readEncoder()
{
  int b = digitalRead(ENCB);
  if (b > 0)
  {
    pos++;
  }
  else
  {
    pos--;
  }
}

It is useful to define a function that will set the motor direction and speed. The interface for the setMotor function, I have written here sets the direction and speed of a motor with the pins defined in the last three inputs. Inside the function, I have set the speed with an analog write command. if the direction integer is 1 then the motor will rotate one way by writing a high low combination to the input pins of the driver. If you reverse the order to a low-high combination the motor will rotate in the other direction. Inside the loop function, you can call the setMotor function to drive the motor also write the position to the serial line.

定义一个可以设置电机方向和速度的函数非常有用。 我在这里编写的 setMotor 函数接口使用最后三个输入中定义的引脚来设置电机的方向和速度。 如果方向整数为 1,那么通过向驱动器的输入引脚写入高-低组合,电机将单向旋转。 如果将顺序颠倒为低-高组合,电机将向另一个方向旋转。 在循环函数中,可以调用 setMotor 函数来驱动电机,同时将位置写入串行线。

Control DC motor using Encoder feedback loop:

So far we have connected the controller motor driver and motor in a loop but we have not used the position signal from the encoder to control the motor position. We will use a feedback loop. In a feedback loop the control components are often referred to as the plant(motor) here that is the motor and the motor driver. The sensor(encoder) that we are using to measure position is the encoder. In order to actually control the position of the motor you need to provide it with a target position, then you take the difference between the target position and the measured position, the result is the error usually written as e(t).

到目前为止,我们已将控制器电机驱动器和电机连接成一个环路,但还没有使用编码器的位置信号来控制电机位置。 我们将使用反馈回路。 在反馈回路中,控制部件通常被称为设备(电机),这里指的是电机和电机驱动器。 我们用来测量位置的传感器(编码器)就是编码器。 为了实际控制电机的位置,需要为其提供一个目标位置,然后求出目标位置与测量位置之间的差值,其结果就是误差,通常写为 e(t)。
Arduino DC Motor Encoder

Now that the error has been computed you can use a controller to compute a control signal that is sent to the plant(motor). The control signal is configured so that it will attempt to reduce the error. The control signal is typically written as u(t).

In this project, we will use the PID control algorithm to generate the control signal u(t) the PID control signal is constructed using a sum of three terms a proportional, derivative, and integral term that is what PID stands for. The proportional term is the most important as it is directly responsible for reducing the error, the derivative and integral terms are typically used to smooth out the control system response. The three constants kp, ki and kd determine how strongly each term is represented in the control loop; you can adjust these constants to tune your response. You can estimate the integral and derivative of the error using the simple finite difference approximation. The integral term accumulates the error over time and the derivative computes how quickly the error is changing with the feedback control loop. Complete, you’re ready to write code to control the position of the motor.

既然误差已经计算出来,那么就可以使用控制器来计算发送给设备(电机)的控制信号。 控制信号经过配置后,来减小误差。 在本项目中,我们将使用 PID 控制算法来生成控制信号 u(t)。PID 控制信号由比例项、导数项和积分项三个项的总和构成,这就是 PID 的含义。 比例项最为重要,因为它直接负责减少误差,而导数和积分项通常用于平滑控制系统的响应。 三个常数 kp、ki 和 kd 决定了每个项在控制回路中的体现程度;您可以调整这些常数来调整响应。 您可以使用简单的有限差分近似估算误差的积分和导数。 积分项计算误差随时间的累积,导数项计算误差随反馈控制回路变化的速度。 完成后,您就可以编写代码来控制电机的位置了。

DC Motor control with Encoder Feedback, Arduino Code:

#include <Arduino.h>

#define ENCA 2
#define ENCB 3
#define PWM 5
#define IN2 6
#define IN1 7

int pos = 0;
long prevT = 0;
float eprev = 0;
float eintegral = 0;

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2);
void readEncoder();

void setup()
{
  Serial.begin(9600);
  pinMode(ENCA, INPUT);
  pinMode(ENCB, INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA), readEncoder, RISING);
  Serial.println("target pos");
}

void loop()
{

  // set target position
  int target = 1200;
  // target = 250*sin(prevT/1e6);

  // PID constants
  float kp = 1;
  float kd = 0.025;
  float ki = 0.0;

  // time difference
  long currT = micros();
  float deltaT = ((float)(currT - prevT)) / (1.0e6);
  prevT = currT;

  // error
  int e = pos - target;

  // derivative
  float dedt = (e - eprev) / (deltaT);

  // integral
  eintegral = eintegral + e * deltaT;

  // control signal
  float u = kp * e + kd * dedt + ki * eintegral;

  // motor power
  float pwr = fabs(u);
  if (pwr > 255)
  {
    pwr = 255;
  }

  // motor direction
  int dir = 1;
  if (u < 0)
  {
    dir = -1;
  }

  // signal the motor
  setMotor(dir, pwr, PWM, IN1, IN2);

  // store previous error
  eprev = e;

  Serial.print(target);
  Serial.print(" ");
  Serial.print(pos);
  Serial.println();
}

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2)
{
  analogWrite(pwm, pwmVal);
  if (dir == 1)
  {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
  }
  else if (dir == -1)
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
  }
  else
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
  }
}

void readEncoder()
{
  int b = digitalRead(ENCB);
  if (b > 0)
  {
    pos++;
  }
  else
  {
    pos--;
  }
}

Start by defining global storage variables that can be used to save values between time steps, these are used in the finite difference estimates for the integral and derivative. The first thing that you need to do in the loop function is set a target for the control loop; the control signal will be adjusted over time as the measured position becomes closer to the target. Next, define the constants used in the PID control algorithm start by setting kp to 1 and kd and ki to 0. you can come back and adjust these later to compute the finite difference approximations. We need to compute the time difference t start by recording the current time in microseconds using the micros function then compute t in seconds by taking the difference between the current time and the previous time, be careful that you are performing floating-point arithmetic not integer arithmetic; complete the calculation by storing the current time in the previous time variable for use.

首先要定义全局存储变量,用于保存周期内的值,这些值将用于积分和导数的有限差分估算。 在loop()函数中需要做的第一件事是为控制环路设置一个目标值;当测量位置变得更接近目标值时,控制信号将随时间而调整。 接下来,定义 PID 控制算法中使用的常数,将 kp 设为 1,kd 和 ki 设为 0。 我们需要计算时间差 t,首先使用 micros() 函数记录当前时间(以微秒为单位),然后计算当前时间与前一时间的差值(以秒为单位),注意是浮点运算而不是整数运算;完成计算后,将当前时间存储到前一时间变量中备用。
In the next iteration of the loop, the error is computed as the difference between the target and measured positions here I have reversed the order because of the way that I wired the motor leads if you find that your control algorithm is not working you can try switching the sign of the error term as I did now compute the derivative and integral of the arrow signal using the finite difference approximations with all that work done you are finally ready to compute your control signal. it is surprisingly simple is not it this signal will tell the plant the direction and speed to turn the motor to send the signal to the motor. We need to convert it into a speed and direction start by computing the PWM signal as the floating-point absolute value of the control signal U.

在循环的下一次迭代中,误差被计算为目标位置和测量位置之间的差值。在这里,由于电机引线的接线方式不同,我将顺序颠倒了一下,如果您发现控制算法不起作用,可以尝试像我一样转换误差项的符号。 这个信号将告诉工厂电机的旋转方向和速度,并将信号发送给电机。 我们需要通过计算 PWM 信号作为控制信号 U 的浮点绝对值,将其转换为速度和方向起始信号。

You also need to cap the PWM signal at 255 as that is the maximum value we can write next to determine the direction by computing the sign of the control signal u with the motor speed and direction computed from the control signal call the setMotor function to write to the motor driver to complete the loop function store the previous value of the error also print your target and measure positions to the serial com, so you can test how well your control algorithm is performing with these parameters. I am seeing a little overshoot after reaching the target. In other words, the motor spins too far and has to reverse directions to achieve the target position one way to reduce overshoot is to increase the derivative term here I have set kd equal to 0.025 this is enough to completely remove the overshoot for this system. Once your system works to achieve a constant target try setting a target that changes with time here I have set a sinusoidal target depending on your target and loading conditions you will need to further tune your PID parameters.

此外,您还需要将 PWM 信号的上限设定为 255,因为这是我们可以写入的最大值。接下来,通过计算控制信号 u 的符号以及从控制信号中计算出的电机速度和方向来确定方向,调用 setMotor 函数写入电机驱动器,以完成循环功能,存储之前的误差值,并将目标位置和测量位置打印到串行通信端口,这样您就可以测试您的控制算法在使用这些参数时的性能如何。 在达到目标位置后,我看到了一点过冲。 换句话说,电机转得太快,必须反转方向才能达到目标位置。减少过冲的方法之一是增加导数项,在这里我将 kd 设为 0.025,这足以完全消除该系统的过冲。 一旦您的系统能够实现恒定目标,请尝试设置一个随时间变化的目标,在这里我设置了一个正弦目标,具体取决于您的目标和负载条件,您需要进一步调整 PID 参数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2034682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文读懂Xinstall专属链接推广,轻松解决App运营痛点!

随着互联网的飞速发展&#xff0c;App推广和运营面临着前所未有的挑战。传统的营销方式已经难以适应多变的市场环境&#xff0c;而Xinstall专属链接推广应运而生&#xff0c;成为解决App获客难题的新利器。本文将深入探讨Xinstall专属链接推广如何帮助推广者触达更多用户&#…

MacOS vue-cli为2.9.6 无法升级的解决方案

背景 今天需要验证plop工具做前端工程化实践&#xff0c;打算使用vue3方式&#xff0c;结果发现vue-cli 2.9.6一直无法升级成功&#xff0c;也无法通过vue-cli生成vue3模板工程&#xff0c;测试了几把后&#xff0c;最终升级vue-cli成功&#xff0c;为了能给出现同样问题的小伙…

上瘾模型与产品激励系统

​产品要增加客户粘性&#xff0c;使产品深入人心就需要让用户对产品上瘾。如何使用户对产品上瘾&#xff1f;对于产品来说&#xff0c;就需要建立产品的激励系统。 产品的激励系统要做的事就是对用户进行激励&#xff0c;就是让用户主动完成产品或服务想要他们做的事情。 那么…

重启人生计划-勇敢者先行

&#x1f973;&#x1f973;&#x1f973; 茫茫人海千千万万&#xff0c;感谢这一刻你看到了我的文章&#xff0c;感谢观赏&#xff0c;大家好呀&#xff0c;我是最爱吃鱼罐头&#xff0c;大家可以叫鱼罐头呦~&#x1f973;&#x1f973;&#x1f973; 如果你觉得这个【重启人生…

分布式与微服务详解

1. 单机架构 只有一台机器&#xff0c;这个机器负责所有的工作 &#xff08;这里假定一个电商网站&#xff09; 现在大部分公司的产品都是单机架构 。 2. 分布式架构 一台机器的硬件资源是有限的&#xff0c;服务器处理请求是需要占用硬件资源的&#xff0c;如果业务增长&a…

前端学习笔记-JS篇-01

JS基础Day1-01-必看-基本软件以及准备工作_哔哩哔哩_bilibili JavaScript介绍 是什么 1.JavaScript (是什么?) 是一种运行在客户端(浏览器)的编程语言&#xff0c;实现人机交互效果2.作用(做什么?) 网页特效(监听用户的一些行为让网页作出对应的反馈)表单验证(针对表单…

streampark-使用记录-备忘

1、重新部署的任务会读历史配置&#xff08;包括错误配置&#xff09;&#xff0c;即使点击确认了也无效 解决&#xff1a;复制新的任务&#xff0c;修改ckeckpoint 路径&#xff08;重要&#xff09; 2、任务启动报错&#xff0c;即使后续把脚本改正确或者复制其他脚本过来执…

什么是 Java?

探索 Java&#xff0c;一种多功能且功能强大的编程语言。释放其构建强大应用程序的潜力。 前言 简单来说&#xff0c;Java 是一种用于开发软件应用程序的面向对象设计的编程语言。截至 2019 年&#xff0c;它是世界上最受欢迎的编程语言&#xff0c;尤其是因为它是开源的&#…

MySQL 的 InnoDB 缓冲池里有什么?--InnoDB存储梳理(二)

文章目录 缓冲池的配置介绍一张表 INNODB_BUFFER_POOL_PAGES字段解释 缓冲池的配置 以下配置的意思&#xff0c;缓冲池在内存中的大小为20M&#xff1b;只有1个缓冲池实例&#xff1b;每一块的大小&#xff0c;插入缓冲占的百分比 # InnoDB 缓存池配置 innodb_buffer_pool_si…

Spring Boot 3.x Web单元测试最佳实践

上一篇&#xff1a;Spring Boot 3.x Rest API统一异常处理最佳实践 下一篇&#xff1a;Spring Boot 3.x Filter实战&#xff1a;记录请求日志 Spring Boot为我们提供了非常便捷的web层Rest API单元测试的API&#xff0c;这种开发能力也是小伙伴必须要掌握的。如何对数据库、中…

【简历】扬州某一本大学:前端秋招简历指导,面试通过率低

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 简历说明 这是25届一本前端同学的简历。这是一个老牌一本学校&#xff0c;老牌一本定位求职层次&#xff0c;可以从传统的中厂上升到大厂。学历可以…

Pytorch离线文件的快速下载

一、为什么要使用离线方式安装Pytorch 参考我的博客《直接用文件方式安装Cuda版本的Pytorch》可以方便的安装Cuda版本的Pytorch&#xff0c;比较方便快捷。系统重装后&#xff0c;可以快速的重新搭建系统。 二、如何直接下载Pytorch的离线安装文件whl 可以参考这个博客&#…

基于SpringBoot的桂林二手房交易系统的设计与实现---附源码17680

目录 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4 系统流程分析 2.4.1系统开发流程 2.4.2 用户登录流程 2.4.3 系统操作流程 2.4…

java数字产科管理系统源码,产科业务信息系统源码,产科电子病历系统源码,前端框架:Vue、ElementUI 数 据 库:MySQL8.0.36

数字产科管理系统源码&#xff0c;产科业务信息系统源码&#xff0c;产科电子病历系统源码 数字产科管理系统是一套针对孕产妇的基于流程管控的产科业务信息系统。该系统由门诊系统、住院系统、数据统计模块三部分组成。实现孕产妇围产期一待产一住院的持续化、专业化、电子化…

高性能并行计算面试-核心概念-问题理解

目录 1.什么是并行计算&#xff1f;高性能从哪些方面体现&#xff1f; 2.CPU常见的并行技术 3.GPU并行 4.并发与并行 5.常见的并行计算模型 6.如何评估并行程序的性能&#xff1f; 7.描述Am达尔定律和Gustafson定律&#xff0c;并解释它们对并行计算性能的影响 8.并行计…

python使用 数值微分法 求梯度,实现单层线性回归

文章目录 模型构建数据数值微分实现&#xff08;梯度计算&#xff09;模型封装运行测试运行结果 主要介绍 数值微分法 求梯度&#xff0c;以及基于此对参数作随机梯度下降&#xff0c;并封装一个简单的线性回归模型以作调试&#xff0c;最后绘制loss图像。 模型 y X W b y X…

Java:接口interface

文章目录 接口interface好处为什么要用接口 接口案例需求思路代码Student.javaClassManage.javaStudentOperator 接口StudentOperatorImpl1.javaStudentOperatorImpl2.javaTest.java 黑马程序员学习笔记 接口interface 接口中&#xff1a;变量默认为常量&#xff0c;方法默认为…

上门预约o2o系统源码开发及商业模式探索

随着互联网的飞速发展&#xff0c;O2O&#xff08;Online to Offline&#xff09;模式已成为连接线上与线下服务的重要桥梁。上门预约O2O系统作为这一模式下的典型应用&#xff0c;通过整合线上线下资源&#xff0c;为用户提供便捷、高效、个性化的上门服务体验。本文将从商业模…

WebStorm 2024 for Mac/Win:JavaScript开发的高效利器

WebStorm 2024 for Mac/Win是一款专为前端开发者和全栈工程师设计的集成开发环境&#xff08;IDE&#xff09;&#xff0c;由JetBrains公司精心打造。这款软件以其强大的功能和卓越的性能&#xff0c;在JavaScript及相关技术的开发领域中脱颖而出&#xff0c;成为众多开发者的首…

无人机电池的使用寿命!

无人机电池的循环寿命一般在100次到500次之间&#xff0c;具体取决于电池类型、质量和使用条件。高品质电池和正确的使用方式可以延长电池的循环寿命。 避免极端温度 避免在过高或过低的温度下使用无人机电池&#xff0c;以免影响电池性能和寿命。 正确存储 将电池存放在干燥…