Arduino UNO R3 +TB6612驱动电机转动、调试PID并实现电机测速

目录

一、硬件材料

二、软件材料

三、驱动电机转动

3.1 编码器电机接口的连接

3.2 Arduino与TB6612的控制信号输入引脚连接

3.3 电机驱动逻辑实现

四、实现电机的脉冲计数

4.1 Arduino 与TB6612的电机编码器输出引脚的连接

4.2 脉冲计数原理

五、实现电机的测速

六、PID调速

6.1 PID介绍

6.2 下载PID库

6.3 PID调速


一、硬件材料

1、Arduino UNO R3 

图1 Arduino UNO R3 

2、电机驱动板——TB6612

图2 TB6612

3、MC520编码器电机x2

图3 MC520编码器电机

4、电池

图4 电池

二、软件材料

1、Arduino IDE

官方下载链接:https://www.arduino.cc/en/Main/Software

图5 Arduino IDE

启动并配置 Arduino IDE:

图6 启动配置界面

三、驱动电机转动

通过Arduino实现电机转动,由于Arduino的输出电流不足以直接驱动电机,因此,需要通过电机驱动板(作者选用TB6612)进行放大电机控制信号。

3.1 编码器电机接口的连接

MOTOR_A连接右电机,MOTOR_B连接左电机,如图7-8所示。

图7 编码器电机与驱动板连接

图8 编码器电机与驱动板连接

3.2 Arduino与TB6612的控制信号输入引脚连接

Arduino与TB6612的引脚连接方式如图9-10所示。

Arduino UNO R3 : 13         12           11           10           9          8           7

TB6612驱动板:  AIN1      STBY     PWMA     PWMB    BIN1    BIN2     AIN2     

注意:

①A表示右电机,B表示左电机。

②Arduino UNO R3只有引脚3、5、6、9、10、11具有PWM输出功能。

图9 Arduino与TB6612的引脚连接

图10 Arduino与TB6612的引脚连接

3.3 电机驱动逻辑实现

  TB6612真值表如图11所示,由于A控制右电机,B控制左电机,因此想要实现小车的前进,AIN1(电机A的A相)和BIN1(电机B的A相)同时为高电压,而AIN2(电机A的B相)和BIN2(电机B的B相)同时为低电压,可实现左右电机的正转,即小车的前进。

图11 TB6612真值表

左右电机同时正转,小车前进程序如下:

图12 左右电机同时前进

先定义引脚关系,再在setup()中通过pinMode()函数设置控制信号引脚为输出(即为写:上位机写入至下位机(从上位机输出至下位机)),最后在loop中通过digitalWrite()给相应的引脚写入高低电平。

//右电机的引脚连接情况:DIR_A1 = 13控制反转、DIR_A2 =7控制正转
int DIR_A1 = 13;
int DIR_A2 = 7;
//左电机的引脚连接情况:DIR_B1 = 9控制反转、DIR_B2 = 8控制正转
int DIR_B1 = 9;
int DIR_B2 = 8;

void setup() {
  // put your setup code here, to run once:
  // 设置控制信号引脚为输出
  pinMode(DIR_A1,OUTPUT);
  pinMode(DIR_A2,OUTPUT);   
  pinMode(DIR_B1,OUTPUT);
  pinMode(DIR_B2,OUTPUT);   
}
void loop() {
  // put your main code here, to run repeatedly:
  //右电机正转
  digitalWrite(DIR_A1,HIGH);
  digitalWrite(DIR_A2,LOW);
  //左电机正转
  digitalWrite(DIR_B1,HIGH);
  digitalWrite(DIR_B2,LOW);
}

同理,通过digitalWrite()将AIN1和BIN1同时设置为低电压,而AIN2和BIN2同时为高电压,可实现左右电机的反转,即小车的后退。

将AIN1设置为高电压,AIN2设置为低电压,使得右电机的正转;BIN1设置为低电压,BIN2设置为高电压,可实现左电机的反转,即小车的左转。

将AIN1设置为低电压,AIN2设置为高电压,使得右电机的反转;BIN1设置为高电压,BIN2设置为低电压,可实现左电机的反转,即小车的右转。

通过analogWrite() 函数调节PWM值(占空比)可控制小车不同的转速,并加入delay()延时函数,实现持续的状态。

以下程序为小车慢速正转1s后,反转1s,如此循环的实现:


//右电机的引脚连接情况:DIR_A1 = 13控制反转、DIR_A2 =7控制正转
int DIR_A1 = 13;
int DIR_A2 = 7;
int PWM_A = 11;
//左电机的引脚连接情况:DIR_B1 = 9控制反转、DIR_B2 = 8控制正转
int DIR_B1 = 9;
int DIR_B2 = 8;
int PWM_B = 10;


void setup() {
  // put your setup code here, to run once:
  // 设置控制信号引脚为输入
  pinMode(DIR_A1,OUTPUT);
  pinMode(DIR_A2,OUTPUT);   
  pinMode(PWM_A,OUTPUT);
  pinMode(DIR_B1,OUTPUT);
  pinMode(DIR_B2,OUTPUT);   
  pinMode(PWM_B,OUTPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  //右电机正转
  digitalWrite(DIR_A1,HIGH);
  digitalWrite(DIR_A2,LOW);
  analogWrite(PWM_A,25);
  //左电机正转
  digitalWrite(DIR_B1,HIGH);
  digitalWrite(DIR_B2,LOW);
  analogWrite(PWM_B,25);
  delay(1000);
  //右电机反转
  digitalWrite(DIR_A1,LOW);
  digitalWrite(DIR_A2,HIGH);
  analogWrite(PWM_A,25);
  //左电机反转
  digitalWrite(DIR_B1,LOW);
  digitalWrite(DIR_B2,HIGH);
  analogWrite(PWM_B,25);
  delay(1000);
}

四、实现电机的脉冲计数

4.1 Arduino 与TB6612的电机编码器输出引脚的连接

要实现输出电机的脉冲计数,需要知道Arduino 与TB6612的电机编码器输出引脚的对应关系。

并且在编码器数据输出中需要应用中断函数,因此需要知道中断引脚。

以下为作者设置的编码器输出引脚对应关系:

Arduino UNO R3 : 2         4          3          5       

TB6612驱动板:   E1A     E1B     E2A      E2B    

注意:

①Arduino UNO R3只有引脚2、3为中断引脚。

② E1A表示右电机的A相编码器,E1B表示右电机的B相编码器;E2A表示左电机的A相编码器,E2B表示左电机的B相编码器;

图13 编码器输出引脚对应关系

图14 AB相传感器

4.2 脉冲计数原理

作者使用的此编码器电机为增量式编码器,将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。

该编码器分为A相与B相,也称为AB相编码器。每一相每转过单位的角度就发出一个脉冲信号(一圈可以发出N个脉冲信号),A相、B相为相互延迟1/4周期的脉冲输出,根据延迟关系可以区别正反转,而且通过取A相、B相的上升和下降沿可以进行单频或2倍频或4倍频测速,如图15所示。

图15 正反转时AB相电平对应的状态

统计方波/脉冲个数:
①单倍频计数:一个脉冲统计一次
②双倍频计数:一个脉冲统计两次
③四倍频计数:两个脉冲统计四次

即:单倍、双倍频计数都是只计A相的脉冲,所以是一个脉冲。并且如果只是统计一次电平的突变则为单倍频计数,统计两次次电平的突变则为双倍频计数。而四倍频计数是计了A、B相的脉冲,为两个脉冲,并分别统计了两个相的两次突变。

用计数器count来计数电平的突变,当是正转时,count++,而反转时,count--,并且由A相电平突变时对应B相此时的电平来判断正反转。

脉冲计数实现逻辑思路:

定义引脚关系,并初始化两电机的计数变量,设置串口通信的波特率,在setup()中通过pinMode()函数将编码器输出引脚设置为输入模式(即为读:上位机从下位机读取(从下位机输入至上位机)),并设置中断函数attachInterrupt(),编写中断函数,这是关键!最后在loop()中打印输出计数。

在中断函数中,需要做出判断,当电机编码器A相的电平等于B相的电平时,count++,计数加一,即为正转;否则,count--,计数减一,即为反转。详见以下程序。

补充中断函数attachInterrupt()语法:
attachInterrupt(interrupt, function, mode)

interrupt:中断引脚数

function:中断发生时调用的函数,此函数必须不带参数和不返回任何值。该函数有时被称为中断服务程序。

mode:定义何时发生中断以下四个contstants预定有效值:

  • LOW 当引脚为低电平时,触发中断

  • CHANGE 当引脚电平发生改变时,触发中断

  • RISING 当引脚由低电平变为高电平时,触发中断

  • FALLING 当引脚由高电平变为低电平时,触发中断.

#define E1A 2  // 电机 A 编码器 A 相
#define E1B 4  // 电机 A 编码器 B 相
#define E2A 3  // 电机 B 编码器 A 相
#define E2B 5  // 电机 B 编码器 B 相

volatile int countA = 0; // 电机 A 的脉冲计数
volatile int countB = 0; // 电机 B 的脉冲计数

//右电机的引脚连接情况:DIR_A1 = 13控制反转、DIR_A2 =7控制正转
int DIR_A1 = 13;
int DIR_A2 = 7;
int PWM_A = 11;
//左电机的引脚连接情况:DIR_B1 = 9控制反转、DIR_B2 = 8控制正转
int DIR_B1 = 9;
int DIR_B2 = 8;
int PWM_B = 10;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  // 设置编码器引脚为输入
  pinMode(E1A, INPUT);
  pinMode(E1B, INPUT);
  pinMode(E2A, INPUT);
  pinMode(E2B, INPUT);
  // 设置控制信号引脚为输入
  pinMode(DIR_A1,OUTPUT);
  pinMode(DIR_A2,OUTPUT);   
  pinMode(PWM_A,OUTPUT);
  pinMode(DIR_B1,OUTPUT);
  pinMode(DIR_B2,OUTPUT);   
  pinMode(PWM_B,OUTPUT);
  
// 设置中断 
  attachInterrupt(digitalPinToInterrupt(E1A), handleMotorA, RISING);
  attachInterrupt(digitalPinToInterrupt(E2A), handleMotorB, RISING);
}

void loop() {
  // put your main code here, to run repeatedly:
  // 打印编码器计数
  Serial.print("Motor A Count: ");
  Serial.println(countA);
  Serial.print("Motor B Count: ");
  Serial.println(countB);
//  delay(1000); // 每秒打印一次
  
  //右电机正转
  digitalWrite(DIR_A1,HIGH);
  digitalWrite(DIR_A2,LOW);
  analogWrite(PWM_A,25);
  //左电机正转
  digitalWrite(DIR_B1,HIGH);
  digitalWrite(DIR_B2,LOW);
  analogWrite(PWM_B,25);
  delay(1000);
  //右电机反转
  digitalWrite(DIR_A1,LOW);
  digitalWrite(DIR_A2,HIGH);
  analogWrite(PWM_A,25);
  //左电机反转
  digitalWrite(DIR_B1,LOW);
  digitalWrite(DIR_B2,HIGH);
  analogWrite(PWM_B,25);
  delay(1000);
  
}

// 中断服务程序:处理电机 A 的编码器信号
void handleMotorA() {
  if (digitalRead(E1A) == digitalRead(E1B)) {
      countA++; // 正转          
    } else {
      countA--;
    }
}
// 中断服务程序:处理电机 B 的编码器信号
void handleMotorB() {
  if (digitalRead(E2A) == digitalRead(E2B)) {
      countB--; // 正转      
    } else {
      countB++;
    }
}

打开串口监视器,设置波特率,即可观察到输出的脉冲计数(还是有点偏差的):

图16 脉冲计数-串口监视器

五、实现电机的测速

有了脉冲计数的基础后,只需要将脉冲计数转换成为速度即可实现测速。

主要关注中断的实现。

转速逻辑思路:

需要一个开始时间(用于记录每个测速周期的开始时刻),还需要定义一个时间区间(比如50毫秒),实时的获取当前时刻,当当前时刻 - 上传结束时刻 >= 时间区间时,就获取当前计数并根据测速公式计算时时速度,计算完毕,计数器归零,重置开始时间。

当使用中断函数中的变量时,需要先禁止中断noInterrupts(),调用完毕,再重启中断interrupts()


#define E1A 2  // 电机 A 编码器 A 相
#define E1B 4  // 电机 A 编码器 B 相
#define E2A 3  // 电机 B 编码器 A 相
#define E2B 5  // 电机 B 编码器 B 相

volatile int countA = 0; // 电机 A 的脉冲计数
volatile int countB = 0; // 电机 B 的脉冲计数

//右电机的引脚连接情况:DIR_A1 = 13控制反转、DIR_A2 =7控制正转
int DIR_A1 = 13;
int DIR_A2 = 7;
int PWM_A = 11;
//左电机的引脚连接情况:DIR_B1 = 9控制反转、DIR_B2 = 8控制正转
int DIR_B1 = 9;
int DIR_B2 = 8;
int PWM_B = 10;

int reducation = 90;//减速比,根据电机参数设置,比如 15 | 30 | 60
int pulse = 11; //编码器旋转一圈产生的脉冲数该值需要参考商家电机参数
int per_round = pulse * reducation * 4;//一圈输出的脉冲数×减速比×4倍频
long start_time = millis();//一个计算周期的开始时刻,初始值为 millis(); 获取开始的时间
long interval_time = 50;//一个计算周期 50ms //单位时间
double current_velA;
double current_velB;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  // 设置编码器引脚为输入
  pinMode(E1A, INPUT);
  pinMode(E1B, INPUT);
  pinMode(E2A, INPUT);
  pinMode(E2B, INPUT);
  // 设置控制信号引脚为输入
  pinMode(DIR_A1,OUTPUT);
  pinMode(DIR_A2,OUTPUT);   
  pinMode(PWM_A,OUTPUT);
  pinMode(DIR_B1,OUTPUT);
  pinMode(DIR_B2,OUTPUT);   
  pinMode(PWM_B,OUTPUT);
  
// 设置中断
  attachInterrupt(digitalPinToInterrupt(E1A), handleMotorA, RISING);
  attachInterrupt(digitalPinToInterrupt(E2A), handleMotorB, RISING);
}

void loop() {
  // put your main code here, to run repeatedly:
  // 打印编码器计数
  Serial.print("Motor A Count: ");
  Serial.println(countA);
  Serial.print("Motor B Count: ");
  Serial.println(countB);
//  delay(1000); // 每秒打印一次
   //打印转速
//  get_current_vel();
  
  //右电机正转
  digitalWrite(DIR_A1,HIGH);
  digitalWrite(DIR_A2,LOW);
  analogWrite(PWM_A,50);
  //左电机正转
  digitalWrite(DIR_B1,HIGH);
  digitalWrite(DIR_B2,LOW);
  analogWrite(PWM_B,50);
  delay(1000);
  //右电机反转
  digitalWrite(DIR_A1,LOW);
  digitalWrite(DIR_A2,HIGH);
  analogWrite(PWM_A,50);
  //左电机反转
  digitalWrite(DIR_B1,LOW);
  digitalWrite(DIR_B2,HIGH);
  analogWrite(PWM_B,50);
  delay(1000);
  
}

// 中断服务程序:处理电机 A 的编码器信号
void handleMotorA() {
  if (digitalRead(E1A) == digitalRead(E1B)) {
      countA++; // 正转          
    } else {
      countA--;
    }
}
// 中断服务程序:处理电机 B 的编码器信号
void handleMotorB() {
  if (digitalRead(E2A) == digitalRead(E2B)) {
      countB--; // 正转      
    } else {
      countB++;
    }
}

//获取当前转速的函数
void get_current_vel(){
  long right_now = millis();  
  long past_time = right_now - start_time;//计算逝去的时间
  if(past_time >= interval_time){//如果逝去时间大于等于一个计算周期
    //1.禁止中断
    noInterrupts();
    //2.计算转速 转速单位可以是秒,也可以是分钟... 自定义即可
    current_velA = (double)countA / per_round / past_time * 1000 * 60;
    current_velB = (double)countB / per_round / past_time * 1000 * 60;
    //3.重置计数器
    countA = 0;
    countB = 0;
    //4.重置开始时间
    start_time = right_now;
    //5.重启中断
    interrupts();

    Serial.println(current_velA);
    Serial.println(current_velB);
  }
}

打开串口监视器可看到打印的转速(由于反转的持续时间长,所以转速为负数)

图17 电机测速-串口监视器

Arduino IDE中还提供了串口绘图器:

图18 电机测速-串口绘图器

六、PID调速

6.1 PID介绍

偷个小懒,就不敲了(滑稽)

图19 PID介绍

6.2 下载PID库

Arduino-PID-Library

在 GitHub 下载 PID 库: git clone GitHub - br3ttb/Arduino-PID-Library

然后将该文件夹移动到 arduino 的 libraries下

需要把中划线去掉,即重命名Arduino-PID-Library 为ArduinoPIDLibrary

最后重启 ArduinoIDE

满足下图即安装完成。

图20 PID安装

6.3 PID调速

  PID 调速实现:
 使用之前电机控制以及测速代码,并且包含PID头文件,创建PID对象,在setup中启用自动调试,用Compute()输出控制电机的 PWM 值并更新PWM。

PID头文件:

#include <PID_v1.h> 

创建PID对象:

获取当前转速来源于之前实现的测速。

设定目标转速target。

在PID中会根据当前转速和设定的目标转速,不断实时的调整计数PWM值,并将计算出来的PWM值赋值给电机相应的PWM值,实现速度的调节。

调参:Kp(比例环节)、Ki(积分环节)、Kd(微分环节)  

PID的参数过程是需要细心仔细的微调哦

//创建 PID 对象
//1.当前转速 ; 2.计算输出的pwm ; 3.目标转速 ; 4.kp ; 5.ki ; 6.kd ; 7.DIRECT/REVERSE(后者的P、I、D值会取反) 当输入与目标值出现偏差时,向哪个方向控制
double pwmA;//右电机A驱动的PWM值
double pwmB;//左电机B驱动的PWM值
double target = 10;
double Kp=5.0, Ki=5.0, Kd=0.08;
PID pidA(&current_velA,&pwmA,&target,Kp,Ki,Kd,DIRECT);
PID pidB(&current_velB,&pwmB,&target,Kp,Ki,Kd,DIRECT);

完整程序如下:

注:A表示右电机,B表示左电机

/*
 * PID 调速实现:
 * 1.代码准备,复制并修改电机控制以及测速代码
 * 2.包含PID头文件
 * 3.创建PID对象
 * 4.在setup中启用自动调试
 * 5.调用Compute()输出控制电机的 PWM 值并更新PWM
 * 
 */
//右电机表示A或1
//左电机表示B或2

#include <PID_v1.h> 

#define E1A 2  // 右电机 A 编码器 A 相
#define E1B 4  // 右电机 A 编码器 B 相
#define E2A 3  // 左电机 B 编码器 A 相
#define E2B 5  // 左电机 B 编码器 B 相

int DIR_A1 = 13;//右电机A的A相-接13引脚  
int DIR_A2 = 7;//右电机A的B相-接7引脚     
int PWM_A = 11; //右电机A的PWM值       

int DIR_B1 = 9;//左电机B的A相-接9引脚  
int DIR_B2 = 8;//左电机B的B相-接8引脚     
int PWM_B = 10; //左电机B的PWM值

volatile int countA = 0;//左电机B的计数
volatile int countB = 0;//右电机A的计数

// 中断服务程序:处理右电机 A 的编码器信号
//如果右电机1的A相等于右电机1的B相,则countA++计数一次;否则--。
void handleMotorA() {
  if (digitalRead(E1A) == digitalRead(E1B)) { 
      countA++; // 正转          
    } else {
      countA--;
    }
}
// 中断服务程序:处理左电机 A 的编码器信号
//如果左电机2的A相不等于左电机2的B相,则countB++计数一次;否则--。(注意:因为与右电机的计数方向相反,这里手动调整了一下)
void handleMotorB() {
  if (digitalRead(E2A) == digitalRead(E2B)) {
      countB--; // 正转      
    } else {
      countB++;
    }
}
//定义"计数"换算"转速"的一些计算参数
int reducation = 90;//减速比,根据电机参数设置,比如 15 | 30 | 60
int pulse = 11; //编码器旋转一圈产生的脉冲数,该值需要参考商家电机参数
int per_round = pulse * reducation * 4;//车轮旋转一圈产生的脉冲数 *减速比*N倍频(1 2 4)
long start_time = millis();//一个计算周期的开始时刻,初始值为 millis();
long interval_time = 50;//一个计算周期 50ms
double current_velA;  //定义双浮点数 current_velA来接收右电机A由countA转换后的转速
double current_velB;  //定义双浮点数 current_velB来接收左电机B由countB转换后的转速

//获取当前转速的函数
void get_current_vel(){
  long right_now = millis();  //获取当前时刻
  long past_time = right_now - start_time;//计算逝去的时间:当前时刻-开始时刻
  if(past_time >= interval_time){//如果逝去时间大于等于一个计算周期,则进入计算转速过程
    //1.禁止中断  
    noInterrupts();
    //2.计算转速 转速单位可以是秒,也可以是分钟... 自定义即可。
//用计数(double(确保count非零))count除以[车轮旋转一圈产生的脉冲数 *减速比*N倍频(1 2 4)]per_round,再除以持续的时间past_time,再分别除以1000和60将单位化成m/s
    current_velA = (double)countA / per_round / past_time * 1000 * 60;
    current_velB = (double)countB / per_round / past_time * 1000 * 60;
    delay(1000); //延时1s再打印
//    Serial.println(start_time); //可打印开始时刻
//    Serial.println(right_now);  //可打印当前时刻
    Serial.println(current_velA);  //打印转速
    Serial.println(current_velB);
    //3.重置计数器
    countA = 0;
    countB = 0;
    //4.重置开始时间
    start_time = right_now;
    //5.重启中断
    interrupts();
  }
}

//-------------------------------------PID-------------------------------------------
//创建 PID 对象
//1.当前转速 ; 2.计算输出的pwm ; 3.目标转速 ; 4.kp ; 5.ki ; 6.kd ; 7.DIRECT/REVERSE(后者的P、I、D值会取反) 当输入与目标值出现偏差时,向哪个方向控制
double pwmA;//右电机A驱动的PWM值
double pwmB;//左电机B驱动的PWM值
double target = 10;
//double Kp=4.5, Ki=5.0, Kd=0.1;
double Kp=5.0, Ki=5.0, Kd=0.08;
PID pidA(&current_velA,&pwmA,&target,Kp,Ki,Kd,DIRECT);
PID pidB(&current_velB,&pwmB,&target,Kp,Ki,Kd,DIRECT);
//速度更新函数
void update_vel(){
  //获取当前速度
  get_current_vel();
  // 对当前速度进行平滑处理
//  static double smoothed_velA = 0;
//  smoothed_velA = 0.9 * smoothed_velA + 0.1 * current_velA;  // 低通滤波
  //右电机A
  pidA.Compute();//计算需要输出右电机A的PWM
  pwmA = constrain(pwmA, 0, 255);  // 限制PWM值,防止控制反应过激
  digitalWrite(DIR_A1,HIGH);  //右电机A的A相设置为HIGH,右电机A的B相设置为LOW,实现正转
  digitalWrite(DIR_A2,LOW);  
  analogWrite(PWM_A,pwmA);   //将  pidA.Compute()计算输出的pwmA值给右电机的PWM_A值
  //左电机B
  pidB.Compute();//计算需要输出左电机B的PWM
  pwmB = constrain(pwmB, 0, 255);  // 限制PWM值,防止控制反应过激
  digitalWrite(DIR_B1,HIGH);  //左电机B的A相设置为HIGH,左电机B的B相设置为LOW,实现正转
  digitalWrite(DIR_B2,LOW);  
  analogWrite(PWM_B,pwmB);   //将  pidB.Compute()计算输出的pwmB值给右电机的PWM_B值  
}

void setup() {
  Serial.begin(57600);//设置波特率  
  pinMode(E1A, INPUT);
  pinMode(E1B, INPUT);
  pinMode(E2A, INPUT);
  pinMode(E2B, INPUT);
  //两个电机驱动引脚都设置为 OUTPUT
  pinMode(DIR_A1,OUTPUT);
  pinMode(DIR_A2,OUTPUT);
  pinMode(PWM_A,OUTPUT);
  pinMode(DIR_B1,OUTPUT);
  pinMode(DIR_B2,OUTPUT);
  pinMode(PWM_B,OUTPUT);
  
  attachInterrupt(digitalPinToInterrupt(E1A),handleMotorA,RISING);//当电平发生改变时触发中断函数
  //四倍频统计需要为B相也添加中断
  attachInterrupt(digitalPinToInterrupt(E2A),handleMotorB,RISING);
  //启用PID自动控制
  pidA.SetMode(AUTOMATIC);
  pidB.SetMode(AUTOMATIC);
}



void loop() {
  //循环调用update_vel()去获取当前转速,并通过PID调速
  delay(10);
  update_vel();
}

将串口绘图器打开,可以很清楚的看到,PID努力调节的样子。

当然,这也需要你——调参侠的努力!

图21 PID控制-串口绘图器


欢迎大家讨论交流学习!

<think>好的,我现在需要帮助用户了解如何使用Arduino Uno控制步进电机。用户提供了几个引用资料,我需要先仔细阅读这些资料,然后整理出一个清晰的步骤。 首先,引用1提到需要使用Arduino控制步进电机的运动,通过调整脉冲信号来实现位置和速度控制。引用3和2都提到了连接引脚和编写代码,以及可能需要的库修改。引用4则指出可能需要修改驱动库,特别是Stepper.cpp中的部分代码。这说明用户可能需要处理库的兼容性问题。 接下来,我应该按照步骤来组织回答:硬件连接、库安装与修改、代码编写、参数调整。硬件连接部分需要明确步进电机型号,比如28BYJ-48,说明如何连接Uno的引脚到驱动板。可能用户使用的是ULN2003驱动板,这常见于这种电机。 然后是库的问题。Arduino自带的Stepper库可能需要修改,如引用4所述,需要注释掉Stepper.cpp中的某些行。这部分需要详细说明步骤,避免用户遇到问题。代码示例部分,引用3和2都提供了示例代码,可以整合这些代码,解释关键部分,比如设置步数、速度,以及步进函数的使用。 同时,要注意用户可能遇到的常见问题,比如电机不转或者抖动,可能需要检查接线、电流或代码中的步数是否正确。参数调整部分要解释如何通过改变延迟时间来控制速度,或者步数来控制角度。 最后,生成相关问题需要考虑用户可能的后续疑问,比如如何控制转动角度、提高精度、处理多电机,或者不使用库的方法。这些问题应该基于引用中的内容,比如引用1提到位置和速度控制,引用2提到应用场景,引用4涉及库修改,所以相关问题可以围绕这些点展开。 需要确保回答符合系统级指令,数学公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及较少数学内容,主要关注代码和硬件步骤。同时,回答要结构清晰,分步骤说明,在适当位置添加引用标记,如引用1到4的信息。 总结下来,回答的结构应该是: 1. 硬件连接 2. 库安装与修改 3. 代码示例及解释 4. 参数调整方法 5. 注意事项 6. 相关问题 这样用户能够一步步跟随操作,解决问题。同时,确保代码格式正确,使用中文回答,自然添加引用标记。</think>### Arduino Uno控制步进电机驱动与代码示例 #### 1. 硬件连接(以28BYJ-48步进电机为例) - **步进电机**:4相5线式,工作电压5V - **驱动模块**:ULN2003驱动板 - **接线方式**: - `IN1` → Arduino `D8` - `IN2` → Arduino `D9` - `IN3` → Arduino `D10` - `IN4` → Arduino `D11` - 驱动板电源正负极需连接外部5V电源[^3] #### 2. 驱动库修改(部分型号需调整) 找到Arduino IDE安装目录,修改`\libraries\Stepper\src\Stepper.cpp`文件: ```cpp // 注释以下代码(约255行) // switch (this->pin_count) { // case 4:... // } ``` 修改后保存文件重启IDE[^4] #### 3. 基础控制代码 ```cpp #include <Stepper.h> // 定义步进电机参数(2048步/圈) #define STEPS 2048 Stepper motor(STEPS, 8, 10, 9, 11); // 注意引脚顺序 void setup() { motor.setSpeed(10); // 设置转速(RPM) } void loop() { motor.step(2048); // 顺时针转1圈 delay(1000); motor.step(-2048); // 逆时针转1圈 delay(1000); } ``` 代码说明: - `setSpeed()`设置每分钟转数(RPM) - `step()`参数正负控制转向,绝对值控制步数 #### 4. 关键参数调整方法 - **速度控制**:通过改变`setSpeed()`值或调整步间延时 ```cpp // 高速模式(15 RPM) motor.setSpeed(15); ``` - **定位控制**:通过计算步数实现角度控制 $$角度 = \frac{步数}{总步数/360^\circ}$$ 例如:`motor.step(512)`实现$90^\circ$转动[^1] #### 5. 注意事项 1. 确保驱动板供电充足(建议单独5V电源) 2. 电机抖动时可尝试降低转速 3. 实际步数可能因电机型号不同需要校准[^2]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值