
自动巡逻安防智能小车
简介
基于STM32F407VET6与K230视觉模块的智能巡逻小车,集成DHT11温湿度、HC-SR04超声波避障、SU03T语音交互、AT8236电机驱动及蓝牙遥控,支持自动巡航与手动遥控双模式。
简介:基于STM32F407VET6与K230视觉模块的智能巡逻小车,集成DHT11温湿度、HC-SR04超声波避障、SU03T语音交互、AT8236电机驱动及蓝牙遥控,支持自动巡航与手动遥控双模式。开源协议
:GPL 3.0
(未经作者授权,禁止转载)描述
智能巡逻监护小车
题目要求
本作品为基于STM32F407VET6的智能巡逻监护小车,实现功能如下:
- 实时采集环境温湿度数据
- 切换到自动模式时:通过超声波模块小车自助巡航主动避让
- K230识别老人跌倒,老人呼叫APP语言提示
- 视频数据回传手机(备用ESP32CSAM)
- 切换到手动模式:小车可以通过手机遥控操作
- 语音交互功能
- 跟随功能(人体跟踪)
题目分析
本项目以STM32F407VET6作为主控核心,选型理由在于其拥有丰富的外设接口(多路UART、定时器、PWM输出),能够同时驱动多个传感与执行模块。系统按功能可分为四个层次:感知层(DHT11温湿度、HC-SR04超声波、电压采集)、视觉与交互层(K230视觉模块、SU03T语音模块、按键、OLED)、通信层(蓝牙JDY-31、备用ESP32CSAM WiFi视频回传)、执行层(AT8236电机驱动、SG90舵机、蜂鸣器)。
技术难点主要在于多传感器数据的融合调度与实时性保障。K230视觉模块的人体检测与跌倒识别需要在有限算力下运行推理模型,而超声波避障与自动驾驶模式则依赖PID闭环控制算法实现平稳的差速转向。蓝牙遥控与自动模式的平滑切换通过全局状态机管理,确保模式切换时小车立即停车,避免失控风险。
软件架构建议采用前后台系统,主循环负责传感器数据采集、OLED刷新、蓝牙指令解析和模式调度,定时器中断中完成PID速度环计算与电机PWM输出更新,K230与SU03T语音模块通过独立串口中断接收数据。
总体设计方案框图

原理图设计说明
-
STM32F407VET6 主控:基于ARM Cortex-M4内核,主频168MHz。采用最小系统板方案,外接8MHz晶振和32.768kHz RTC晶振。BOOT0和BOOT1接地确保从Flash启动,复位电路采用RC复位+按键。
-
DHT11温湿度模块:采用成品数字温湿度传感器模块,单总线接口与MCU GPIO相连。模块内部已集成8位MCU完成数模转换和校准,对外输出40位数字信号,无需自行设计信号调理电路。建议DATA引脚外接4.7kΩ上拉电阻以保证通信稳定性。
-
HC-SR04超声波模块:采用成品超声波测距模块,Trig和Echo引脚直连MCU GPIO。Trig发送10μs以上高电平脉冲触发测距,Echo输出高电平持续时间对应距离。工作电压5V,需注意与MCU的3.3V电平匹配,建议Echo引脚串联分压电阻。
-
AT8236电机驱动模块:AT8236为双路H桥电机驱动芯片,峰值电流3.6A。通常采用成品驱动板方案,MCU通过PWM控制转速、GPIO控制方向。需在电机两端并联续流二极管(如已内置则无需额外添加),电源输入端建议并联100μF电解电容+0.1μF瓷片电容滤波。
-
SU03T语音模块:采用成品离线语音识别模块,通过UART与MCU通信。模块已内置语音指令集,用户可通过串口配置关键词。建议供电3.3V,RX/TX直连MCU对应UART引脚。
-
K230视觉模块:嘉楠科技K230 AI视觉模组,通过UART与MCU通信传输识别结果。模块内部运行轻量级目标检测模型。需独立供电(5V/2A),与MCU之间仅需TX/RX两根线连接。
-
蓝牙JDY-31:采用成品BLE透传模块,UART接口与MCU通信。HC-06指令集兼容,默认波特率9600bps。工作电压3.3V,直连MCU UART引脚即可。
-
0.96寸OLED屏幕:SSD1306驱动,I2C接口。采用成品模块,SCL和SDA直连MCU I2C引脚,需外接4.7kΩ上拉电阻(部分模块已内置)。
-
SG90舵机:微型舵机,PWM控制角度(0.5ms
2.5ms对应0°180°)。采用成品模块,信号线直连MCU定时器PWM输出通道,工作电压5V。 -
蜂鸣器(声光报警):有源蜂鸣器通过NPN三极管驱动,基极串联限流电阻后接MCU GPIO。LED报警指示灯串联限流电阻接MCU GPIO。
-
电压采集:电池电压通过电阻分压后接入MCU ADC通道。分压比根据电池最大电压(如8.4V锂电2S)计算,确保分压后不超过3.3V。建议分压电阻旁并联0.1μF电容进行滤波。
-
按键*4:轻触按键,一端接GND,另一端接MCU GPIO并配置内部上拉。软件中需做消抖处理(延时20ms或状态机消抖)。
PCB设计说明
-
布局:电源电路(电池输入、稳压)放置在PCB一侧,远离模拟信号采集区域。电机驱动AT8236布局在PCB边缘靠近电机接口处。MCU及数字电路集中在中间区域,OLED、按键等放在便于人机交互的位置。K230模块因功耗较大且对发热敏感,建议独立成板或预留足够的散热间距。
-
布线:电源走线宽≥40mil(1mm),电机驱动电源线宽≥80mil(2mm)。信号线宽10mil,线间距≥8mil。ADC电压采集走线尽量短且远离PWM等高频信号线,并铺地包围以降低噪声干扰。
-
散热与隔离:AT8236电机驱动为大功率器件,需在PCB上预留散热铜皮面积或外加散热片。电机回路与数字电路回路应单点接地(星形接地),避免电机干扰通过地线耦合到MCU。K230模块电流较大(峰值2A),独立走粗电源线并加去耦电容。
-
接口:各模块连接器采用2.54mm排针/排母,标注丝印名称和方向。电源输入采用DC座或XT30接口,反接保护二极管串接在电源输入端。
软件说明
代码基于STM32 HAL库开发,采用前后台架构:主循环负责传感器数据采集、OLED显示刷新、蓝牙指令解析和模式调度,定时器中断中完成PID速度环闭环控制与电机输出更新。
代码结构概览:
-
初始化阶段(
main函数前半部分):依次初始化HAL库、系统时钟(168MHz)、延时函数、LED、定时器、编码器、电机PWM、舵机PWM、超声波、自动驾驶系统、DHT11、电压检测、OLED、按键、串口(蓝牙+K230)以及PID控制器。初始化完成后蜂鸣器关闭,进入主循环。 -
主循环(
while(1)):轮询按键扫描,切换OLED显示页面(数据页面和设置页面);根据当前模式(自动/手动/跟踪)调用对应的任务函数;接收处理蓝牙和K230的串口数据;通过蓝牙定时发送传感器数据到手机APP。 -
定时器中断(
GTIM_TIMX_INT_IRQHandler):每10ms触发一次,读取编码器转速,通过斜坡函数实现软启动/软停止,运行位置式或增量式PID计算出PWM值,最终通过Motor_SetSpeed输出到电机驱动。
主函数代码:
/**
****************************************************************************************************
* @file main.c
* @author Mr.Sea
* @version V1.0
* @date 2026-1-15
* @brief 自动巡逻智能小车
****************************************************************************************************
*/
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/timer.h"
#include "./BSP/AT8236/at8236.h"
#include "encoder.h"
#include "pid.h"
#include "pid_inc.h"
#include "sg90.h"
#include "hcsr04.h"
#include "auto_drive.h"
#include "oled.h"
#include "dht11.h"
#include "voltage.h"
#include "dev_bluetooth.h"
#include "bsp_usart.h"
#include "key.h"
#include "dev_k230.h"
//C库函数
#include
#include "stdio.h"
#include "math.h"
#include "stdbool.h"
#include "su_03t.h"
/*--------------------------------------全局变量声明-----------------------------------------------*/
/* === 这是送给 PID 计算的"当前爬升速度" (初始化为0) === */
float target_rpm_real_fl = 0.0f;
float target_rpm_real_fr = 0.0f;
float target_rpm_real_rl = 0.0f;
float target_rpm_real_rr = 0.0f;
#define PID_MODE 1 // 1 位置式PID 0 是增量式PID
DHT11_Data_TypeDef dht11; // DHT11 结构体变量声明
char oled_buf[128]; // oled 数据缓存
uint8_t key_num; //按键键值
uint8_t Page_flag=1;
char sensor_data[256]; /*发送数据到APP*/
uint8_t BT_Send_flag=1; /*蓝牙发送标志位*/
/*---------------------------------------------------------------------------*/
/*---------------------------------------函数声明-------------------------------------*/
void Ramp_Target(float final, float *curr, float step);
void Show_Data(void);
void Show_Speed(void);
void Page_setting(void);
/*---------------------------------------------------------------------------*/
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
SU_03T_Init(115200);
led_init(); /* 初始化LED */
gtim_timx_int_init(100 - 1, 8400 - 1); /* 定时器初始化 */
gtim_tim10_int_init(49999, 8399);
Encoder_Init(); /* 初始化编码器 (TIM2, 3, 4, 5) */
Motor_Hardware_Init(); /* 初始化电机 PWM */
SG90_Init(); /* 初始化SG90舵机 PWM */
Hcsr04Init(); /*超声波初始化 */
Auto_Drive_Init(); /*自动驾驶系统初始化 (归位舵机) */
DHT11_Init(); /*初始化DHT11*/
Voltage_Init(); /*初始化电压检测*/
OLED_Init(); /*OLED 初始化*/
OLED_Clear(0); /*初始化 清屏*/
key_init(); /* 按键初始化 */
BSP_USART_Init(); /* 串口 蓝牙 K230初始化*/
#if PID_MODE
System_PID_Init(); /*位置式PID算法 初始化*/
#else
PID_INC_Init(); /*增量式PID算法 初始化*/
#endif
BEEP = OFF; /*初始化 蜂鸣器关*/
TIM11_Init_Config(); /*定时器11初始化*/
printf("START!");
while(1)
{
key_num=key_scan(0); /* 按键扫描 */
if(key_num == 1) {
Page_flag++;
Page_flag %= 2;
OLED_Clear(0);
}
switch(Page_flag) {
case 1:
Show_Data(); //数据显示页面
break;
case 0:
Page_setting(); //设置页面
break;
}
/* 自动驾驶模式 */
if(C_MODE==auto_mode){Auto_Drive_Task();}
/* 接收处理蓝牙信息 */
BT_Task_Parse();
/* 接收处理K230信息 */
K230_Task_Parse();
/* 蓝牙定时发送传感器数据 */
if(BT_Send_flag==1)
{
int len = snprintf(sensor_data, sizeof(sensor_data),
"data#%d#%d#%d#%d#%3d#%d#\r\n",
(int)C_MODE,
dht11.temp_int,
dht11.humi_int,
Voltage_Get_Percentage(),
(int)Hcsr04GetLength(),
k210_flag);
if(len > 0 && len < sizeof(sensor_data))
{
HAL_UART_Transmit(&huart3, (uint8_t*)sensor_data, len, 100);
}
BT_Send_flag=0; /*重置发送标志位*/
}
}
}
/**
* @brief 定时器中断服务函数 (PID速度环)
*/
void GTIM_TIMX_INT_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&timer_handle, TIM_FLAG_UPDATE) != RESET)
{
Encoder_Get_All_Speed(10, &motor_fl, &motor_fr, &motor_rl, &motor_rr);
#if PID_MODE
float ramp_step = 20.0f;
Ramp_Target(target_rpm_fl, &target_rpm_real_fl, ramp_step);
Ramp_Target(target_rpm_fr, &target_rpm_real_fr, ramp_step);
Ramp_Target(target_rpm_rl, &target_rpm_real_rl, ramp_step);
Ramp_Target(target_rpm_rr, &target_rpm_real_rr, ramp_step);
float pwm_fl = PID_Compute(&pid_fl, target_rpm_real_fl, motor_fl.rpm);
float pwm_fr = PID_Compute(&pid_fr, target_rpm_real_fr, motor_fr.rpm);
float pwm_rl = PID_Compute(&pid_rl, target_rpm_real_rl, motor_rl.rpm);
float pwm_rr = PID_Compute(&pid_rr, target_rpm_real_rr, motor_rr.rpm);
#else
float ramp_step = 5.0f;
Ramp_Target(pid_inc_target_rpm_fl, &target_rpm_real_fl, ramp_step);
Ramp_Target(pid_inc_target_rpm_fr, &target_rpm_real_fr, ramp_step);
Ramp_Target(pid_inc_target_rpm_rl, &target_rpm_real_rl, ramp_step);
Ramp_Target(pid_inc_target_rpm_rr, &target_rpm_real_rr, ramp_step);
float pwm_fl = PID_Inc_Compute(&pid_inc_fl, target_rpm_real_fl, motor_fl.rpm);
float pwm_fr = PID_Inc_Compute(&pid_inc_fr, target_rpm_real_fr, motor_fr.rpm);
float pwm_rl = PID_Inc_Compute(&pid_inc_rl, target_rpm_real_rl, motor_rl.rpm);
float pwm_rr = PID_Inc_Compute(&pid_inc_rr, target_rpm_real_rr, motor_rr.rpm);
#endif
Motor_SetSpeed(MOTOR_FL, (int)pwm_fl);
Motor_SetSpeed(MOTOR_FR, (int)pwm_fr);
Motor_SetSpeed(MOTOR_RL, (int)pwm_rl);
Motor_SetSpeed(MOTOR_RR, (int)pwm_rr);
__HAL_TIM_CLEAR_IT(&timer_handle, TIM_IT_UPDATE);
}
}
/**
* @brief 斜坡函数 (实现软启动/软停止)
*/
void Ramp_Target(float final, float *curr, float step)
{
if (*curr < final) {
*curr += step;
if (*curr > final) *curr = final;
}
else if (*curr > final) {
*curr -= step;
if (*curr < final) *curr = final;
}
}
/**
* @brief TIM10 中断服务函数 (定时发送)
*/
void GTIM_TIM10_INT_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&timer10_handle, TIM_FLAG_UPDATE) != RESET)
{
k210_flag=1;
BT_Send_flag=1;
__HAL_TIM_CLEAR_IT(&timer10_handle, TIM_IT_UPDATE);
}
}
/**
* @brief OLED显示温湿度、电量、模式
*/
void Show_Data(void)
{
static uint16_t dht_timer = 0;
if(++dht_timer > 5) {
dht_timer = 0;
Read_DHT11(&dht11);
}
snprintf(oled_buf, sizeof(oled_buf), "温 度:%3d%℃", dht11.temp_int);
OLED_ShowText(0, 0, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "湿 度:%3d%%", dht11.humi_int);
OLED_ShowText(0, 2, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "电 量:%3d%%", Voltage_Get_Percentage());
OLED_ShowText(0, 4, (uint8_t *)oled_buf, 0);
if(key_num==2){
C_MODE++; if(C_MODE>2){C_MODE=manual_mode;}
Car_SetMotion(0,0);
}
static char mode_text[16];
switch (C_MODE) {
case auto_mode: strcpy(mode_text, "自动"); break;
case manual_mode: strcpy(mode_text, "手动"); break;
case gz_mode: strcpy(mode_text, "跟踪"); break;
default: strcpy(mode_text, "未知"); break;
}
sprintf(oled_buf, "模 式: %s", mode_text);
OLED_ShowText(0, 6, (uint8_t*)oled_buf, 0);
}
/**
* @brief OLED显示四轮转速
*/
void Show_Speed(void)
{
snprintf(oled_buf, sizeof(oled_buf), "FL:%3.1f", motor_fl.rpm);
OLED_ShowText(0, 0, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "FR:%3.1f", motor_fr.rpm);
OLED_ShowText(0, 2, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "BL:%3.1f", motor_rl.rpm);
OLED_ShowText(0, 4, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "BR:%3.1f", motor_rr.rpm);
OLED_ShowText(0, 6, (uint8_t *)oled_buf, 0);
}
/**
* @brief 设置页面 (速度、安全距离)
*/
void Page_setting(void)
{
static uint8_t Index_flag = 0;
snprintf(oled_buf, sizeof(oled_buf), "速 度:%d", SPEED);
OLED_ShowText(16, 0, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "安全距离A:%3d", OBSTACLE_FRONT_DIST);
OLED_ShowText(16, 2, (uint8_t *)oled_buf, 0);
snprintf(oled_buf, sizeof(oled_buf), "安全距离B:%3d", OBSTACLE_SIDE_DIST);
OLED_ShowText(16, 4, (uint8_t *)oled_buf, 0);
if(key_num == 2) {
Index_flag++;
Index_flag = Index_flag % 3;
}
switch(Index_flag) {
case 0:
OLED_ShowString(0, 0, (uint8_t *)">", 16);
OLED_ShowString(0, 2, (uint8_t *)" ", 16);
OLED_ShowString(0, 4, (uint8_t *)" ", 16);
break;
case 1:
OLED_ShowString(0, 0, (uint8_t *)" ", 16);
OLED_ShowString(0, 2, (uint8_t *)">", 16);
OLED_ShowString(0, 4, (uint8_t *)" ", 16);
break;
case 2:
OLED_ShowString(0, 0, (uint8_t *)" ", 16);
OLED_ShowString(0, 2, (uint8_t *)" ", 16);
OLED_ShowString(0, 4, (uint8_t *)">", 16);
break;
}
if(Index_flag == 0 && key_num == 3) SPEED++;
if(Index_flag == 0 && key_num == 4) { SPEED > 0 ? SPEED-- : 0; }
if(Index_flag == 1 && key_num == 3) OBSTACLE_FRONT_DIST++;
if(Index_flag == 1 && key_num == 4) { OBSTACLE_FRONT_DIST > 0 ? OBSTACLE_FRONT_DIST-- : 0; }
if(Index_flag == 2 && key_num == 3) OBSTACLE_SIDE_DIST++;
if(Index_flag == 2 && key_num == 4) { OBSTACLE_SIDE_DIST > 0 ? OBSTACLE_SIDE_DIST-- : 0; }
}
软件架构说明:
系统共分为多个BSP(板级支持包)层模块,各模块职责如下:
| 模块 | 文件 | 功能 |
|---|---|---|
| 系统初始化 | sys.c | 时钟配置168MHz |
| 电机驱动 | at8236.c | AT8236 PWM/方向控制 |
| 编码器 | encoder.c | 四路编码器速度采集 |
| PID控制 | pid.c / pid_inc.c | 位置式/增量式PID速度环 |
| 自动驾驶 | auto_drive.c | 超声波避障与自主巡航决策 |
| 舵机 | sg90.c | SG90角度控制 |
| 超声波 | hcsr04.c | HC-SR04测距驱动 |
| 温湿度 | dht11.c | DHT11单总线读写 |
| 电压检测 | voltage.c | ADC电池电压采集 |
| OLED | oled.c | SSD1306 I2C显示驱动 |
| 蓝牙 | dev_bluetooth.c | 蓝牙数据收发解析 |
| K230通信 | dev_k230.c | K230串口指令解析 |
| 语音 | su_03t.c | SU03T离线语音识别 |
| 按键 | key.c | 按键扫描与消抖 |
主循环在while(1)中交替执行:按键扫描→页面刷新→自动驾驶任务→蓝牙指令解析→K230数据解析→传感器数据定时发送。定时器中断以10ms周期执行PID闭环控制,斜坡函数Ramp_Target实现软启动/软停止,避免电机突然加减速造成的机械冲击。
实物展示说明
- 正面整体照片,展示小车底盘、四轮、各传感器模块的布局位置



注意事项
- 焊接:电机驱动模块和K230模块供电电流较大,电源焊点需饱满,避免虚焊导致供电不稳定。各模块的VCC和GND不要接反。
- 接线:蓝牙JDY-31的RX/TX注意交叉连接(MCU TX→蓝牙RX,MCU RX→蓝牙TX)。
- 上电调试顺序:先单独测试各模块功能(LED→OLED→按键→DHT11→超声波→电机→蓝牙→语音→K230),确认各模块正常后再联调整体功能。初次上电前用万用表检测电源与地之间是否短路。
- 常见问题:DHT11读取失败通常是因为时序不满足(延时函数不准确),检查系统时钟配置。电机不动或抖动可能是PWM频率太低,建议设置在10kHz以上。蓝牙无法连接时检查波特率是否匹配(JDY-31默认9600)。
- K230模块:需独立5V/2A供电,不可与MCU共用同一路3.3V LDO。模块上电后需要约5秒初始化时间,MCU应在延时后发查询指令。
- PID调试:车速闭环调试时,先调P(比例),观察到振荡后再加D(微分)抑制,最后加I(积分)消除稳态误差。实际调试时建议从小P值开始逐步增大。
演示视频
其它附件上传
无
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










