【花雕学编程】Arduino BLDC 之速度控制与位置控制结合
本文介绍了一种基于双闭环PID控制的电机控制策略,用于实现精确的转速和位置控制。该系统采用位置环(外环)和速度环(内环)的串级结构,通过Arduino主控结合编码器和霍尔传感器反馈,实现对BLDC电机的精准控制。系统具有精确位置跟踪、平滑速度调节、良好动态性能等特点,适用于工业自动化、机器人、3D打印等多种场景。文章详细阐述了硬件配置(Arduino UNO、BLDC电机、L298N驱动器等)、P

这是一个经典的双闭环(或嵌套单/双闭环)电机控制策略,旨在同时实现对电机转速和旋转位置(或角度)的精确控制。这种结合方式能够满足许多需要既定速度又定位置的复杂运动控制需求。
一、 主要特点 (Key Features)
双闭环控制结构 (Dual-Loop Control Structure):
核心: 通常采用位置环(外环)和速度环(内环)的串级控制结构。
工作原理:
位置环: 由Arduino主控执行。将期望位置(Position Setpoint)与实际反馈位置(Position Feedback,通常来自编码器)进行比较,得到位置误差。该误差通过位置环控制器(通常是PI或PID控制器)计算出一个速度指令(Velocity Command)。
速度环: 可以由Arduino执行,或者由电机驱动器(ESC)的内部控制器执行(如果ESC支持速度控制模式)。将位置环输出的速度指令与实际反馈速度(Speed Feedback,通常也由编码器计算得出)进行比较,得到速度误差。该误差通过速度环控制器(通常是PI或PID控制器)计算出最终的控制量(如PWM占空比或期望电流)来驱动BLDC电机。
功能: 位置环决定了电机最终要达到的目标,速度环则负责快速、平稳地将电机转速调节到位置环所要求的值,从而实现精确的位置控制。
精确的位置跟踪 (Precise Position Tracking):
核心: 依赖于高分辨率的位置反馈传感器(通常是增量式或绝对值编码器)。
功能: 能够将电机轴精确地定位到指令要求的角度或位置,实现点对点(Point-to-Point, PTP)定位或按特定轨迹运动(如电子齿轮、凸轮)。精度取决于编码器分辨率、机械传动精度和控制算法的性能。
平滑的速度调节与过渡 (Smooth Speed Regulation and Transition):
核心: 速度环的存在使得电机在从一个位置移动到另一个位置的过程中,其速度可以被精确控制,实现平滑的加速、匀速和减速过程。
功能: 避免了单纯位置控制可能带来的速度冲击或震荡,提高了运动的平稳性,减少了机械冲击,尤其在需要高速运动或精确定位的应用中至关重要。
动态性能与稳定性 (Dynamic Performance & Stability):
核心: 两个控制环的参数(PI/PID的比例、积分、微分系数)需要协调整定。
功能: 速度环通常设计得比位置环快,以提供快速的电流/转矩响应。位置环则负责整体的定位精度和响应速度。正确的参数整定能够保证系统具有良好的动态响应(快速到达目标)、较小的超调量和良好的稳定性。
灵活的运动模式 (Flexible Motion Modes):
核心: 控制系统可以根据需要切换不同的控制模式。
功能:
位置模式: 指定目标位置,系统自动完成运动。
速度模式: 指定目标速度,系统维持该速度运行。
混合模式: 例如,在大部分行程中保持某一速度,接近目标时切换到位置控制进行精确定位。
二、 应用场景 (Application Scenarios)
工业自动化设备 (Industrial Automation Equipment):
用途: 在数控机床(CNC)、印刷机械、包装机械、纺织机械中,需要电机精确地移动到指定位置并可能以特定速度运行,如刀具定位、工件传送、图案印刷、布料裁剪等。
价值: 提高生产精度和效率。
机器人关节驱动 (Robot Joint Actuation):
用途: 机器人手臂的各个关节需要精确控制其角度(位置),同时在运动过程中需要平滑的速度控制以实现协调、稳定的运动。
价值: 实现精确的末端执行器定位和姿态控制。
精密仪器与光学设备 (Precision Instruments & Optical Equipment):
用途: 如激光打标机、激光切割机、光学扫描仪、望远镜指向系统等,需要电机驱动镜片、激光头或整个设备以精确的位置和速度移动。
价值: 确保操作精度和图像/光束质量。
3D打印机/绘图仪 (3D Printers/Plotters):
用途: 控制打印头或绘图笔在X、Y、Z轴上的精确定位和移动速度,以形成精确的物理模型或图形。
价值: 提高打印/绘制精度和表面质量。
相机云台/天线座 (Camera Gimbals/Antenna Mounts):
用途: 需要将相机或天线精确指向特定角度,并能平滑地跟踪移动目标。
价值: 实现稳定的拍摄或通信。
医疗设备 (Medical Equipment):
用途: 如手术机器人、诊断设备中的精密扫描机构、输液泵等,需要高精度的位置和速度控制。
价值: 确保操作安全和精确。
三、 需要注意的事项 (Considerations & Challenges)
高性能主控需求 (High-Performance Controller Requirement):
问题: 实时运行双闭环PI/PID控制算法,特别是当编码器分辨率高、控制频率要求高(如kHz级别)时,对Arduino主控的计算能力提出了较高要求。经典的Arduino Uno/Nano可能难以胜任,尤其是在同时处理其他任务(如通信、I/O)时。
对策: 选用性能更强的Arduino型号(如Arduino Due、Mega 2560)或更强大的主控(如ESP32、STM32系列),确保有足够的处理能力和定时器资源。
高分辨率位置反馈 (High-Resolution Position Feedback):
问题: 精确的位置控制依赖于高分辨率、高精度、响应迅速的位置传感器(编码器)。
对策: 选择合适的编码器类型(增量式/绝对值)和分辨率,确保其机械安装精度和电气接口(如ABZ正交编码器信号)与系统匹配。
控制参数整定 (Controller Parameter Tuning):
问题: 位置环和速度环的PI/PID参数需要仔细整定,以达到最佳的动态响应、稳定性和抗干扰能力。参数不合适可能导致系统震荡、响应迟缓或无法稳定。
对策: 使用Ziegler-Nichols等经典整定方法,或更先进的自整定算法、仿真工具进行辅助调试。通常先整定内环(速度环),再整定外环(位置环)。
传感器噪声与延迟 (Sensor Noise & Delay):
问题: 编码器信号可能存在噪声或传输延迟,影响控制精度和稳定性。
对策: 采用硬件滤波(如RC滤波器)或软件滤波(如移动平均、卡尔曼滤波)处理传感器信号,但需注意滤波会引入延迟,需权衡。
机械系统特性 (Mechanical System Characteristics):
问题: 机械传动装置(如齿轮箱、皮带、丝杠)的间隙(Backlash)、弹性变形、摩擦力等因素会影响最终的控制精度和动态性能。
对策: 在设计和控制算法中考虑这些非线性因素,或采用补偿算法。
BLDC驱动器特性 (BLDC Driver Characteristics):
问题: 如果使用现成的ESC(电子调速器),其内部速度控制环的带宽和参数可能无法精确匹配外层的位置环需求。ESC的响应延迟和非线性特性也会影响整体性能。
对策: 选择性能优良、响应速度快的ESC,或考虑使用支持闭环速度/位置控制的专用BLDC驱动芯片(如Trinamic、Infineon等),以便更精细地控制内外环参数。
实时性 (Real-Time Performance):
问题: 位置和速度控制要求严格的实时性,控制周期必须稳定且足够短。
对策: 使用定时器中断来触发控制算法的执行,避免使用delay()等阻塞函数,确保控制回路的周期性。

1、基于PID的双环控制机械臂(入门级)
功能:通过电位器设定机械臂目标角度,用霍尔传感器检测电机位置(位置环),编码器检测转速(速度环),双PID控制器输出PWM信号驱动BLDC电机,实现精准的角度控制。
硬件:Arduino UNO、BLDC电机(带霍尔传感器+增量式编码器)、L298N驱动器、SG90舵机(辅助关节)、OLED显示屏。
代码逻辑:
输入采集:读取电位器的目标角度(setpoint_pos);
位置环计算:计算当前角度(current_pos)与目标角度的误差(error_pos),经位置PID输出速度给定值(speed_ref);
速度环计算:计算当前转速(current_speed)与速度给定值的误差(error_speed),经速度PID输出PWM占空比;
显示输出:实时显示目标角度、当前角度、速度给定值等信息。
#include <PID_v2.h>
#include <Adafruit_SSD1306.h>
// 硬件引脚定义
#define HALL_A D2 // 霍尔传感器(测位置)
#define ENCODER_A D3 // 编码器A相(测速度)
#define ENCODER_B D4 // 编码器B相(判方向)
#define MOTOR_EN A1 // 电机使能引脚
#define POT_PIN A0 // 电位器(目标角度输入)
// PID参数(经验调参)
double Kp_pos = 2.0, Ki_pos = 0.5, Kd_pos = 0.1; // 位置环PID
double Kp_speed = 10.0, Ki_speed = 2.0, Kd_speed = 1.0; // 速度环PID
// PID对象
PID posPID(&posError, &speedRef, &targetPos, Kp_pos, Ki_pos, Kd_pos, DIRECT);
PID speedPID(&speedError, &pwmOutput, &speedRef, Kp_speed, Ki_speed, Kd_speed, DIRECT);
// 全局变量
float targetPos = 0; // 目标角度(电位器输入)
float currentPos = 0; // 当前角度(霍尔计数转换)
float currentSpeed = 0; // 当前转速(编码器脉冲/秒)
float posError = 0; // 位置误差
float speedError = 0; // 速度误差
float speedRef = 0; // 速度给定值(位置环输出)
float pwmOutput = 0; // PWM输出(速度环输出)
int hallCount = 0; // 霍尔传感器计数
volatile int encoderCount = 0; // 编码器脉冲计数(中断累加)
void setup() {
Serial.begin(9600);
pinMode(HALL_A, INPUT);
pinMode(ENCODER_A, INPUT);
pinMode(ENCODER_B, INPUT);
pinMode(MOTOR_EN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(HALL_A), hallISR, FALLING); // 霍尔中断
attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderISR, CHANGE); // 编码器中断
// 初始化PID
posPID.SetSampleTime(10); // 位置环采样周期10ms
speedPID.SetSampleTime(10); // 速度环采样周期10ms
// 初始化OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
}
void loop() {
// 1. 读取目标角度(电位器)
targetPos = map(analogRead(POT_PIN), 0, 1023, 0, 180); // 映射到0-180°
// 2. 位置环计算(每10ms执行一次)
if (millis() - lastPosTime >= posPID.GetSampleTime()) {
posError = targetPos - currentPos;
posPID.Compute();
lastPosTime = millis();
}
// 3. 速度环计算(每10ms执行一次)
if (millis() - lastSpeedTime >= speedPID.GetSampleTime()) {
speedError = speedRef - currentSpeed;
speedPID.Compute();
// 输出PWM(限制在0-255)
pwmOutput = constrain(pwmOutput, -255, 255);
analogWrite(MOTOR_EN, abs(pwmOutput));
digitalWrite(MOTOR_IN1, pwmOutput > 0 ? HIGH : LOW);
digitalWrite(MOTOR_IN2, pwmOutput > 0 ? LOW : HIGH);
lastSpeedTime = millis();
}
// 4. 显示信息
display.clearDisplay();
display.setCursor(0, 0);
display.println("Target: " + String(targetPos) + "°");
display.println("Current: " + String(currentPos) + "°");
display.println("Speed Ref: " + String(speedRef) + " RPM");
display.println("PWM: " + String(pwmOutput));
display.display();
delay(10);
}
// 霍尔传感器中断:更新当前角度(每转一圈计数)
void hallISR() {
hallCount++;
currentPos = hallCount * 360 / BLDC_POLE_PAIRS; // 转换为角度(需校准极对数)
}
// 编码器中断:计算转速(M法测速)
void encoderISR() {
static unsigned long lastTime = 0;
static int lastCount = 0;
encoderCount++;
if (millis() - lastTime >= 100) { // 每100ms计算一次转速
currentSpeed = (encoderCount - lastCount) * 60 / (millis() - lastTime); // 转换为RPM
lastCount = encoderCount;
lastTime = millis();
}
}
2、基于模糊PID的自适应控制传送带(进阶级)
功能:针对传送带负载变化(如货物重量波动)导致的转速不稳定问题,用模糊PID动态调整速度环参数,结合位置环实现恒张力/恒速度控制。
硬件:Arduino Mega、BLDC电机(带编码器)、TB6560驱动器、Load Cell(称重传感器)、HX711 AD模块、OLED显示屏。
代码逻辑:
负载检测:通过Load Cell读取传送带上的货物重量;
模糊推理:根据负载变化率和速度误差,调整速度环的Kp/Ki/Kd参数;
双环控制:位置环保证传送带长度精度,速度环用模糊PID保持恒速度;
显示报警:当负载超过阈值时触发蜂鸣器报警。
#include <Fuzzy.h>
#include <PID_v2.h>
#include <HX711.h>
// 硬件定义
HX711 loadCell; // Load Cell AD模块
#define LOAD_DT D5
#define LOAD_SCK D6
PID adaptiveSpeedPID(&speedError, &pwmOutput, &speedRef, Kp_speed, Ki_speed, Kd_speed, DIRECT); // 自适应速度PID
Fuzzy* fuzzyParams; // 模糊控制器(调整PID参数)
// 全局变量
float loadWeight = 0; // 负载重量(kg)
float loadChangeRate = 0; // 负载变化率(kg/s)
float speedError = 0; // 速度误差
float pwmOutput = 0; // PWM输出
float Kp_speed = 10.0, Ki_speed = 2.0, Kd_speed = 1.0; // 初始速度PID参数
void setup() {
Serial.begin(9600);
loadCell.begin(LOAD_DT, LOAD_SCK); // 初始化Load Cell
loadCell.set_gain(128); // 增益设置(对应1kg满量程)
// 初始化模糊控制器
fuzzyParams = new Fuzzy(2, 3); // 输入:负载变化率、速度误差;输出:ΔKp、ΔKi、ΔKd
defineFuzzyRules();
adaptiveSpeedPID.SetSampleTime(10); // 速度环采样周期10ms
}
void loop() {
// 1. 读取负载重量
loadWeight = loadCell.read() / 1000.0; // 转换为kg(需校准)
static float lastLoad = 0;
loadChangeRate = (loadWeight - lastLoad) / 0.1; // 计算100ms内的变化率
lastLoad = loadWeight;
// 2. 模糊推理调整PID参数
fuzzyParams->setInput(0, loadChangeRate); // 输入1:负载变化率
fuzzyParams->setInput(1, speedError); // 输入2:速度误差
fuzzyParams->fuzzify();
float deltaKp = fuzzyParams->getOutput(0); // ΔKp
float deltaKi = fuzzyParams->getOutput(1); // ΔKi
float deltaKd = fuzzyParams->getOutput(2); // ΔKd
// 3. 更新PID参数(限制变化范围,避免不稳定)
Kp_speed = constrain(Kp_speed + deltaKp, 5.0, 15.0);
Ki_speed = constrain(Ki_speed + deltaKi, 1.0, 3.0);
Kd_speed = constrain(Kd_speed + deltaKd, 0.5, 2.0);
adaptiveSpeedPID.SetTunings(Kp_speed, Ki_speed, Kd_speed);
// 4. 双环控制计算(同案例1)
// ...(位置环计算→速度环计算→输出PWM)...
// 5. 显示与报警
if (loadWeight > 10) { // 负载超过10kg触发报警
digitalWrite(BUZZER, HIGH);
} else {
digitalWrite(BUZZER, LOW);
}
delay(100);
}
// 定义模糊规则(示例)
void defineFuzzyRules() {
// 输入1:负载变化率(-2~2 kg/s)→ Negative/Zero/Positive
fuzzyParams->addInputSet(new Triangular("Neg", -2, -1, 0));
fuzzyParams->addInputSet(new Triangular("Zero", -1, 0, 1));
fuzzyParams->addInputSet(new Triangular("Pos", 0, 1, 2));
// 输入2:速度误差(-50~50 RPM)→ Small/Medium/Large
fuzzyParams->addInputSet(new Triangular("Sml", -50, -25, 0));
fuzzyParams->addInputSet(new Triangular("Med", -25, 0, 25));
fuzzyParams->addInputSet(new Triangular("Lrg", 0, 25, 50));
// 输出:ΔKp(-2~2)→ Decrease/Maintain/Increase
fuzzyParams->addOutputSet(new Triangular("Dec", -2, -1, 0));
fuzzyParams->addOutputSet(new Triangular("Main", -1, 0, 1));
fuzzyParams->addOutputSet(new Triangular("Inc", 0, 1, 2));
// 添加规则:若负载增加快且速度误差大→增大Kp
fuzzyParams->addRule(new FuzzyRule("Pos", "Lrg", "Inc"));
}
3、基于CAN总线的多电机同步控制(高级级)
功能:通过CAN总线实现多台BLDC电机的同步控制(如印刷机、纺织机的辊轴同步),主电机作为“主轴”发送同步指令,从电机跟随主轴的位置与速度,保证多轴协同精度。
硬件:Arduino Mega(主)、Arduino Uno(从×3)、BLDC电机(带编码器)、MCP2515 CAN模块、TB6560驱动器、示波器。
代码逻辑:
主电机控制:主电机接收人工输入的目标位置/速度,运行双环控制;
CAN通信:主电机定期向CAN总线发送同步帧(包含当前位置、速度、校验码);
从电机控制:从电机接收同步帧,解析后调整自身的位置/速度目标值,运行双环控制;
同步校验:从电机定期向主电机发送状态反馈,主电机检查同步误差,超限时调整从电机参数。
// 主电机代码(Arduino Mega)
#include <SPI.h>
#include <MCP2515.h>
#include <PID_v2.h>
MCP2515 canBus(CAN_CS, CAN_INT); // CAN模块对象
PID mainPosPID(&posError, &mainSpeedRef, &mainTargetPos, Kp_pos, Ki_pos, Kd_pos); // 主电机位置环
PID mainSpeedPID(&speedError, &mainPwm, &mainSpeedRef, Kp_speed, Ki_speed, Kd_speed); // 主电机速度环
void setup() {
Serial.begin(115200);
canBus.reset();
canBus.setBitrate(CAN_500KBPS); // CAN总线速率
canBus.setNormalMode();
// 初始化PID
mainPosPID.SetSampleTime(10);
mainSpeedPID.SetSampleTime(10);
}
void loop() {
// 1. 主电机双环控制(同案例1)
// ...(读取目标位置→位置环计算→速度环计算→输出PWM)...
// 2. 发送CAN同步帧(包含主电机当前位置、速度)
CanMsg syncFrame;
syncFrame.id = 0x100; // 同步帧ID
syncFrame.data[0] = (uint8_t)(mainTargetPos >> 8); // 位置高字节
syncFrame.data[1] = (uint8_t)(mainTargetPos & 0xFF); // 位置低字节
syncFrame.data[2] = (uint8_t)(mainSpeedRef >> 8); // 速度高字节
syncFrame.data[3] = (uint8_t)(mainSpeedRef & 0xFF); // 速度低字节
syncFrame.data[4] = 0xAA; // 校验码(固定)
canBus.sendMessage(syncFrame);
// 3. 接收从电机反馈(检查同步误差)
if (canBus.available()) {
CanMsg feedback = canBus.readMessage();
float slavePos = (feedback.data[0] << 8) | feedback.data[1];
float error = abs(mainTargetPos - slavePos);
if (error > 5) { // 同步误差超过5°,调整从电机参数
canBus.sendMessage(createAdjustFrame(error));
}
}
delay(10);
}
// 从电机代码(Arduino Uno)
#include <MCP2515.h>
#include <PID_v2.h>
MCP2515 canBus(CAN_CS, CAN_INT);
PID slavePosPID(&slavePosError, &slaveSpeedRef, &syncPos, Kp_pos, Ki_pos, Kd_pos); // 从电机位置环
PID slaveSpeedPID(&slaveSpeedError, &slavePwm, &slaveSpeedRef, Kp_speed, Ki_speed, Kd_speed); // 从电机速度环
void setup() {
canBus.init();
canBus.setListenOnlyMode(); // 监听CAN总线
}
void loop() {
if (canBus.available()) {
CanMsg frame = canBus.readMessage();
if (frame.id == 0x100) { // 接收同步帧
// 解析同步数据
syncPos = (frame.data[0] << 8) | frame.data[1];
syncSpeed = (frame.data[2] << 8) | frame.data[3];
// 从电机双环控制(跟随同步数据)
slavePosError = syncPos - currentPos;
slavePosPID.Compute();
slaveSpeedError = slaveSpeedRef - currentSpeed;
slaveSpeedPID.Compute();
controlMotor(slavePwm);
}
}
}
要点解读
- 双环结构的“分层设计”:位置环在外,速度环在内
双环控制的核心是“外层定位置,内层定速度”:
位置环:负责跟踪目标位置,输出是速度给定值(告诉电机“要跑多快才能到达目标”);
速度环:负责跟踪速度给定值,输出是PWM占空比(直接控制电机扭矩,调整转速)。
注意:位置环的更新频率必须≥速度环(如均为10ms),否则会导致速度给定值滞后,引发振荡。
案例对应:所有案例均采用“位置环→速度环”的分层结构,确保控制的实时性与稳定性。 - 传感器的“互补融合”:解决单一传感器的局限性
位置检测:霍尔传感器适合检测电机的绝对位置(每转一圈计数固定),但分辨率低(如8极对数电机每圈8个脉冲);增量式编码器适合检测相对位置,分辨率高(如1000线编码器每圈1000个脉冲),两者结合可实现“粗精结合”的位置检测。
速度检测:编码器的M法测速(单位时间内脉冲数)适合高速场景,T法测速(脉冲间隔时间)适合低速场景,可通过切换算法覆盖全转速范围。
案例对应:案例1同时使用霍尔传感器(位置)和编码器(速度),解决了单一传感器的分辨率或精度问题。 - 参数调整的“工程技巧”:先调内环,再调外环
双环控制的调参顺序至关重要:
先调速度环:断开位置环(将位置环输出设为固定速度),调整速度环的Kp/Ki/Kd,直到电机能稳定跟踪速度给定值(无明显超调、稳态误差≤1%);
再调位置环:连接位置环,调整位置环的参数,重点关注阶跃响应(无超调或小超调,调整时间≤0.5s);
最后联调:测试负载变化、扰动等情况,优化参数鲁棒性。
案例对应:案例2中的模糊PID自动调整速度环参数,简化了手动调参的复杂度;案例3中的主从同步控制通过CAN总线传递参数,保证了多电机的一致性。 - 工业级的“可靠性”:抗干扰与容错设计
电磁兼容:电源端并联TVS二极管抑制浪涌,信号线采用屏蔽线,远离动力线(如BLDC电机的相线);
软件容错:加入看门狗定时器(Watchdog Timer),防止程序跑飞;设置传感器故障检测(如霍尔传感器离线时,自动切换到编码器模式);
硬件冗余:关键传感器(如编码器)采用双路备份,避免单点故障导致停机。
案例对应:案例3中的CAN总线具有CRC校验功能,可检测数据传输错误,保证同步指令的可靠性。 - 应用场景的“需求匹配”:不同场景选择不同控制策略
入门级:选PID双环控制(案例1),适合小型设备(如桌面机械臂),成本低、易实现;
进阶级:选模糊PID自适应控制(案例2),适合负载变化的工业设备(如传送带、包装机),鲁棒性强;
高级级:选CAN总线多电机同步(案例3),适合多轴协同的复杂设备(如印刷机、纺织机),支持分布式控制。
案例对应:三个案例分别覆盖了从“简单定位”到“自适应控制”再到“多机协同”的典型工业场景,满足不同需求。

4、机器人差速驱动(速度+位置协同)
#include <SimpleFOC.h>
// 双BLDC电机配置(左右轮)
BLDCMotor motorLeft(9), motorRight(10);
BLDCDriver3PWM driverLeft(5, 6, 7), driverRight(8, 11, 12);
Encoder encoderLeft(2, 3), encoderRight(4, 5); // 假设共用中断引脚需调整
// 定位与速度变量
volatile float targetPos = 0; // 目标位置(编码器脉冲数)
float currentPosLeft = 0, currentPosRight = 0;
float targetSpeed = 2.0; // rad/s
void setup() {
Serial.begin(115200);
// 左电机配置(位置+速度闭环)
motorLeft.linkDriver(&driverLeft);
motorLeft.linkSensor(&encoderLeft);
motorLeft.controller = MotionControlType::velocity; // 默认速度模式
motorLeft.PID_velocity.P = 0.5;
motorLeft.init();
// 右电机配置(同左电机)
motorRight.linkDriver(&driverRight);
motorRight.linkSensor(&encoderRight);
motorRight.controller = MotionControlType::velocity;
motorRight.init();
// 外部中断触发位置控制
attachInterrupt(digitalPinToInterrupt(2), updatePosition, CHANGE);
}
void loop() {
static unsigned long lastCmdTime = 0;
// 1. 位置控制阶段(例如移动1000脉冲)
if (millis() - lastCmdTime > 2000) { // 每2秒切换一次模式
targetPos += 1000; // 新目标位置
motorLeft.controller = motorRight.controller = MotionControlType::position;
motorLeft.target = targetPos;
motorRight.target = targetPos;
lastCmdTime = millis();
}
// 2. 速度控制阶段(位置到达后)
if (abs(currentPosLeft - targetPos) < 10) { // 接近目标时切换
motorLeft.controller = motorRight.controller = MotionControlType::velocity;
motorLeft.target = targetSpeed;
motorRight.target = targetSpeed;
}
// 3. 实时监控
currentPosLeft = encoderLeft.getPulseCount();
Serial.print("Pos:"); Serial.print(currentPosLeft);
Serial.print(" Speed:"); Serial.println(motorLeft.shaftVelocity());
}
void updatePosition() {
// 编码器中断服务程序(需根据实际硬件调整)
currentPosLeft = encoderLeft.getPulseCount();
currentPosRight = encoderRight.getPulseCount();
}
5、无人机螺旋桨转速与桨距角协同控制
#include <SimpleFOC.h>
#include <Servo.h>
// BLDC电机(转速控制)
BLDCMotor motor(9);
BLDCDriver3PWM driver(5, 6, 7);
Encoder encoder(2, 3);
// 舵机(桨距角位置控制)
Servo pitchServo;
#define PITCH_PIN 10
// 控制变量
float targetRPM = 3000;
float targetPitch = 15.0; // 度
bool isPositionMode = false;
void setup() {
Serial.begin(115200);
// BLDC初始化
motor.linkDriver(&driver);
motor.linkSensor(&encoder);
motor.controller = MotionControlType::velocity;
motor.PID_velocity.P = 0.1;
motor.init();
// 舵机初始化
pitchServo.attach(PITCH_PIN);
pitchServo.write(0); // 初始位置
// 远程控制输入(模拟)
pinMode(A0, INPUT); // 转速电位器
pinMode(A1, INPUT); // 桨距电位器
pinMode(8, INPUT_PULLUP); // 模式切换按钮
}
void loop() {
// 1. 模式切换(通过按钮)
if (digitalRead(8) == LOW) {
isPositionMode = !isPositionMode;
delay(300); // 防抖
}
// 2. 速度+位置混合控制
if (isPositionMode) {
// 桨距角位置控制(例如起飞后调整)
targetPitch = map(analogRead(A1), 0, 1023, 0, 30);
pitchServo.write(targetPitch);
// 转速保持恒定
motor.controller = MotionControlType::velocity;
motor.target = targetRPM;
} else {
// 起飞阶段:转速与桨距协同
float throttle = analogRead(A0) / 1023.0;
motor.target = throttle * 5000; // 0-5000 RPM
// 桨距角与转速联动(避免失速)
targetPitch = constrain(throttle * 30, 5, 30);
pitchServo.write(targetPitch);
}
// 3. 监控输出
Serial.print("RPM:"); Serial.print(motor.shaftVelocity());
Serial.print(" Pitch:"); Serial.println(targetPitch);
}
6、数控机床主轴定位与恒切削速度控制
#include <SimpleFOC.h>
// 主轴BLDC电机
BLDCMotor spindle(9);
BLDCDriver6PWM driver(5, 6, 7, 8, 11, 12); // 六步换相驱动
MagneticSensorSPI sensor(10, 14, 0x3FFF); // AS5048A编码器
// 控制参数
float targetAngle = 0; // 目标角度(度)
float cuttingSpeed = 1000; // 恒切削速度(RPM)
bool isCuttingMode = false;
void setup() {
Serial.begin(115200);
// 主轴初始化(高精度位置控制)
spindle.linkDriver(&driver);
spindle.linkSensor(&sensor);
spindle.controller = MotionControlType::angle;
spindle.PID_angle.P = 20.0; // 位置环高增益
spindle.init();
// 外部命令输入(模拟数控系统)
pinMode(A0, INPUT); // 角度电位器
pinMode(A1, INPUT); // 速度电位器
pinMode(2, INPUT_PULLUP); // 模式切换
}
void loop() {
// 1. 模式切换
if (digitalRead(2) == LOW) {
isCuttingMode = !isCuttingMode;
delay(300);
}
// 2. 混合控制逻辑
if (isCuttingMode) {
// 恒切削速度控制(忽略位置)
spindle.controller = MotionControlType::velocity;
spindle.target = cuttingSpeed;
// 动态调整位置环增益(防止超调)
spindle.PID_angle.P = 5.0;
} else {
// 精确位置控制
targetAngle = map(analogRead(A0), 0, 1023, 0, 360);
spindle.controller = MotionControlType::angle;
spindle.target = targetAngle * PI/180; // 转为弧度
// 降低速度环增益(提高位置精度)
spindle.PID_velocity.P = 0.3;
}
// 3. 实时补偿(温度/负载变化)
static float lastSpeed = 0;
float currentSpeed = spindle.shaftVelocity();
if (abs(currentSpeed - lastSpeed) > 100) {
spindle.voltage_sensor_align = currentSpeed * 0.1; // 动态补偿
lastSpeed = currentSpeed;
}
// 4. 监控输出
Serial.print("Angle:"); Serial.print(sensor.getAngle()*180/PI);
Serial.print(" Speed:"); Serial.println(currentSpeed);
}
要点解读
控制模式切换策略
必须设置平滑过渡机制(如案例1的abs(currentPos - targetPos) < 10判断),避免硬切换导致的振荡。
建议使用状态机模式(如案例2的isPositionMode标志)管理复杂逻辑。
传感器融合与冗余
速度控制依赖编码器(如案例6的shaftVelocity()),位置控制需高精度传感器(如AS5048A)。
建议增加霍尔传感器作为速度备份(BLDC常用配置)。
参数动态调整
位置环与速度环PID需分开调参(如案例6的PID_angle.P和PID_velocity.P)。
负载变化时需动态补偿(如案例3的温度补偿逻辑)。
执行机构协同
多执行器需解耦控制(如案例5的BLDC+舵机)。
建议通过CAN总线或共享变量实现协同(案例1的currentPosLeft/Right)。
安全机制
必须设置超时保护(如案例4的millis() - lastCmdTime)。
位置控制需限位保护(通过软件限制targetPos范围)。
速度控制需堵转检测(通过电流监测实现)。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)