嵌入式硬件篇---超声波|PID


文章目录

  • 前言
  • [1. 硬件准备](#1. 硬件准备)
  • [2. 硬件连接](#2. 硬件连接)
  • [3. 超声波测距原理](#3. 超声波测距原理)
  • [4. 代码实现(基于HAL库)](#4. 代码实现(基于HAL库))
    • [(1) 初始化定时器(用于测量Echo高电平时间)](#(1) 初始化定时器(用于测量Echo高电平时间))
    • [(2) 超声波触发与距离计算](#(2) 超声波触发与距离计算)
  • [5. PID算法实现](#5. PID算法实现)
    • [(1) PID结构体定义](#(1) PID结构体定义)
    • [(2) PID计算函数](#(2) PID计算函数)
  • [6. 控制执行机构](#6. 控制执行机构)
    • [(1) 电机控制(PWM调速)](#(1) 电机控制(PWM调速))
    • [(2) 舵机控制(角度调整)](#(2) 舵机控制(角度调整))
  • [7. 主循环逻辑](#7. 主循环逻辑)
  • [8. PID参数整定技巧](#8. PID参数整定技巧)
  • [9. 关键问题与优化](#9. 关键问题与优化)

前言

STM32F103RCT6 上使用超声波传感器(如HC-SR04)结合PID算法 实现稳定距离控制 ,通常用于避障、跟随或定位等场景。以下是详细实现步骤:


1. 硬件准备

主控芯片

主控芯片:STM32F103RCT6(72MHz Cortex-M3,足够处理超声波数据与PID运算)。

超声波模块

超声波模块:HC-SR04(测距范围2cm~400cm,精度±3mm)。

执行机构

执行机构:电机(如直流电机+编码器)或舵机(用于调整方向)。

其他

其他:电源、驱动电路(如L298N)、OLED/LCD(可选,用于显示距离)。

2. 硬件连接

HC-SR04引脚 STM32引脚 说明

VCC 5V 模块供电

Trig PA1 触发信号(输出)

Echo PA0 回波信号(输入,需EXTI中断)

GND GND 共地

注意:Echo信号为5V电平,需通过**电阻分压(如1kΩ+2kΩ)**转换为3.3V,避免损坏STM32。

3. 超声波测距原理

触发信号

触发信号:STM32发送10μs的高电平脉冲到Trig引脚。

回波检测

回波检测:HC-SR04发射超声波并接收回波,Echo引脚输出高电平,

持续时间与距离成正比(公式:距离(cm) = 高电平时间(μs) / 58)。

中断捕获

中断捕获:使用定时器输入捕获模式或EXTI中断+普通定时器测量Echo高电平时间。

4. 代码实现(基于HAL库)

(1) 初始化定时器(用于测量Echo高电平时间)

c 复制代码
// 定时器2初始化(1MHz计数频率,72MHz/72分频)
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void) {
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 71;          // 72分频
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0xFFFF;         // 最大计数值
  HAL_TIM_IC_Init(&htim2);
}

// 输入捕获配置(通道1,PA0)
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 上升沿触发
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

(2) 超声波触发与距离计算

c 复制代码
float Get_Distance(void) {
  uint32_t start_time = 0, end_time = 0;
  float distance_cm = 0;

  // 发送10us触发脉冲
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
  delay_us(10);
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);

  // 等待Echo上升沿(启动定时器)
  while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
  HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_1);
  start_time = htim2.Instance->CCR1;

  // 等待Echo下降沿(停止定时器)
  while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
  end_time = htim2.Instance->CCR1;
  HAL_TIM_IC_Stop(&htim2, TIM_CHANNEL_1);

  // 计算距离(单位:cm)
  distance_cm = (end_time - start_time) / 58.0f;
  return distance_cm;
}

5. PID算法实现

PID控制器通过调节输出使当前距离(反馈值)趋近目标距离(设定值)。

(1) PID结构体定义

c 复制代码
typedef struct {
  float Kp, Ki, Kd;      // PID参数
  float target;          // 目标距离(cm)
  float error, last_error, integral;
} PID_Controller;

PID_Controller pid = {0.5, 0.01, 0.2, 10.0, 0, 0, 0}; // 示例参数

(2) PID计算函数

c 复制代码
float PID_Update(PID_Controller *pid, float current) {
  pid->error = pid->target - current;  // 误差=目标值-当前值

  // 积分项(防饱和)
  pid->integral += pid->error;
  if (pid->integral > 100) pid->integral = 100;
  else if (pid->integral < -100) pid->integral = -100;

  // 微分项
  float derivative = pid->error - pid->last_error;
  pid->last_error = pid->error;

  // PID输出
  float output = pid->Kp * pid->error + 
                 pid->Ki * pid->integral + 
                 pid->Kd * derivative;
  return output;
}

6. 控制执行机构

根据PID输出调整电机或舵机:

(1) 电机控制(PWM调速)

c 复制代码
// 初始化PWM(TIM3通道1,PA6)
void MX_TIM3_Init(void) {
  TIM_OC_InitTypeDef sConfigOC = {0};
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 71;          // 1MHz频率
  htim3.Init.Period = 999;             // 1kHz PWM
  HAL_TIM_PWM_Init(&htim3);
  sConfigOC.Pulse = 0;                // 初始占空比0
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

// 更新电机速度(PID输出映射到PWM占空比)
void Set_Motor_Speed(float pid_output) {
  uint16_t pwm = (uint16_t)fabs(pid_output * 10); // 比例缩放
  if (pwm > 999) pwm = 999;
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm);

  // 根据输出正负设置电机方向
  if (pid_output > 0) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);   // 正转
  } else {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 反转
  }
}

(2) 舵机控制(角度调整)

c 复制代码
// 设置舵机角度(PID输出映射到0~180°)
void Set_Servo_Angle(float pid_output) {
  uint16_t angle = 90 + (uint16_t)(pid_output * 10); // 示例映射
  uint16_t pwm = 500 + angle * 2000 / 180;           // 0.5ms~2.5ms
  __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, pwm);
}

7. 主循环逻辑

c 复制代码
int main(void) {
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();

  float current_distance = 0;
  float pid_output = 0;

  while (1) {
    current_distance = Get_Distance();      // 获取当前距离
    pid_output = PID_Update(&pid, current_distance);
    Set_Motor_Speed(pid_output);           // 调整电机速度
    // 或 Set_Servo_Angle(pid_output);     // 调整舵机角度

    HAL_Delay(50); // 控制周期50ms(20Hz)
  }
}

8. PID参数整定技巧

先调P

先调P:增大Kp直到系统快速响应但出现振荡。

再调D

再调D:加入Kd抑制振荡。

最后调I

最后调I:小幅增加Ki消除静态误差。

测试

测试场景:目标距离从远到近阶跃变化,观察响应曲线。

9. 关键问题与优化

超声波噪声

超声波噪声:多次采样取中值滤波

死区处理

死区处理:当误差小于阈值关闭PID输出,避免抖动。

动态目标

动态目标:若目标距离变化,需重新整定PID参数

通过上述步骤,STM32F103RCT6可稳定控制物体与超声波传感器间的距离。实际应用中需根据机械特性(如电机惯性)调整PID参数。


相关推荐
mini_nine2 小时前
DSP28335 串口中断收发及FIFO使用
单片机·dsp开发
走错路的程序员4 小时前
stm32测频率占空比最好的方案
stm32·单片机·嵌入式硬件
Ronin-Lotus6 小时前
嵌入式硬件篇---SPI
单片机·嵌入式硬件
白天学嵌入式6 小时前
STM32f103 标准库 零基础学习之按键点灯(不涉及中断)
stm32·单片机·学习
Ronin-Lotus7 小时前
嵌入式硬件篇---陀螺仪|PID
单片机·嵌入式硬件
小智学长 | 嵌入式7 小时前
单片机-STM32部分:12、I2C
单片机·嵌入式硬件
四夕白告木贞7 小时前
stm32week15
stm32·单片机·嵌入式硬件·学习
Ronin-Lotus8 小时前
嵌入式硬件篇---TOF|PID
单片机·嵌入式硬件·c·pid·tof
摞代码的猴哥9 小时前
单片机调用printf概率性跑飞解决方法
单片机·printf·ucos·跑飞
weixin_452813099 小时前
如何根据HardFault中断抛出的寄存器值排查数组越界
单片机·嵌入式硬件·嵌入式软件