嵌入式硬件篇---TOF|PID
文章目录
- 前言
- 1. 硬件准备
- 主控芯片
- ToF模块
- 1.VL53L0X
- 2.TFmini
- 执行机构:
- 电机
- 舵机
- 其他
- 2. 硬件连接
- (1) VL53L0X(I²C接口)
- (2) TFmini(串口通信)
- 3. ToF模块初始化与数据读取
- (1) VL53L0X(基于HAL库)
- (2) TFmini(串口接收)
- 4. PID算法实现
- (1) PID结构体定义
- (2) PID计算函数(带抗积分饱和)
- 5. 控制执行机构
- (1) 电机控制(PWM调速)
- (2) 舵机控制(角度调整)
- 6. 主循环逻辑
- 7. 关键优化与问题处理
- (1) ToF数据滤波
- (2) PID参数整定
- 阶跃响应法
- 典型参数范围:
- (3) 动态目标适应
- 8. 实际应用注意事项
- ToF模块限制
- 实时性
- 机械延迟
前言
在STM32F103RCT6上使用ToF(Time-of-Flight)模块(如VL53L0X、VL53L1X或TFmini)结合PID算法实现稳定距离控制,适用于高精度场景(如自动跟随、避障或工业定位)。以下是简单实现步骤:
1. 硬件准备
主控芯片
主控芯片:STM32F103RCT6(Cortex-M3,72MHz,足够处理ToF数据与PID运算)。
ToF模块
1.VL53L0X
VL53L0X:测距范围30cm~2m,精度±3mm,I²C接口。
2.TFmini
TFmini:串口通信,测距0.3m~12m,精度1%。
执行机构:
电机
电机(直流电机+编码器/PWM调速)。
舵机
舵机(用于方向调整,可选)。
其他
其他:电源、电机驱动(如TB6612)、OLED(显示距离,可选)。
2. 硬件连接
(1) VL53L0X(I²C接口)
VL53L0X引脚 STM32引脚 说明
VCC 3.3V 模块供电
GND GND 共地
SDA PB7 I²C数据线
SCL PB6 I²C时钟线
XSHUT PA8 复位引脚(可选)
(2) TFmini(串口通信)
TFmini引脚 STM32引脚 说明
VCC 5V 模块供电
GND GND 共地
TX PA10 接STM32的RX
RX PA9 接STM32的TX
3. ToF模块初始化与数据读取
(1) VL53L0X(基于HAL库)
#include "vl53l0x.h"
VL53L0X_Dev_t dev = {.i2c_handle = &hi2c1}; // I²C初始化略void ToF_Init() {VL53L0X_Error status;status = VL53L0X_Init(&dev);if (status != VL53L0X_ERROR_NONE) {printf("ToF init failed!\n");}VL53L0X_StartMeasurement(&dev);
}float Get_Distance() {VL53L0X_RangingMeasurementData_t data;VL53L0X_GetRangingMeasurementData(&dev, &data);return data.RangeMilliMeter / 10.0f; // 转换为cm
}
(2) TFmini(串口接收)
uint8_t tfmini_buffer[9];
float Get_Distance() {HAL_UART_Receive(&huart1, tfmini_buffer, 9, 100); // 接收9字节数据帧if (tfmini_buffer[0] == 0x59 && tfmini_buffer[1] == 0x59) { // 帧头校验uint16_t distance = tfmini_buffer[2] + (tfmini_buffer[3] << 8);return distance / 100.0f; // 转换为米}return -1; // 无效数据
}
4. PID算法实现
PID控制器通过调节输出使当前距离(反馈值)趋近目标距离(设定值)。
(1) PID结构体定义
typedef struct {float Kp, Ki, Kd; // PID参数float target; // 目标距离(单位与ToF一致)float error, last_error, integral;float output_max, output_min; // 输出限幅
} PID_Controller;PID_Controller pid = {.Kp = 0.8, .Ki = 0.05, .Kd = 0.2,.target = 50.0, // 目标距离50cm.output_max = 100, .output_min = -100
};
(2) PID计算函数(带抗积分饱和)
float PID_Update(PID_Controller *pid, float current) {pid->error = pid->target - current;// 积分项(抗饱和)pid->integral += pid->error;if (pid->integral > pid->output_max) pid->integral = pid->output_max;else if (pid->integral < pid->output_min) pid->integral = pid->output_min;// 微分项(抑制突变)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;if (output > pid->output_max) output = pid->output_max;else if (output < pid->output_min) output = pid->output_min;return output;
}
5. 控制执行机构
(1) 电机控制(PWM调速)
// 初始化PWM(TIM4通道1,PB6)
void PWM_Init() {TIM_OC_InitTypeDef sConfigOC = {0};htim4.Instance = TIM4;htim4.Init.Prescaler = 71; // 1MHz频率htim4.Init.Period = 999; // 1kHz PWMHAL_TIM_PWM_Init(&htim4);sConfigOC.Pulse = 0; // 初始占空比0sConfigOC.OCMode = TIM_OCMODE_PWM1;HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
}// 设置电机速度和方向
void Set_Motor(float pid_output) {uint16_t pwm = (uint16_t)fabs(pid_output);__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, pwm);// 方向控制(假设PB0为方向引脚)HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, pid_output > 0 ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
(2) 舵机控制(角度调整)
// 设置舵机角度(PID输出映射到0~180°)
void Set_Servo(float pid_output) {uint16_t angle = 90 + (int16_t)pid_output; // 示例:PID输出±30对应60°~120°uint16_t pwm = 500 + angle * 2000 / 180; // 0.5ms~2.5ms__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm);
}
6. 主循环逻辑
int main(void) {HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_I2C1_Init(); // VL53L0X使用I²C// MX_USART1_UART_Init(); // TFmini使用串口PWM_Init();ToF_Init();float current_dist, pid_output;while (1) {current_dist = Get_Distance(); // 获取当前距离pid_output = PID_Update(&pid, current_dist);Set_Motor(pid_output); // 控制电机// Set_Servo(pid_output); // 或控制舵机HAL_Delay(20); // 控制周期20ms(50Hz)}
}
7. 关键优化与问题处理
(1) ToF数据滤波
移动平均滤波:
#define FILTER_SIZE 5
float filter_buffer[FILTER_SIZE];
float Filter_Distance(float new_value) {static uint8_t index = 0;filter_buffer[index++] = new_value;if (index >= FILTER_SIZE) index = 0;float sum = 0;for (uint8_t i = 0; i < FILTER_SIZE; i++) sum += filter_buffer[i];return sum / FILTER_SIZE;
}
(2) PID参数整定
阶跃响应法
设Ki=0, Kd=0,逐渐增大Kp直到系统振荡,然后取50%的值。
加入Kd抑制超调,最后加Ki消除稳态误差。
典型参数范围:
Kp: 0.5~2.0(比例增益)
Ki: 0.01~0.1(积分时间)
Kd: 0.1~0.5(微分时间)
(3) 动态目标适应
若目标距离变化频繁,可加入动态参数调整:
if (fabs(pid.error) > 20) pid.Kp = 1.5; // 大误差时提高响应
else pid.Kp = 0.8;
8. 实际应用注意事项
ToF模块限制
ToF模块限制:VL53L0X在强光下性能下降,需避免直射阳光。
实时性
实时性:控制周期建议20~50ms,过短可能导致PID震荡。
机械延迟
机械延迟:电机响应滞后时,需增加Kd或降低Ki。
通过上述步骤,STM32F103RCT6可精确控制物体与ToF模块间的距离。实际调试时需结合硬件特性(如电机惯性、ToF精度)优化参数。
相关文章:
嵌入式硬件篇---TOF|PID
文章目录 前言1. 硬件准备主控芯片ToF模块1.VL53L0X2.TFmini 执行机构:电机舵机其他 2. 硬件连接(1) VL53L0X(IC接口)(2) TFmini(串口通信) 3. ToF模块初始化与数据读取(1) VL53L0X(基于HAL库)(…...
Realtek 8126驱动分析第四篇——multi queue相关
Realtek 8126是 5G 网卡,因为和 8125 较为接近,第四篇从这里开始也无不可。本篇主要是讲 multi queue 相关,其他的一些内容在之前就已经提过,不加赘述。 1 初始化 1.1 rtl8126_init_one 从第一篇我们可以知道每个 PCI 驱动都注…...

基于Java和PostGIS的AOI面数据球面面积计算实践
目录 前言 一、计算方法简介 二、球面面积计算 1、AOI数据转Polygon 2、Geotools面积计算 3、GeographicLib面积计算 4、PostGIS面积计算 三、结果分析 1、不同算法结果对比 2、与互联网AOI对比 3、与天地图测面对比 四、总结 前言 在现代地理信息系统(G…...

Spring Boot之Web服务器的启动流程分析
如何判断创建哪种web容器:servlet?reactive? 我们在启动Spring Boot程序的时候,会使用SpringApplication.run方法来启动,在启动流程中首先要判断的就是需要启动什么类型的服务器,是servlet?或者…...
C# SQLite高级功能示例
目录 1 主要功能 2 程序结构和流程 3 详细实现说明 3.1 基础设置 3.2 事务演示 3.3 索引演示 3.4 视图演示 3.5 触发器演示 3.6 全文搜索演示 3.7 窗口函数演示 3.8 外键约束演示 4 高级功能示例 5 单个方法详细介绍 5.1 SetupExampleData()方法 5.2 UseTransact…...

【周输入】510周阅读推荐-1
本号一年了,有一定的成长,也有很多读者和点赞。自觉更新仍然远远不够,需要继续努力。 但是还是要坚持2点: 在当前这个时代,信息大爆炸,层次不齐,不追加多, 信息输入可以很多&#x…...

基于动态规划的强化学习方法
目录 # 动态规划 # 基于动态规划的强化学习方法 # 求解过程: ## 策略评估 ## 策略提升 # 价值迭代算法 # 参考 # 动态规划 动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到目标问题的解。…...
启动 spyder ModuleNotFoundError: No module named ‘PyQt5.QtWebKitWidgets‘
一、根本原因 Spyder 版本兼容性:Spyder 4.x 依赖 QtWebKitWidgets,但该模块在 PyQt5 5.15 中已被移除。 PyQt5 版本冲突:如果你安装了较新的 PyQt5(如 5.15),则会缺少 QtWebKitWidgets。 二、解决方案 方法…...
ChemBlender:科研绘图创新解决方案
一、研究背景与冲突 (一)研究背景 在科学研究领域,可视化表达对于成果的呈现与交流至关重要。科研绘图作为科学可视化的关键手段,涵盖了从微观分子结构到宏观实验现象等广泛的内容。随着科研的深入发展,研究对象的复杂…...

Uniapp Android/IOS 获取手机通讯录
介绍 最近忙着开发支付宝小程序和app,下面给大家介绍一下 app 获取通讯录的全部过程吧,也是这也是我app开发中的一项需求吧。 效果图如下 勾选配置文件 使用uniapp开发的童鞋都知道有一个配置文件 manifest.json 简单的说一下,就是安卓/ios/…...
设计一个分布式系统:要求全局消息顺序,如何使用Kafka实现?
一、高吞吐低延迟 Kafka 集群设计要点 1. 分区策略优化 // 计算合理分区数公式(动态调整) int numPartitions max(Tp, Tc) / min(Tp, Tc) // Tp生产者吞吐量 Tc消费者吞吐量建议初始按业务键(如订单ID)哈希分区单分区吞吐建议…...

2025年RIS SCI2区,改进白鲸优化算法+复杂非线性方程组求解,深度解析+性能实测
目录 1.摘要2.白鲸优化算法BWO原理3.改进策略4.结果展示5.参考文献6.代码获取7.读者交流 1.摘要 本文提出了一种改进白鲸优化算法(ABWOA)用来解决非线性方程组(SNLEs)求解问题。ABWOA引入了平衡因子和非线性自适应参数࿰…...

Java后端开发day48--反射动态代理
(以下内容全部来自上述课程) 反射 反射允许对成员变量,成员方法和构造方法的信息进行编程访问。 就是获取里面的成员变量、构造方法和成员方法,idea中打代码跳出来的提示就是反射。 1. 获取class对象的三种方式 Class.for…...
十四、继承与组合(Inheritance Composition)
十四、继承与组合(Inheritance & Composition) 引言 C最引人注目的特性之一是代码复用。组合:在新类中创建已有类的对象。继承:将新类作为已有类的一个类型来创建。 14.1 组合的语法 Useful.h //C14:Useful.h #ifndef US…...

ValueError: Caught ValueError in DataLoader worker process 0.
参考链接: https://stackoverflow.com/questions/1841565/valueerror-invalid-literal-for-int-with-base-10 它提示我有个地方值错误空字符 果然因为格式处理没有传进去东西,找下原因,让它正常处理 原来是相对路径的.影响了程序运行 将v…...

【数据结构】——链表OJ(下)
前面我们已经刷了几道单链表的题目,下面我们继续看几道题目。 一、相交链表 这道题题目的要求是很好理解的,就是现在我们有两个链表,然后我们就相办法进行判断,这两个链表是否是相交的,那么链表的相交其实就是有没有共…...

Adobe Acrobat pro在一份PDF中插入空白页
在Adobe Acrobat pro中先打开我们的PDF文件; 用鼠标点击需要插入空白页处的上一页; 然后如下图操作: 默认会在光标处的下一页插入一张空白页,你也可以修改插入页的页码或者向前一页插入...

java-----异常
对于Error:表示系统级错误或者资源耗尽的状况,像OutOfMemoryError、StackOverflowError等。这类错误是程序无法处理的,通常也不应该尝试去处理。 对于Exception:表示程序可以处理的异常。它又能细分为: 受检查异常&a…...

[工具]B站缓存工具箱 (By 郭逍遥)
📌 项目简介 B站缓存工具箱是一个多功能的B站缓存工具,包含视频下载、缓存重载、文件合并及系统设置四大核心功能。基于yutto开发,采用图形化界面操作,极大简化B站资源获取与管理流程。 工具可以直接将原本缓存的视频读取&#…...
《内网渗透测试:绕过最新防火墙策略》
内网渗透测试是检验企业网络安全防御体系有效性的核心手段,而现代防火墙策略的持续演进(如零信任架构、AI流量分析、深度包检测)对攻击者提出了更高挑战。本文系统解析2024年新型防火墙的防护机制,聚焦协议隐蔽隧道、上下文感知绕…...
python_竞态条件
好的,我们通过一个具体的例子来说明在多线程环境中,可变对象和不可变对象的行为差异,以及不可变对象如何避免竞态条件(race condition)。 1. 竞态条件(Race Condition) 竞态条件是指在多线程环…...
聊聊JetCache的CachePenetrationProtect
序 本文主要研究一下JetCache的CachePenetrationProtect CachePenetrationProtect com/alicp/jetcache/anno/CachePenetrationProtect.java Documented Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD, ElementType.FIELD}) public interface CachePenetr…...
【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)
🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全流程)📊 📑 目录 🚀🔧【实战】基于 ABP vNext 构建高可用 S7 协议采集平台(西门子 PLC 通信全…...
数据结构:树(Tree)
目录 为什么需要树? 🌱 基本的树结构定义 什么是树? 树的术语 🌿 常见基本树的变体 🌳 二叉搜索树(BST) 🌲 自平衡二叉搜索树 1. AVL树(Adelson-Velsky and La…...

自动化测试与功能测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 什么是自动化测试? 自动化测试是指利用软件测试工具自动实现全部或部分测试,它是软件测试的一个重要组成 部分,能完成许多手工测试无…...
java中的Optional
在 Java 8 中,Optional 是一个用于处理可能为 null 的值的容器类,旨在减少空指针异常(NullPointerException)并提升代码的可读性。以下是 Optional 的核心用法和最佳实践: 1. 创建 Optional 对象 1.1 常规创建方式 Op…...
Qt事件循环机制
受事件循环机制影响,按钮的样式表改变了可能不会立即刷新。 需要使用 update() 或 repaint() 或者调用 QApplication::processEvents() 强制处理所有待处理的事件,从而确保界面更新。 在 Qt 中,事件循环(Event Loop)是…...
深入理解 OAuth 2.0:技术核心与实战场景
在互联网应用日益复杂的今天,如何安全、高效地实现第三方应用授权访问资源,成为开发者面临的重要问题。OAuth 2.0 凭借其灵活、安全的授权机制,成为解决这一问题的主流方案。本文将深入剖析 OAuth 2.0 的技术重点,并结合具体使用场…...
Rust 环境变量管理秘籍:从菜鸟到老鸟都爱的 dotenv 教程
前言 写代码的你,是否遭遇过这些灵魂拷问: “我现在在哪个环境?开发?测试?还是直接在生产线上裸奔?”“少写一个 .env,测试脚本在数据库里上演清空大法,客户当场破防。”“每次手动设置 RUST_ENV,命令敲到一半就开始怀疑人生,还怕输错一个字符引发灭世级事故。”别慌…...

CSS经典布局之圣杯布局和双飞翼布局
目标: 中间自适应,两边定宽,并且三栏布局在一行展示。 圣杯布局 实现方法: 通过float搭建布局margin使三列布局到一行上relative相对定位调整位置; 给外部容器添加padding,通过相对定位调整左右两列的…...