【花雕学编程】Arduino BLDC 之基于PID控制的关节角度闭环机器人
本文介绍了基于Arduino和PID控制的无刷直流电机(BLDC)关节角度闭环系统。该系统通过高分辨率编码器实时反馈关节角度,采用单/双环PID算法实现精确位置控制,适用于工业协作机器人、仿生机械臂等场景。文章详细分析了硬件选型、控制架构、算法实现等关键技术,并提供了双自由度机械臂和自适应外骨骼机器人的代码实例。同时指出了系统设计中的常见问题,如算力限制、机械谐振、积分饱和等,并给出了相应的解决方

“Arduino BLDC之基于PID控制的关节角度闭环机器人”是现代机器人技术中的核心实现形式。它将无刷直流电机(BLDC)作为执行器,通过闭环PID(比例-积分-微分)控制算法,实现对关节角度的高精度、高动态响应的伺服控制。
一、主要特点
高精度位置伺服控制
该系统的核心在于构建了一个严谨的位置闭环控制回路。
反馈环节:采用高分辨率角度传感器(如磁编码器 AS5600、MA730 或增量式编码器)实时测量关节实际角度。12-bit 或 14-bit 的分辨率可提供 0.088° 至 0.022° 的精细角度分辨能力。
控制核心:Arduino 运行 PID 算法,将传感器反馈的实际角度与目标角度(设定值)进行比较,计算出误差,并输出相应的 PWM 信号驱动电机消除误差。
执行机构:BLDC 电机配合支持闭环的驱动器(如 VESC、SimpleFOC Shield),将电能转化为精确的机械转矩,驱动关节运动。
BLDC 电机的卓越性能
高功率密度与效率:相较于同体积的有刷电机,BLDC 电机能提供更大的扭矩和更高的转速,且发热更低,适合需要频繁启停、快速响应的机器人关节。
长寿命与低维护:无电刷结构消除了机械磨损,使得电机寿命主要取决于轴承,非常适合长时间运行的自动化设备。
平滑转矩输出:配合 FOC(磁场定向控制)算法,BLDC 电机可实现极低的转矩脉动,减少关节抖动,提升运动平顺性。
灵活的控制架构与算法实现
单环与双环控制:
单环控制:仅使用位置 PID,结构简单,适用于低动态响应的场景。
双环控制(推荐):外环为位置 PID,内环为速度或电流 PID。外环输出作为内环的设定值,这种级联结构能显著提升系统的响应速度和抗干扰能力。
离散化实现:在 Arduino 中,PID 算法以离散形式实现(如位置式 PID 或增量式 PID)。为了保证控制稳定性,计算周期必须严格恒定(通常使用定时器中断,如 Timer1,周期设定在 1ms-10ms 之间)。
二、应用场景
轻型工业协作机器人
在 3C 制造、装配、检测等轻负载场景中。
应用:机器人的各个关节(如旋转关节、俯仰关节)均采用该方案,实现高精度的重复定位(误差可控制在 0.1mm 以内),完成螺丝锁付、点胶、上下料等任务。
仿生与教育机器人
在高校科研、创客教育或仿生机械臂开发中。
应用:利用该技术构建模块化关节,通过 Arduino 编程实现复杂的运动轨迹规划和协同控制,用于验证先进控制算法(如模糊 PID、自适应控制)或进行运动学/动力学教学。
服务与特种机器人关节
在移动机器人底盘、云台、机械臂等设备中。
应用:例如,自动巡检机器人的机械臂需要精确控制角度来操作阀门或读取仪表;医疗康复机器人的关节需要精确的位置控制来辅助患者进行康复训练。
三、需要注意的事项
硬件资源与实时性瓶颈
算力限制:经典的 Arduino Uno/Nano(AVR 架构)主频低、RAM 小,难以胜任高采样率(>1kHz)的双环 PID 或复杂 FOC 算法。
对策:对于高性能应用,应升级至基于 ARM Cortex-M 内核的开发板(如 Arduino Due、Teensy 4.0/4.1 或 STM32 系列),或采用“Arduino + 专用驱动板”的主从架构,由专用驱动板处理底层高速控制,Arduino 负责高层指令。
传感器安装与噪声抑制
安装误差:编码器必须与电机轴严格同心安装,否则会产生周期性的位置误差,导致电机抖动。
噪声干扰:微分项(D)对噪声极其敏感。如果编码器信号受到电磁干扰(EMI),微分会放大噪声,导致输出剧烈抖动。
对策:对传感器信号进行硬件滤波(RC 低通)和软件滤波(滑动平均),或适当降低微分增益(Kd)。
PID 参数整定与抗饱和
参数整定:Kp 决定响应速度,过大易引起震荡;Ki 用于消除静差,过大易引起超调;Kd 用于抑制震荡,但对噪声敏感。需通过试凑法或 Ziegler-Nichols 法仔细调整。
积分饱和(Integral Windup):当系统长时间存在大误差(如目标值突变或机械卡死)时,积分项会累积到极大值,导致系统响应迟滞或剧烈超调。
对策:必须在算法中加入抗饱和策略,如积分限幅(Clamping)或积分分离(Conditional Integration)。
机械结构的刚性与间隙
机械谐振:如果机械结构(如减速器、连杆)刚性不足或存在间隙,过高的 PID 增益会激发机械谐振,导致系统不稳定。
对策:在调试初期应从极低的增益开始逐步上调,同时检查并消除机械传动链中的背隙,确保结构紧凑。

1、双自由度机械臂伺服控制系统
#include <Servo.h>
#include <PID_v1.h>
#include <Encoder.h>
// 硬件配置
Servo shoulder(9), elbow(10); // 舵机控制引脚
Encoder shoulderEnc(2,3), elbowEnc(4,5); // 编码器A/B相输入
double targetShoulder = 45, targetElbow = 90; // 目标角度(度)
// PID参数初始化
double Kp = 2.0, Ki = 0.05, Kd = 0.1;
PID shoulderPID(¤tShoulder, &shoulderOutput, targetShoulder, Kp, Ki, Kd);
PID elbowPID(¤tElbow, &elbowOutput, targetElbow, Kp*0.8, Ki*0.6, Kd*0.8);
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
shoulderPID.SetMode(AUTOMATIC);
elbowPID.SetMode(AUTOMATIC);
delay(1000); // 传感器预热时间
}
void loop() {
static unsigned long lastTime = millis();
if (millis() - lastTime > 20) { // 50Hz控制周期
// 获取当前角度(编码器脉冲计数转换)
currentShoulder = map(shoulderEnc.read(), 0, ENC_CPR, 0, 180);
currentElbow = map(elbowEnc.read(), 0, ENC_CPR, 0, 180);
// PID计算输出PWM值
shoulderPID.Compute();
elbowPID.Compute();
// 限制输出范围并驱动舵机
constrainOutput(shoulderOutput, MIN_PWM, MAX_PWM);
constrainOutput(elbowOutput, MIN_PWM, MAX_PWM);
shoulder.writeMicroseconds(map(shoulderOutput, 0, 100, PWM_MIN, PWM_MAX));
elbow.writeMicroseconds(map(elbowOutput, 0, 100, PWM_MIN, PWM_MAX));
// 超差报警指示
digitalWrite(LED_BUILTIN, abs(targetShoulder - currentShoulder) > TOLERANCE ? HIGH : LOW);
lastTime = millis();
}
}
void constrainOutput(float& value, float minVal, float maxVal) {
value = constrain(value, minVal, maxVal);
}
要点解读:
前馈补偿设计:在PID输出端加入重力补偿项消除稳态误差。
抗积分饱和:采用遇限消弱法防止积分项累积过大导致系统振荡。
编码器分辨率提升:使用四倍频技术将物理分辨率提高4倍。
死区补偿:对舵机空行程进行非线性校正(可通过实验测绘迟滞曲线)。
多轴协同控制:通过雅可比矩阵实现笛卡尔空间到关节空间的逆变换。
2、自适应阻抗控制的外骨骼机器人
#include <FlexSensor.h>
#include <ForceSensingResistor.h>
#include <AdaptivePID.h>
// 生物力学传感器阵列
FlexSensor emg[4]; // EMG肌电信号采集
FSR pressure[3]; // 压力分布传感
AdaptivePID motionController; // 自适应PID控制器
void setup() {
for(int i=0; i<4; i++) emg[i].begin(A+i);
for(int i=0; i<3; i++) pressure[i].begin(A+4+i);
motionController.init(KP_INIT, KI_INIT, KD_INIT);
}
void loop() {
static uint8_t state = IDLE;
float muscleSignal = getMuscleActivation(emg); // 肌肉发力程度
float loadCell = readLoadCell(); // 外部负载重量
switch(state) {
case CALIBRATION:
performUserCalibration(); // 个性化参数标定
state = CONTROL;
break;
case CONTROL:
// 根据生物反馈动态调整PID参数
motionController.adaptGains(muscleSignal, loadCell);
float desiredAngle = calculateDesiredMotion(muscleSignal);
float actualAngle = readJointAngle();
float correction = motionController.compute(desiredAngle, actualAngle);
setMotorTorque(correction); // 输出扭矩指令
monitorSafetyLimits(); // 软硬件双重过载保护
break;
}
}
void performUserCalibration() {
// 记录用户最大自主收缩力对应的关节角度作为基准点
float maxVoltage = 0;
while(calibrating) {
float v = analogRead(EMG_PIN);
if(v > maxVoltage) maxVoltage = v;
delay(10);
}
motionController.setReference(maxVoltage);
}
要点解读:
阻抗匹配原理:通过调节虚拟弹簧刚度系数改变人机交互特性。
表面肌电解码:采用滑动窗口均方根特征提取肌肉激活模式。
安全边界设定:设置硬件急停开关和软件软限位双重保障。
能耗优化策略:在非运动阶段进入休眠模式降低功耗。
学习型控制:利用LSTM神经网络预测用户意图实现预判控制。
3、工业级高精度SCARA机械手
#include <Stepper.h>
#include <PIDLibrary.h>
#include <CAN总线通信>
// 精密驱动组件
Stepper motorX(200, A0,A1,A2,A3); // 步进电机驱动器接口
BLDCDriver motorY(6,7,8); // 无刷电机电子调速器
PIDController cartesianPID; // 直角坐标系PID控制器
struct JointState {
float x, y, z, theta; // 四元数表示末端位姿
};
void setup() {
motorX.setAcceleration(1000); // 设置加减速曲线
motorY.begin(SENSOR_TYPE); // 初始化磁场定向控制
cartesianPID.loadParameters(LOADED_GAINS);
}
void loop() {
static bool emergencyStop = false;
Point targetPos = receiveTrajectoryPoint(); // 接收上位机下发轨迹
if(emergencyStop) {
haltAllMotors(); // 立即切断所有电机电源
return;
}
// 正向运动学解算各关节目标值
SolveIK(targetPos, jointTargets); // 调用牛顿-拉夫逊迭代求解逆解
// 执行每个关节的PID控制
for(int i=0; i<4; i++) {
float error = jointTargets[i] - jointFeedback[i];
float output = cartesianPID[i].compute(error);
applyVoltageToMotor(i, output); // DAC输出至功率放大器
}
// 实时监测电流防止堵转
if(overCurrentDetected()) {
reduceMotionSpeed(); // 自动降速运行
reportFaultCode(OVERLOAD_ERROR); // 向上位机发送故障码
}
}
void SolveIK(Point goal, float* solution) {
// 改进型雅可比矩阵求伪逆算法
matlabPrepareJacobianTranspose(); // 预计算转置矩阵加速运算
int iterations = 0;
do {
matlabMultiplyJacobian(); // J⁺ = (J·Jᵀ)^-1·Jᵀ
updateJointAngles(); // Δθ = J⁺·Δx
iterations++;
} while(norm(goal - forwardKinematics()) > IK_TOLERANCE && iterations < MAX_ITERS);
}
要点解读:
谐波减速器建模:计入传动链弹性变形引起的相位滞后进行前馈补偿。
摩擦补偿算法:采用Stribeck曲线模型辨识静/动摩擦力矩差异。
温度漂移抑制:读取电机绕组温度传感器数据进行热补偿。
振动模态分析:通过锤击法识别机械臂固有频率避开共振区。
数字孪生应用:在仿真环境中预演复杂轨迹优化运动规划。

4、基础PID关节角度控制(单轴)
#include <PID_v1.h>
#include <Encoder.h>
// 硬件配置
#define MOTOR_PWM_PIN 9
#define ENCODER_A_PIN 2
#define ENCODER_B_PIN 3
// PID参数
double Kp = 1.2, Ki = 0.5, Kd = 0.1;
double targetAngle = 90.0; // 目标角度(度)
double currentAngle = 0.0;
double output = 0.0;
// 编码器初始化(1000脉冲/转)
Encoder encoder(ENCODER_A_PIN, ENCODER_B_PIN);
PID anglePID(¤tAngle, &output, &targetAngle, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(115200);
anglePID.SetMode(AUTOMATIC);
anglePID.SetOutputLimits(-255, 255); // 限制PWM输出范围
anglePID.SetSampleTime(10); // 控制周期10ms
}
void loop() {
// 读取编码器值并转换为角度(假设减速比10:1)
long encValue = encoder.read();
currentAngle = encValue * (360.0 / (1000.0 * 10.0));
// PID计算
anglePID.Compute();
// 输出PWM控制电机
analogWrite(MOTOR_PWM_PIN, (int)output);
// 调试输出
Serial.print("Target: "); Serial.print(targetAngle);
Serial.print(" Current: "); Serial.print(currentAngle);
Serial.print(" Output: "); Serial.println(output);
delay(10); // 匹配控制周期
}
5、多关节协同控制(双轴)
#include <PID_v1.h>
#include <Encoder.h>
// 关节1配置
#define MOTOR1_PWM_PIN 9
#define ENCODER1_A_PIN 2
#define ENCODER1_B_PIN 3
double Kp1 = 1.0, Ki1 = 0.3, Kd1 = 0.05;
double targetAngle1 = 45.0;
double currentAngle1 = 0.0;
double output1 = 0.0;
Encoder encoder1(ENCODER1_A_PIN, ENCODER1_B_PIN);
PID pid1(¤tAngle1, &output1, &targetAngle1, Kp1, Ki1, Kd1, DIRECT);
// 关节2配置
#define MOTOR2_PWM_PIN 10
#define ENCODER2_A_PIN 4
#define ENCODER2_B_PIN 5
double Kp2 = 1.5, Ki2 = 0.4, Kd2 = 0.08;
double targetAngle2 = 30.0;
double currentAngle2 = 0.0;
double output2 = 0.0;
Encoder encoder2(ENCODER2_A_PIN, ENCODER2_B_PIN);
PID pid2(¤tAngle2, &output2, &targetAngle2, Kp2, Ki2, Kd2, DIRECT);
void setup() {
Serial.begin(115200);
// 关节1初始化
pid1.SetMode(AUTOMATIC);
pid1.SetOutputLimits(-255, 255);
pid1.SetSampleTime(10);
// 关节2初始化
pid2.SetMode(AUTOMATIC);
pid2.SetOutputLimits(-255, 255);
pid2.SetSampleTime(10);
}
void loop() {
// 关节1数据采集
long encValue1 = encoder1.read();
currentAngle1 = encValue1 * (360.0 / (1000.0 * 10.0));
pid1.Compute();
analogWrite(MOTOR1_PWM_PIN, (int)output1);
// 关节2数据采集
long encValue2 = encoder2.read();
currentAngle2 = encValue2 * (360.0 / (1000.0 * 10.0));
pid2.Compute();
analogWrite(MOTOR2_PWM_PIN, (int)output2);
// 调试输出
Serial.print("J1: "); Serial.print(currentAngle1); Serial.print(" ");
Serial.print("J2: "); Serial.println(currentAngle2);
delay(10);
}
6、带前馈补偿的PID控制(抗扰动)
#include <PID_v1.h>
#include <Encoder.h>
// 硬件配置
#define MOTOR_PWM_PIN 9
#define ENCODER_A_PIN 2
#define ENCODER_B_PIN 3
// PID参数
double Kp = 1.5, Ki = 0.6, Kd = 0.2;
double targetAngle = 0.0;
double currentAngle = 0.0;
double output = 0.0;
double lastTarget = 0.0;
double lastTime = 0.0;
const double Kff = 0.3; // 前馈增益(需根据负载调整)
Encoder encoder(ENCODER_A_PIN, ENCODER_B_PIN);
PID anglePID(¤tAngle, &output, &targetAngle, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(115200);
anglePID.SetMode(AUTOMATIC);
anglePID.SetOutputLimits(-255, 255);
anglePID.SetSampleTime(10);
}
void loop() {
// 读取编码器值
long encValue = encoder.read();
currentAngle = encValue * (360.0 / (1000.0 * 10.0));
// 计算目标速度(用于前馈补偿)
double targetVel = (targetAngle - lastTarget) / 0.01; // 假设控制周期10ms
lastTarget = targetAngle;
// PID计算 + 前馈补偿
anglePID.Compute();
double ffOutput = Kff * targetVel; // 惯性前馈
output += ffOutput;
output = constrain(output, -255, 255); // 重新限幅
// 输出PWM
analogWrite(MOTOR_PWM_PIN, (int)output);
// 动态目标生成(正弦波测试)
static unsigned long t = 0;
targetAngle = 90.0 * sin(t * 0.001); // 1Hz正弦波
t += 10;
// 调试输出
Serial.print("Target: "); Serial.print(targetAngle);
Serial.print(" Current: "); Serial.print(currentAngle);
Serial.print(" Output: "); Serial.println(output);
delay(10);
}
要点解读
传感器精度与安装
编码器分辨率直接影响控制精度(如12位编码器提供0.088°分辨率)。
编码器必须安装在关节输出端(而非电机轴),以补偿减速器背隙。例如,案例中通过encValue * (360.0 / (1000.0 * 10.0))将脉冲数转换为实际角度,其中10.0为减速比。
PID参数整定策略
顺序整定:先调电流环(通常由驱动器完成),再调速度环,最后调位置环。案例中位置环PID参数(Kp=1.5, Ki=0.6, Kd=0.2)需根据实际负载调整。
临界比例度法:先设Ki=Kd=0,逐步增大Kp至系统临界振荡,再引入Ki消除稳态误差,最后加Kd抑制超调。
前馈补偿的必要性
纯PID为反馈控制,存在滞后性。案例6通过Kff * targetVel加入前馈项,补偿惯性扰动,显著提升动态响应。例如,在目标角度突变时,前馈可提前输出扭矩,减少启停阶段的超调。
实时性与硬件限制
Arduino Uno(16MHz主频)难以同时运行多轴高频率PID(>1kHz)。案例中通过delay(10)强制控制周期为10ms,但实际建议:
使用ESP32(240MHz)或STM32(480MHz)等高性能MCU。
采用硬件定时器中断(如Timer1.initialize(10000))替代delay(),确保周期稳定性。
抗扰动与安全设计
机械限位:软件中需设置角度边界(如constrain(output, -255, 255)),硬件上增加限位开关。
过流保护:监控驱动器电流反馈,案例中虽未直接实现,但实际需通过analogRead()读取驱动器电流信号,超阈值时触发紧急制动。
热管理:长时间运行需监测电机温度,可通过NTC热敏电阻或驱动器内置温度传感器实现。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

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



所有评论(0)