1. 项目背景与核心需求
毫米波雷达在智能家居、工业检测和自动驾驶等领域应用越来越广泛,R60AFD1作为一款性价比极高的24GHz雷达模块,配合STM32F103这款经典MCU,可以快速搭建各种检测系统。我在最近一个安防项目中需要实现人体存在检测功能,经过对比选择了这套方案。
选择STM32F103C8T6主要考虑三点:首先是价格优势,作为"蓝屏小钢炮"其性价比在基础控制场景依然能打;其次是丰富的外设资源,3个USART和2个SPI接口完全满足雷达数据通信需求;最后是成熟的生态,遇到问题容易找到解决方案。而R60AFD1模块的优势在于其数字输出接口和内置的信号处理算法,开发者无需处理原始射频信号就能获取目标信息。
2. 硬件设计与接口连接
2.1 硬件连接示意图
R60AFD1模块采用4线制连接:
- VCC: 接3.3V电源
- GND: 共地连接
- TX: 接STM32的PA10(USART1_RX)
- RX: 接STM32的PA9(USART1_TX)
注意:虽然模块支持5V供电,但建议使用3.3V以避免电平转换问题。我在首次测试时使用5V供电导致通信不稳定,后来发现是电平匹配问题。
2.2 电源设计要点
采用AMS1117-3.3为系统供电时需注意:
- 输入电容10μF/16V电解电容
- 输出电容22μF/6.3V陶瓷电容
- 在模块电源引脚就近放置0.1μF去耦电容
实测发现,电源纹波控制在50mV以内时,雷达检测稳定性最佳。当使用开关电源时,建议增加π型滤波电路。
3. 通信协议解析与实现
3.1 数据帧结构分析
R60AFD1采用9600bps波特率,8数据位,无校验位,1停止位。每个数据包包含7字节:
| 字节位置 | 含义 | 说明 |
|---|---|---|
| Byte0 | 帧头 | 固定0xAA |
| Byte1 | 目标状态 | 0x01表示有目标 |
| Byte2 | 距离高字节 | 单位:厘米 |
| Byte3 | 距离低字节 | |
| Byte4 | 信号强度 | 0-255,值越大信号越强 |
| Byte5 | 校验和 | Byte1-Byte4的累加和 |
| Byte6 | 帧尾 | 固定0x55 |
3.2 STM32串口配置代码
c复制void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// TX(PA9)配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// RX(PA10)配置为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
4. 数据解析算法实现
4.1 状态机设计
采用状态机模式解析数据帧:
c复制typedef enum {
FRAME_HEADER,
TARGET_STATUS,
DISTANCE_HIGH,
DISTANCE_LOW,
SIGNAL_STRENGTH,
CHECK_SUM,
FRAME_TAIL
} ParserState;
typedef struct {
uint8_t status;
uint16_t distance;
uint8_t strength;
} RadarData;
4.2 完整解析函数
c复制void ParseRadarData(uint8_t byte, RadarData* data)
{
static ParserState state = FRAME_HEADER;
static uint8_t checksum = 0;
static uint8_t buffer[5];
switch(state) {
case FRAME_HEADER:
if(byte == 0xAA) {
state = TARGET_STATUS;
checksum = 0;
}
break;
case TARGET_STATUS:
buffer[0] = byte;
checksum += byte;
state = DISTANCE_HIGH;
break;
case DISTANCE_HIGH:
buffer[1] = byte;
checksum += byte;
state = DISTANCE_LOW;
break;
case DISTANCE_LOW:
buffer[2] = byte;
checksum += byte;
state = SIGNAL_STRENGTH;
break;
case SIGNAL_STRENGTH:
buffer[3] = byte;
checksum += byte;
state = CHECK_SUM;
break;
case CHECK_SUM:
if(byte == checksum) {
state = FRAME_TAIL;
} else {
state = FRAME_HEADER; // 校验失败重置
}
break;
case FRAME_TAIL:
if(byte == 0x55) {
// 完整帧接收成功
data->status = buffer[0];
data->distance = (buffer[1] << 8) | buffer[2];
data->strength = buffer[3];
}
state = FRAME_HEADER;
break;
}
}
5. 实际应用中的优化技巧
5.1 抗干扰处理
在实际环境中会遇到各种干扰,通过以下方法提升稳定性:
- 连续3次检测到目标才判定为有效
- 设置信号强度阈值(建议>30)
- 对距离值进行滑动平均滤波
c复制#define FILTER_WINDOW 5
uint16_t distanceFilter(uint16_t newValue)
{
static uint16_t values[FILTER_WINDOW] = {0};
static uint8_t index = 0;
static uint32_t sum = 0;
sum -= values[index];
values[index] = newValue;
sum += newValue;
index = (index + 1) % FILTER_WINDOW;
return sum / FILTER_WINDOW;
}
5.2 低功耗设计
对于电池供电的应用场景:
- 设置雷达模块工作周期(如200ms工作,800ms休眠)
- 利用STM32的停止模式降低功耗
- 通过外部中断唤醒系统
c复制void EnterLowPowerMode(void)
{
// 配置PA0为外部中断唤醒源
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 进入停止模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
// 唤醒后需要重新配置系统时钟
SystemInit();
}
6. 常见问题与解决方案
6.1 通信不稳定问题排查
-
现象:数据包接收不完整或频繁出错
- 检查电源纹波(示波器观察3.3V电源线)
- 确认波特率误差(STM32的APB2时钟需正确配置)
- 检查接线长度(建议不超过20cm)
-
现象:检测距离明显缩短
- 检查天线是否完好(无物理损伤)
- 测量供电电压(不低于3.0V)
- 检查环境干扰(避免金属物体靠近天线)
6.2 性能优化记录
在停车场车辆检测项目中,通过以下优化将误报率从15%降到2%:
- 将检测阈值从默认的20提高到35
- 增加距离变化率检测(突然出现的目标才响应)
- 在软件中加入10秒的"锁定"机制,避免频繁触发
c复制#define DYNAMIC_THRESHOLD 50 // cm/s
uint8_t isRealTarget(RadarData* current, RadarData* previous, uint32_t intervalMs)
{
float speed = 0;
if(previous->distance > 0) {
speed = abs(current->distance - previous->distance) * 1000.0 / intervalMs;
}
return (current->strength > 35) && (speed < DYNAMIC_THRESHOLD);
}
7. 项目扩展与进阶应用
7.1 多雷达组网方案
通过USART2和USART3连接多个雷达模块,实现区域覆盖:
- 为每个雷达分配独立ID
- 采用轮询方式读取数据
- 在应用层进行数据融合
c复制typedef struct {
uint8_t id;
RadarData data;
uint32_t timestamp;
} NetworkNode;
void PollRadarNetwork(NetworkNode* nodes, uint8_t count)
{
for(uint8_t i=0; i<count; i++) {
USART_SendData(nodes[i].id); // 发送雷达ID
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
// 接收数据...
nodes[i].timestamp = GetSystemTick();
}
}
7.2 与云平台对接
通过ESP8266模块将数据上传至云平台:
- 定义JSON数据格式
- 每5秒上传一次数据
- 加入设备认证信息
c复制void UploadToCloud(RadarData* data)
{
char json[128];
sprintf(json, "{\"dev\":\"RADAR_001\",\"stat\":%d,\"dist\":%d,\"str\":%d}",
data->status, data->distance, data->strength);
ESP8266_Send("AT+CIPSEND=0,%d\r\n", strlen(json));
ESP8266_Send(json);
}
在实际部署中发现,当检测到目标状态变化时立即上传(而不是定时上传),可以使云平台响应更及时。同时加入本地缓存机制,在网络中断时能保存最近10条记录。