跳到内容

🧮 算法篇 · 电赛单片机不翻页速通手册

🧮 算法篇 — 常用控制算法速通:PID、互补滤波、平滑滤波、滑动窗口、状态机。

← 返回整体说明 · 🔧 功能篇 · 📦 模块篇


PID

【算法·PID】不翻页!速通!电赛教程8:18

【算法·PID】不翻页!速通!电赛教程

电导不是韩导9623播放

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

【算法·互补滤波】不翻页!速通!电赛教程

电导不是韩导1.0万播放

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

只有一行代码却极强的平滑滤波算法

电导不是韩导3901播放

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

数据最简单高效处理:滑动窗口

电导不是韩导1536播放

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++;
    }
}

状态机

超过30行程序的必用算法:状态机9:21

超过30行程序的必用算法:状态机

电导不是韩导5207播放

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;
  }
}