全部版块 我的主页
论坛 数据科学与人工智能 IT基础 C与C++编程
63 0
2025-11-18

深入解读“Arduino BLDC之模糊控制倒立摆”这一系统。这是一次将智能控制策略应用于传统控制难题的成功尝试,它展现了如何利用基于规则的方法解决系统中的非线性和不确定性。

一、主要特点

该系统的特点在于其“模糊逻辑”的控制核心,使得在处理倒立摆问题时表现出独特的属性。

基于规则的非模型控制

核心理念:模糊控制不依赖于受控对象的具体数学模型(例如状态空间方程)。它基于专家的经验或操作者知识,构建了一套“如果-那么”的模糊规则库。

示例规则:“如果 摆杆角度为‘正小’ 并且 角速度为‘负小’,那么 电机输出为‘正大’。”

优点:有效避免了对非线性系统(如倒立摆)进行精准线性化的步骤,对模型不确定性(如负载变化、摩擦系数未知)有天然的适应能力。

处理传感器信息的不准确性

模糊化:控制器将准确的输入信号(如倾角θ=1.5°)转换成模糊语言值(如“正小”),此过程通过隶属度函数来描述。一个输入值可以同时属于多个模糊集合(如70%属于“正小”,30%属于“零”),这更加符合人类对“略微倾斜”这类概念的理解。

优点:能够自然地处理和容忍传感器数据中的噪音和不准确性,增强了系统的稳健性。

启发式设计与强健的鲁棒性

控制器的性能直接受模糊规则库和隶属度函数设计质量的影响。一个设计优秀的模糊控制器能够表现出极高的鲁棒性,即使在参数波动或外部干扰(如轻微风力、对小车的推力)的情况下,也能成功稳定倒立摆。

它通过模拟人类操作员的决策过程,能够以一种“智能化”的方式应对平衡过程中的各种复杂状况。

平滑的非线性控制输出

推理与解模糊化:根据模糊规则,所有激活的规则会并行贡献于最终的控制输出。通过解模糊化方法(如重心法),将这些模糊输出整合为一个精确的控制量(如电机的PWM值)。

优点:这一过程本质上生成了一个平滑的非线性控制规律,避免了传统Bang-Bang控制(开关控制)引起的剧烈震动,使系统运行更加平稳。

二、应用场景

模糊控制倒立摆在教育、研究和某些高性能应用领域具有重要价值。

先进控制理论的教学与验证

作为从传统控制(PID)过渡到智能控制的理想教学案例,生动地展示了模糊逻辑的基本原理和强大功能。

用于对比模糊控制、PID控制、LQR控制在相同受控对象上的表现差异。

机器人平衡技术

两轮自平衡机器人:这是最直接的应用扩展。模糊控制器能够很好地应对载重变化、地面不平等带来的不确定性。

仿人机器人行走:在单脚支撑阶段,身体的稳定可以被视为一个复杂的倒立摆问题,模糊控制可用于姿态稳定。

航空航天领域

火箭姿态控制:火箭在发射初期的垂直上升阶段,其动态特性与倒立摆类似。模糊控制对于处理因燃料消耗导致的质量、质心变化等不确定性具有潜力。

工业过程控制

对于难以建立精确数学模型但人类专家能够熟练操作的复杂工业过程(如化学反应器),模糊控制提供了有效的自动化解决方案。倒立摆是其原理的集中体现。

三、需要注意的事项

实现一个高性能的模糊控制倒立摆系统,其挑战主要集中在控制器的设计与实现上。

模糊控制器设计的艺术性

规则库设计:这是最大的挑战。规则的数量、完整性与一致性直接影响控制效果。规则太少可能导致控制盲区,规则太多或冲突则可能导致推理混乱。设计过程高度依赖于对系统动力学的深刻理解。

隶属度函数设计:隶属度函数的形态(三角形、梯形、高斯形)、重叠程度和论域范围(输入/输出的取值区间)都需要精细调整。这通常是一个繁琐的试错过程,尽管有一定的指导原则,但很大程度上依赖于经验。

系统稳定性分析的难度

与基于Lyapunov稳定性理论的现代控制方法(如LQR)不同,模糊控制系统缺乏统一的、解析的稳定性证明方法。其稳定性通常需要通过大量的仿真和实验来验证,这在一定程度上限制了其在超高安全需求领域的应用。

对传感器数据质量的依赖

虽然模糊控制可以容忍一定程度的不准确,但关键状态必须可测量。倾角和角速度仍是稳定控制的核心。因此,高质量的IMU和有效的传感器融合算法(如互补滤波或Kalman滤波)仍然是必要的,以提供准确、无漂移的倾角估算。

计算负担与实时性

模糊推理过程(包括模糊化、规则评估、聚合和解模糊化)涉及大量的浮点运算和条件判断。

对Arduino的要求:标准的8位Arduino(如Uno)在高速控制循环中可能无法承受,导致循环频率下降,影响稳定性。建议使用更强的32位平台,如Arduino Due、ESP32或STM32。

性能优化的挑战

优化一个模糊控制器比优化一个PID控制器更为复杂,因为可调参数(隶属度函数参数、规则结论)更多,维度更高。

常用自适应模糊控制或与优化算法(如遗传算法、粒子群算法)结合,自动搜索最佳的控制器参数,但这进一步增加了系统的复杂性。

1、基础型模糊倒立摆控制
#include <FuzzyLogic.h>
#include <Servo.h>
// 模糊控制器初始化

FuzzyRuleAntecedent *ifAngleNegative, *ifAngleZero, *ifAnglePositive;
FuzzyRuleConsequent *thenMotorLeft, *thenMotorRight, *thenMotorStop;
// 创建模糊推理系统
FuzzySystem myFuzzySystem;
// 传感器输入
float currentAngle = 0.0;
float angleVelocity = 0.0;
// 执行器输出
Servo motorBase;
void setup() {
Serial.begin(9600);
motorBase.attach(9);
// 定义输入变量隶属度函数
ifAngleNegative = new FuzzyRuleAntecedent();
ifAngleNegative->addMF(new TriangularMF(-30, -30, 0));
ifAngleZero = new FuzzyRuleAntecedent();
ifAngleZero->addMF(new TriangularMF(-30, 0, 30));
ifAnglePositive = new FuzzyRuleAntecedent();
ifAnglePositive->addMF(new TriangularMF(0, 30, 30));
// 定义输出变量隶属度函数
thenMotorLeft = new FuzzyRuleConsequent();
thenMotorLeft->addMF(new TriangularMF(0, 0, 100));
thenMotorStop = new FuzzyRuleConsequent();
thenMotorStop->addMF(new TriangularMF(40, 60, 80));
thenMotorRight = new FuzzyRuleConsequent();
thenMotorRight->addMF(new TriangularMF(80, 100, 100));
// 添加模糊规则
myFuzzySystem.addRule(ifAngleNegative, thenMotorLeft);
myFuzzySystem.addRule(ifAngleZero, thenMotorStop);
myFuzzySystem.addRule(ifAnglePositive, thenMotorRight);
// 设置输入输出范围
myFuzzySystem.setInputRange(-30, 30);
myFuzzySystem.setOutputRange(0, 100);
}
void loop() {
// 读取传感器数据(模拟)
currentAngle = analogRead(A0) * 0.1 - 15.0; // 转换为角度
// 模糊推理
float motorPower = myFuzzySystem.evaluate({currentAngle});
// 控制执行器
motorBase.write(constrain(motorPower, 0, 180));
// 调试输出
Serial.print("Angle: "); Serial.print(currentAngle);
Serial.print(" Motor Power: "); Serial.println(motorPower);
delay(20);
}

案例二:双输入模糊PD控制器
#include <FuzzyLogic.h>
#include <PID_v1.h>
// 双输入模糊PD控制器
class FuzzyPDController {
private:
float lastError = 0;
float integral = 0;
// 模糊变量
float error, derivative;
float output;
// 隶属度函数参数
float mfParams[7][3] = {
{-1.0, -1.0, -0.5}, // NB
{-0.7, -0.5, -0.2}, // NM
{-0.3, -0.2, 0.0}, // NS
{-0.1, 0.0, 0.1}, // ZO
{0.0, 0.2, 0.3}, // PS
{0.2, 0.5, 0.7}, // PM
{0.5, 1.0, 1.0} // PB
};
public:
void compute(float currentAngle, float targetAngle, float dt) {
// 计算误差和导数
```

error = targetAngle - currentAngle;
derivative = (error - lastError) / dt;
lastError = error;
// 标准化输入
float normError = constrain(error / 30.0, -1.0, 1.0);
float normDerivative = constrain(derivative / 10.0, -1.0, 1.0);
// 模糊逻辑推理
float fuzzyOutput = fuzzyInference(normError, normDerivative);
// 反标准化输出
output = fuzzyOutput * 100.0;
}
float fuzzyInference(float e, float d) {
float result = 0;
float totalWeight = 0;
// 迭代所有规则组合
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 7; j++) {
// 计算激活强度
float activation = min(triangularMF(e, mfParams[i][0], mfParams[i][1], mfParams[i][2]),
triangularMF(d, mfParams[j][0], mfParams[j][1], mfParams[j][2]));
// 输出隶属度函数权重
float outputWeight = mfParams[6-i-j][1]; // 对称规则
result += activation * outputWeight;
totalWeight += activation;
}
}
return result / totalWeight;
}
float triangularMF(float x, float a, float b, float c) {
if (x <= a || x >= c) return 0;
if (x == b) return 1;
if (x < b) return (x - a) / (b - a);
return (c - x) / (c - b);
}
float getOutput() { return output; }
};
FuzzyPDController pdController;
void setup() {
Serial.begin(115200);
}
void loop() {
static float lastTime = millis();
float currentTime = millis();
float dt = (currentTime - lastTime) / 1000.0;
lastTime = currentTime;
// 读取传感器数据
float angle = readIMU(); // 需要实现IMU读取功能
float target = 0.0; // 设定目标角度
// 计算控制量
pdController.compute(angle, target, dt);
float controlSignal = pdController.getOutput();
// 控制BLDC电机
controlBLDCMotor(controlSignal);
delay(10);
}
3、自适应模糊滑模控制
#include <Wire.h>
#include <Adafruit_MMA8451.h>
#include <Adafruit_Sensor.h>
// 自适应模糊滑模控制器
class AdaptiveFuzzySMC {
private:
Adafruit_MMA8451 accel;
float slipSurface = 0.1; // 滑模面系数
float adaptiveRate = 0.01; // 自适应速率
// 模糊系统参数
float theta[5] = {0}; // 调节参数
float phi[5] = {0};   // 基础函数
public:
void begin() {
if (!accel.begin()) {
Serial.println("无法启动MMA8451!");
while (1);
}
}
float computeControl(float desiredAngle, float currentAngle, float velocity) {
// 计算滑模面
float s = currentAngle - desiredAngle + velocity;
// 更新模糊系统参数
    
updateParameters(s, velocity);
// 计算控制输出
float u = calculateControlOutput(s, velocity);
return u;
}
void updateParameters(float s, float velocity) {
// 自适应规律
float gradient = s * calculatePhi(velocity);
for (int i = 0; i < 5; i++) {
theta[i] += adaptiveRate * gradient * phi[i];
}
}
float calculatePhi(float x) {
// 高斯隶属度函数作为基函数
float centers[5] = {-2, -1, 0, 1, 2};
float spread = 0.5;
for (int i = 0; i < 5; i++) {
phi[i] = exp(-pow((x - centers[i]) / spread, 2));
}
return phi[0]; // 返回首个基函数值
}
float calculateControlOutput(float s, float velocity) {
float control = 0;
// 模糊推理
for (int i = 0; i < 5; i++) {
control += theta[i] * phi[i];
}
// 增加切换控制项
control += 0.5 * sign(s);
return control;
}
int sign(float x) {
return (x > 0) ? 1 : ((x < 0) ? -1 : 0);
}
};
AdaptiveFuzzySMC smcController;
void setup() {
Serial.begin(115200);
smcController.begin();
}
void loop() {
// 获取IMU数据
sensors_event_t event;
smcController.accel.getEvent(&event);
float angle = atan2(event.acceleration.y, event.acceleration.x) * RAD_TO_DEG;
float velocity = estimateVelocity(angle); // 需要实现速度估计
// 计算控制量
float controlSignal = smcController.computeControl(0.0, angle, velocity);
// 控制BLDC电机
analogWrite(9, abs(controlSignal));
setMotorDirection(sign(controlSignal));
delay(10);
}
要点解读:
隶属度函数设计原则:
三角型隶属度函数计算简便,适用于嵌入式系统
覆盖整个论域且部分重叠(通常重叠25%-50%)
边缘区域使用梯形或半梯形函数增加稳健性
输入输出尺度转换因子需依据系统动态调整
规则库构建策略:
基于物理模型建立初始规则(牛顿力学方程)
借助专家知识补充特殊情况规则
确保规则的完整度(完备性和相容性)
采用对称规则结构减少计算复杂度
推理机制优化:
最小值法(min)代替乘积法(prod)减少运算量
重心法(COG)解模糊得到连续输出
查表法取代实时计算提高响应速率
分区预存推理结果降低内存消耗
稳定性保障措施:
引入李亚普诺夫稳定准则设计自适应规律
滑模控制提升系统稳健性
死区补偿消除零点振动
饱和限制防止积分 windup 现象
参数整定技巧:
先用PID确定基础参数区间
通过试错法调整模糊规则权重
利用遗传算法优化隶属度函数参数
结合系统辨识技术在线更新控制器参数

4、基础模糊控制器(单输入单输出)
#include <Fuzzy.h>  // 使用FuzzyLite库或自定义模糊库
// 定义模糊集参数
#define ANGLE_NB -30   // 负大
#define ANGLE_NS -10   // 负小
#define ANGLE_Z 0      // 零
#define ANGLE_PS 10    // 正小
#define ANGLE_PB 30    // 正大
#define OUTPUT_NB -255 // 负大输出
#define OUTPUT_NS -100 // 负小输出
#define OUTPUT_Z 0     // 零输出
#define OUTPUT_PS 100  // 正小输出
#define OUTPUT_PB 255  // 正大输出

Fuzzy* fuzzy = new Fuzzy();

void setup() {

Serial.begin(9600);

// 1. 建立输入变量(摆杆角度)

FuzzyInput* angle = new FuzzyInput(1);

angle->addTerm(new FuzzySet("NB", ANGLE_NB, ANGLE_NB, ANGLE_NS, ANGLE_Z));

angle->addTerm(new FuzzySet("NS", ANGLE_NS, ANGLE_Z, ANGLE_Z, ANGLE_PS));

angle->addTerm(new FuzzySet("Z", ANGLE_Z, ANGLE_PS, ANGLE_NS, ANGLE_PS));

angle->addTerm(new FuzzySet("PS", ANGLE_PS, ANGLE_Z, ANGLE_Z, ANGLE_NS));

angle->addTerm(new FuzzySet("PB", ANGLE_Z, ANGLE_PB, ANGLE_PB, ANGLE_PB));

fuzzy->addFuzzyInput(angle);

// 2. 建立输出变量(BLDC控制量)

FuzzyOutput* output = new FuzzyOutput(1);

output->addTerm(new FuzzySet("NB", OUTPUT_NB, OUTPUT_NB, OUTPUT_NS, OUTPUT_Z));

output->addTerm(new FuzzySet("NS", OUTPUT_NS, OUTPUT_Z, OUTPUT_Z, OUTPUT_PS));

output->addTerm(new FuzzySet("Z", OUTPUT_Z, OUTPUT_PS, OUTPUT_NS, OUTPUT_PS));

output->addTerm(new FuzzySet("PS", OUTPUT_PS, OUTPUT_Z, OUTPUT_Z, OUTPUT_NS));

output->addTerm(new FuzzySet("PB", OUTPUT_Z, OUTPUT_PB, OUTPUT_PB, OUTPUT_PB));

fuzzy->addFuzzyOutput(output);

// 3. 设置模糊规则

FuzzyRule* rules[] = {

new FuzzyRule(1, new FuzzyCondition(angle, "NB"), new FuzzyConclusion(output, "PB")),

new FuzzyRule(2, new FuzzyCondition(angle, "NS"), new FuzzyConclusion(output, "PS")),

new FuzzyRule(3, new FuzzyCondition(angle, "Z"), new FuzzyConclusion(output, "Z")),

new FuzzyRule(4, new FuzzyCondition(angle, "PS"), new FuzzyConclusion(output, "NS")),

new FuzzyRule(5, new FuzzyCondition(angle, "PB"), new FuzzyConclusion(output, "NB"))

};

for (auto rule : rules) fuzzy->addFuzzyRule(rule);

}

void loop() {

// 模拟读取角度感应器(实际应用中应连接MPU6050等设备)

float angle = getPendulumAngle();

// 输入模糊化

fuzzy->setInput(1, angle);

fuzzy->evaluate();

// 输出解模糊化

int pwmOutput = fuzzy->getOutput(1);

// 调节BLDC(假设采用SimpleFOC库)

controlBLDC(pwmOutput);

delay(10); // 控制间隔

}

// 模拟函数(实际操作中需替换为感应器数据读取)

float getPendulumAngle() {

static float time = 0;

time += 0.01;

return 5 * sin(time); // 模拟正弦波干扰

}

void controlBLDC(int pwm) {

// 实际BLDC控制代码(例如analogWrite或SimpleFOC接口)

}

Serial.print("Angle: "); Serial.print(getPendulumAngle());
Serial.print(" -> PWM: "); Serial.println(pwm);

5、双变量模糊控制(角度+角速度)
#include <Fuzzy.h>
// 扩展输入:角速度(单位:度/秒)
#define RATE_NB -100
#define RATE_NS -30
#define RATE_Z 0
#define RATE_PS 30
#define RATE_PB 100
Fuzzy* fuzzy = new Fuzzy();
void setup() {
// 初始化角度输入(如同示例一)
FuzzyInput* angle = new FuzzyInput(1);
// ...添加角度模糊集...
// 新增角速度输入
FuzzyInput* rate = new FuzzyInput(2);
rate->addTerm(new FuzzySet("NB", RATE_NB, RATE_NB, RATE_NS, RATE_Z));
rate->addTerm(new FuzzySet("NS", RATE_NS, RATE_Z, RATE_Z, RATE_PS));
rate->addTerm(new FuzzySet("Z", RATE_Z, RATE_PS, RATE_NS, RATE_PS));
rate->addTerm(new FuzzySet("PS", RATE_PS, RATE_Z, RATE_Z, RATE_NS));
rate->addTerm(new FuzzySet("PB", RATE_Z, RATE_PB, RATE_PB, RATE_PB));
fuzzy->addFuzzyInput(rate);
// 输出维持不变
FuzzyOutput* output = new FuzzyOutput(1);
// ...添加输出模糊集...
// 强化规则(考量角速度)
FuzzyRule* rules[] = {
// 规则1:正向大角度且正向角速度 -> 显著负控制
new FuzzyRule(1,
new FuzzyConditionAnd(
new FuzzyCondition(angle, "PB"),
new FuzzyCondition(rate, "PB")
),
new FuzzyConclusion(output, "NB")
),
// 规则2:正向小角度且负向角速度 -> 轻微正控制
new FuzzyRule(2,
new FuzzyConditionAnd(
new FuzzyCondition(angle, "PS"),
new FuzzyCondition(rate, "NB")
),
new FuzzyConclusion(output, "PS")
),
// ...添加更多组合规则(共计25条可能组合)...
};
for (auto rule : rules) fuzzy->addFuzzyRule(rule);
}
void loop() {
float angle = getPendulumAngle();
float rate = getAngularRate(); // 新增角速度读取
fuzzy->setInput(1, angle);
fuzzy->setInput(2, rate);
fuzzy->evaluate();
int pwm = fuzzy->getOutput(1);
controlBLDC(pwm);
}
// 模拟角速度计算(实际上应采用陀螺仪数据)
float getAngularRate() {
static float lastAngle = 0;
float currentAngle = getPendulumAngle();
float rate = (currentAngle - lastAngle) * 100; // 放大差异
lastAngle = currentAngle;
return rate;
}

6、自调整模糊控制器(在线参数优化)
#include <Fuzzy.h>
#include <EEPROM.h>
// 可调节参数
struct FuzzyParams {
float angleScale;
float rateScale;
float outputScale;
int ruleWeights[25]; // 假定25条规则
};
FuzzyParams params;
void loadParams() {

EEPROM.get(0, params); // 从EEPROM读取存储的参数
if (isnan(params.angleScale)) {
// 预设参数
params.angleScale = 1.0;
params.rateScale = 1.0;
params.outputScale = 1.0;
for (int i=0; i<25; i++) params.ruleWeights[i] = 100;
}
}
void adjustParams(float error) {
// 简易自适应策略:依据偏差调节参数
if (fabs(error) > 15) {
params.angleScale *= 1.05; // 提高角度灵敏度
params.ruleWeights[0] += 5; // 加强"大角度大控制"规则
} else if (fabs(error) < 5) {
params.outputScale *= 0.95; // 减少控制力度
}
// 限定参数范围
params.angleScale = constrain(params.angleScale, 0.5, 2.0);
params.outputScale = constrain(params.outputScale, 0.5, 1.5);
EEPROM.put(0, params); // 存储参数
}
void setup() {
loadParams();
// ...初始化模糊系统(如同案例二,但应用params中的缩放系数)...
}
void loop() {
static float lastAngle = 0;
float angle = getPendulumAngle() * params.angleScale;
float rate = getAngularRate() * params.rateScale;
// 计算偏差(用于自适应)
float error = angle - lastAngle;
lastAngle = angle;
// 模糊控制
fuzzy->setInput(1, angle);
fuzzy->setInput(2, rate);
fuzzy->evaluate();
int rawOutput = fuzzy->getOutput(1);
// 施加输出缩放
int pwm = rawOutput * params.outputScale;
controlBLDC(pwm);
// 实时调整
adjustParams(error);
delay(5); // 较短的控制间隔
}
要点解析
输入变量选取
单变量(角度)适用于简易系统,双变量(角度+角速度)能增强动态响应(案例一对比案例二)
可扩展至三变量(例如增加车辆位置)以实现Cart-Pole系统
模糊集设计建议
三角形模糊集适合实时应用(计算负担轻)
建议隶属函数重叠30-50%(例如NS与Z的交界区)
输出模糊集应覆盖BLDC的PWM全程(-255到255)
解模糊技术选择
重心法(centroid)输出平稳但计算密集
最大隶属度法(mom)适合资源有限的系统(本例未明确指定,通常默认为重心法)
实时性能优化
推荐控制周期5-20ms(案例三采用5ms)
避免在loop()中执行浮点运算(可以转用定点数)
预编译规则库(将规则条件转化为查找表)
系统整合关键点
传感器融合:MPU6050的DMP数据需经过滤波处理(比如互补滤波)
BLDC驱动:推荐使用FOC控制(例如SimpleFOC库)而不是直接PWM
安全措施:设置角度阈值保护(例如超过45度时停止驱动)
请注意,上述示例旨在拓宽思路,仅供参考。可能存在错误、不适合或无法编译的情况。您的硬件平台、应用场景和Arduino版本可能会影响方法的选择。实际编码时,应根据自身硬件配置、应用场景和特定需求进行调整,并进行多次实地测试。同时确保硬件连接正确,熟悉所用传感器和设备的标准和特点。涉及硬件操作的代码,在使用前务必验证引脚和电压等参数的准确性和安全性。

    
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

栏目导航
热门文章
推荐文章

说点什么

分享

扫码加好友,拉您进群
各岗位、行业、专业交流群