主题
🧮 算法篇 · 电赛单片机不翻页速通手册
🧮 算法篇 — 常用控制算法速通:PID、互补滤波、平滑滤波、滑动窗口、状态机。
PID
8:18【算法·PID】不翻页!速通!电赛教程
java
// 示例伪代码
// 定义PID计算所需变量
float setpoint = 0; // 目标设定值
float measured_value = 0; // 当前测量值
float error = 0; // PID输入的误差值 = 目标值 - 测量值
float integral = 0; // 误差积分项
float last_error = 0; // 上次误差值
float Compensation = 0; // PID输出的补偿值
float output = 0; // 控制器输出
// PID系数(需根据实际系统调整)
float Kp = 1.0; // 比例系数
float Ki = 0.01; // 积分系数
float Kd = 0.1; // 微分系数
// PID计算函数(需周期性调用)
void PID_Compute() {
// 1. 计算当前误差
error = setpoint - measured_value;
// 2. 计算PID三项
float proportional = Kp * error; // 比例项:立即响应误差
integral += Ki * error; // 积分项:消除稳态误差
float derivative = Kd * (error - last_error); // 微分项:抑制超调
// 3. 合成输出
output = proportional + integral + derivative;
// 4. 保存当前误差(用于下次微分计算)
last_error = error;
// 5. (可选)输出限幅
if(output > max_output) output = max_output;
if(output < min_output) output = min_output;
}
// 主函数
int main() {
while(1) {
measured_value = read_sensor(); // 获取传感器数据
PID_Compute(); // 计算控制量
set_actuator(output,Compensation); // 输出控制信号
delay(20); // 20ms控制周期
}
}互补滤波
7:22【算法·互补滤波】不翻页!速通!电赛教程
java
// 示例伪代码
// 初始化
float angle = 0.0f; // 初始角度估计值
constfloat alpha = 0.98f; // 滤波系数
constfloat RAD_TO_DEG = 57.29578f; // 弧度转角度
while(1) {
// 1. 获取传感器数据(伪函数)
floataccelY = read_accelY(); // Y轴加速度值
floataccelZ = read_accelZ(); // Z轴加速度值
floatgyroX = read_gyroX(); // X轴角速度值
float dt = 0.02f; // 采样周期20ms
// 2. 加速度计计算角度(低频)
floataccelAngle = atan2f(accelY, accelZ) * RAD_TO_DEG;
// 3. 陀螺仪计算角度变化(高频)
floatgyroDelta = gyroX * dt;
// 4. 互补滤波融合(核心计算)
angle = alpha * (angle + gyroDelta) + (1.0f - alpha) * accelAngle;
// 5. 使用最终角度(示例)
set_actuator(angle);
delay_ms(20); // 保持固定采样周期
}平滑滤波
4:44只有一行代码却极强的平滑滤波算法
c++
// 示例伪代码
// 初始化
float filteredValue = 0.0f; // 初始滤波值 u(t-1)
const float alpha = 0.2f; // 滤波系数 α (0~1之间)
while(1) {
// 1. 获取当前时刻的原始采样数据(伪函数)
float currentSample = read_sensor(); // 当前采样值 x(t)
float dt = 0.02f; // 采样周期20ms
// 2. EMA滤波核心计算
// u(t) = α × x(t) + (1 - α) × u(t-1)
filteredValue = alpha * currentSample + (1.0f - alpha) * filteredValue;
// 3. 使用滤波后的值(示例)
process_data(filteredValue);
// 4. 为下一次迭代准备:u(t) 自动成为下一次的 u(t-1)
delay_ms(20);
// 保持固定采样周期
}滑动窗口
5:43数据最简单高效处理:滑动窗口
c++
// 示例伪代码
// 循环数组定义
#define BUFFER_SIZE 6
int dataBuffer[BUFFER_SIZE]; // 数据缓冲区
int writeIndex = 0; // 写入位置索引
int count = 0; // 当前数据数量
// 每一次向循环数组存入新数据
void AddDataToBuffer(int newData) {
// 将新数据存入当前写入位置
dataBuffer[writeIndex] = newData;
// 更新写入索引(循环)
writeIndex = (writeIndex + 1) % BUFFER_SIZE;
// 更新数据计数(不超过缓冲区大小)
if (count < BUFFER_SIZE) {
count++;
}
}状态机
9:21超过30行程序的必用算法:状态机
c++
// 示例伪代码
// 状态定义:
// 0=初始化(S0), 1=第一次按下(S1), 2=第一次释放(S2), 3=第二次按下(S3)
int state = 0; // 事件处理函数
// 输入参数:eventType - 0表示按下事件,1表示释放事件 。
// 定时中断50ms触发
void KeyFSM_HandleEvent(int eventType) {
switch(state) {
case 0: // S0状态 - 初始化
if (eventType == 0) { // 按下事件
printf("已按下\n");
state = 1;
}
break;
case 1: // S1状态 - 第一次按下
if (eventType == 1) { // 释放事件
state = 2; // 此时开始记录施放时间 releaseTime
}
break;
case 2: // S2状态 - 第一次释放
if (eventType == 0) { // 按下事件
printf("双击\n");
state = 3;
}
else if (releaseTime >= 500) { // 超过规定间隔500ms
printf("单击\n");
state = 0;
}
break;
case 3: // S3状态 - 第二次按下
if (eventType == 1) { // 释放事件
state = 0;
}
break;
}
}