文章目录
- 前言
- [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参数。