立创开源基于STM32F103与ADS1256的六维力传感器低成本实现方案大家好我是老张一个在嵌入式领域摸爬滚打了十几年的工程师。最近在做一个机器人抓取的项目需要用到六维力传感器来感知抓取力但市面上的成品传感器价格动辄上万实在让人肉疼。于是我决定自己动手丰衣足食基于立创开源平台上的一个项目用STM32F103和ADS1256搞一个低成本方案。今天我就把这个从硬件选型、结构设计到软件调试的全过程掰开揉碎了讲给大家听目标是让有一定单片机基础的朋友都能看懂甚至能自己复现出来。六维力传感器听起来高大上其实就是能同时测出三个方向的力Fx, Fy, Fz和三个方向的力矩Mx, My, Mz的传感器。它在机器人、精密装配、生物力学等领域非常有用。这个项目的核心思路就是用一个3D打印的“六梁弹性体”结构来感受力贴在梁上的应变片把形变转换成微弱的电阻变化再通过高精度ADCADS1256把这个变化读出来最后由单片机STM32F103计算并输出结果。整个方案成本可以控制在几百元内性价比极高。1. 项目核心六梁弹性体结构与测量原理咱们先别急着看电路和代码得把最根本的东西——传感器是怎么“感觉”到力的——弄明白。这是整个项目的物理基础。1.1 什么是六梁弹性体你可以把弹性体想象成一个“弹簧秤”的核心部分只不过这个“弹簧”结构更复杂。本项目采用的是六梁弹性体结构它就像一个中间开孔的圆盘周围均匀分布着六根细长的“梁”。当有外力或力矩作用在传感器上时这六根梁就会发生非常微小的弯曲形变。注意这个结构设计并非凭空想象而是参考了学术论文贺玏重庆大学2023中的成熟方案确保了力学特性的合理性。为什么是六根梁因为我们要测量六个维度的信息三个力三个力矩。理论上至少需要六个独立的测量信号才能解算出这六个未知数。六根梁每根梁上贴应变片正好可以提供六个或更多测量通道为后续计算提供了基础。为了增加传感器的灵敏度让微小的力也能产生可测量的形变在弹性体中间开了腰孔这样梁在受力时更容易弯曲。1.2 应变片与电桥如何把形变变成电信号弹性体会形变但我们单片机只认识电压信号。怎么转换靠的就是应变片和惠斯通电桥。应变片是一种特殊的电阻它的阻值会随着自身的拉伸或压缩而改变。我们用胶水把应变片牢牢贴在弹性体的梁上。梁一弯曲贴在它表面的应变片也就跟着被拉伸或压缩阻值就发生了微小的变化。单个应变片的阻值变化非常微小直接测量很困难而且容易受温度影响。所以我们通常把它们接成电桥电路。本项目采用的是一种简化方案每根梁上串联两片应变片。为什么要用两片这有两个好处温度补偿两片应变片如果对称粘贴环境温度变化对它们的影响是相同的在串联电路里可以相互抵消大大减少了温度漂移带来的误差。提高灵敏度两片应变片一个受拉电阻增加一个受压电阻减少它们的变化在电路中是叠加的使得输出的电压信号变化更明显。1.3 测量原理的数学“翻译”原始文章里给出了一个经典的半桥测量原理图和分析公式咱们用大白话解释一下。假设梁弯曲时应变片R1被拉长阻值变为R ΔRR2被压缩阻值变为R - ΔR。它们串联在一起接在5V电源和地之间。那么中间连接点也就是ADC的输入引脚的电压U_in是多少呢根据串联分压原理U_in (R2 / (R1 R2)) * 5V把R1 R ΔRR2 R - ΔR代进去经过推导具体过程原文有可以得到U_in (5/2) * (1 - ε) V其中ε ΔR / R叫做应变它直接反映了梁的形变程度。而梁的形变程度又和它受到的力成正比这是材料力学的基本原理由梁的尺寸和材料决定。所以整个链条就清晰了外力 - 弹性体梁弯曲 - 应变片阻值变化(ΔR) - 测量点电压变化(U_in) - ADC读取电压 - 单片机计算应变(ε) - 根据标定公式换算成力(F)总结一下我们通过精巧的机械结构六梁弹性体把复杂的六维力分解到六根梁的简单弯曲上再用应变片和电路把弯曲程度转换成电压信号。接下来就是如何精准地测量这个电压信号了。2. 硬件系统设计精准测量的保障知道了原理我们来看看用什么“家伙事儿”来实现。硬件系统分为两大部分采集板和主控板。采集板要尽可能靠近传感器减少信号干扰主控板则负责处理数据和通信。2.1 核心芯片选型主控MCUSTM32F103C8T6这就是大名鼎鼎的“蓝莓派”或“最小系统板”常用的芯片。选择它理由很充分Cortex-M3内核性能足够外设丰富有SPI、I2C、USART等关键是价格极其便宜资料和社区支持海量非常适合低成本项目。模数转换器ADCADS1256IDBR这是本项目的精度担当六维力传感器输出的信号非常微弱变化量可能只有毫伏甚至微伏级别。普通的单片机内置ADC通常12位根本不够看。24位高精度意味着它能把参考电压分成2^24≈1677万份分辨率极高能捕捉极其微小的电压变化。Δ-Σ架构这种架构通过过采样和数字滤波能有效抑制噪声提高精度。8通道差分输入正好满足我们六根梁可能需要多个信号的测量需求差分输入还能进一步抑制共模干扰。高达30kSPS采样率对于力传感器这种变化相对缓慢的信号绰绰有余。2.2 信号调理与电源光有高精度ADC还不够前端信号调理和干净的电源是保证精度的另一半。外部电压基准REF5025AIDR OPA350UAADC的精度严重依赖一个稳定、准确的参考电压。REF5025是一颗高精度、低温漂的2.5V基准电压源芯片。用一颗低噪声运放OPA350做缓冲可以为ADS1256提供非常“干净”的基准电压这是高精度测量的基石。电源管理AMS1117-3.3V B0505SAMS1117-3.3经典的LDO将输入的5V电压稳成3.3V给STM32、ADS1256的数字部分供电。B0505S这是一个隔离电源模块。它把输入和输出的5V电路在电气上隔离开。为什么要隔离因为传感器部分采集板和主控部分可能处在不同的“地”电位直接相连会产生地环路引入巨大的噪声。用隔离电源和隔离通信如光耦、数字隔离器可以切断这个环路保证模拟信号的纯净。这是工业测量电路中非常关键的一步通信接口USB-C CAN (TJA1050)USB-C用于供电和程序调试下载非常方便。CAN总线项目图中虽然预留了CAN芯片(TJA1050)的位置但原作者注明实际并未使用。CAN总线在汽车和工业领域应用广泛抗干扰能力强适合远距离通信。如果你需要将传感器集成到机器人或工业设备中启用CAN接口会是一个专业的选择。整个硬件框架如下图所示清晰地展示了采集板模拟前端和主控板数字处理的划分3. 软件框架与关键代码解析硬件搭好了接下来就是让芯片“活”起来。软件部分也分为嵌入式端STM32和上位机PC两部分。3.1 嵌入式端STM32的职责STM32在这里主要干三件事配置并驱动ADS1256通过SPI总线与ADC通信设置采样率、通道、增益等参数并读取转换结果。数据处理对ADC读回的原始数据进行滤波比如简单的移动平均滤波去除毛刺。数据上传通过串口UART将处理后的数据发送给上位机。核心难点在于ADS1256的驱动。这个芯片功能强大配置寄存器比较多。下面我给出一个最精简的初始化和读取单通道数据的代码框架并加上详细注释。// 必要的头文件和引脚定义以SPI1为例 #include stm32f10x.h #include spi.h // 你需要实现的SPI底层驱动 #include delay.h // 假设ADS1256的片选CS、数据准备就绪DRDY引脚已定义 #define ADS1256_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4) #define ADS1256_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4) #define ADS1256_DRDY_READ() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) // 向ADS1256写一个命令/寄存器 void ADS1256_WriteCmd(uint8_t cmd) { ADS1256_CS_LOW(); SPI1_ReadWriteByte(cmd); // 通过SPI发送一个字节 ADS1256_CS_HIGH(); } // 向ADS1256的某个寄存器写数据 void ADS1256_WriteReg(uint8_t regAddr, uint8_t data) { ADS1256_CS_LOW(); SPI1_ReadWriteByte(CMD_WREG | regAddr); // 写寄存器命令 SPI1_ReadWriteByte(0x00); // 接着发送要写的寄存器数量-1这里写1个寄存器 SPI1_ReadWriteByte(data); // 发送要写入的数据 ADS1256_CS_HIGH(); } // 从ADS1256的某个寄存器读数据 uint8_t ADS1256_ReadReg(uint8_t regAddr) { uint8_t data; ADS1256_CS_LOW(); SPI1_ReadWriteByte(CMD_RREG | regAddr); // 读寄存器命令 SPI1_ReadWriteByte(0x00); // 要读的寄存器数量-1 delay_us(10); // 等待一点时间确保数据就绪 data SPI1_ReadWriteByte(0xFF); // 发送空字节同时接收数据 ADS1256_CS_HIGH(); return data; } // 等待一次ADC转换完成DRDY引脚变低 void ADS1256_WaitDRDY(void) { while(ADS1256_DRDY_READ() 1); // 等待DRDY引脚变为低电平 } // 读取当前设置通道的ADC值24位 int32_t ADS1256_ReadADC(void) { int32_t adc_val 0; uint8_t buf[3]; ADS1256_WaitDRDY(); // 等待数据就绪 ADS1256_CS_LOW(); SPI1_ReadWriteByte(CMD_RDATA); // 发送读取数据命令 delay_us(10); // 短暂等待 // 连续读取3个字节组成24位数据 buf[0] SPI1_ReadWriteByte(0xFF); buf[1] SPI1_ReadWriteByte(0xFF); buf[2] SPI1_ReadWriteByte(0xFF); ADS1256_CS_HIGH(); // 将3个字节组合成一个有符号的32位整数 adc_val ((int32_t)buf[0] 16) | ((int32_t)buf[1] 8) | buf[2]; // 注意24位数据是二进制补码格式最高位是符号位。需要扩展到32位。 if (adc_val 0x00800000) { // 如果最高位第23位是1则为负数 adc_val | 0xFF000000; // 将高8位全部补1完成符号扩展 } return adc_val; } // ADS1256初始化示例 void ADS1256_Init(void) { // 1. 初始化SPI和GPIO略 // 2. 复位ADS1256 ADS1256_CS_LOW(); delay_ms(10); ADS1256_CS_HIGH(); ADS1256_WriteCmd(CMD_RESET); // 发送复位命令 delay_ms(10); // 等待复位完成 // 3. 配置寄存器根据你的需求设置 // 例如设置数据速率采样率为10SPS增益为1 ADS1256_WriteReg(REG_DRATE, 0xF0); // 0xF0对应10SPS ADS1256_WriteReg(REG_ADCON, 0x00); // 增益PGA1 // 4. 自校准建议执行 ADS1256_WriteCmd(CMD_SELFCAL); delay_ms(100); // 等待校准完成 }提示上面的代码是框架性的你需要根据自己实际的SPI驱动和硬件连接进行修改。CMD_*和REG_*这些宏定义需要参考ADS1256的数据手册。重点理解ReadADC函数中24位有符号数据的拼接和符号位扩展处理。3.2 上位机Python数据可视化STM32通过串口把六个通道的ADC原始数据或者初步计算后的力/力矩值发送给电脑。我们需要一个上位机软件来接收、解析并实时显示这些数据。原作者用的是Python的Tkinter库来制作GUI这是一个轻量且标准的选择。这里给一个更简单的、基于pyserial和matplotlib的实时绘图示例方便大家快速验证数据。import serial import matplotlib.pyplot as plt import matplotlib.animation as animation from collections import deque # 设置串口参数请根据实际情况修改端口和波特率 ser serial.Serial(COM3, 115200, timeout1) # 初始化存储数据的队列假设我们显示最近100个数据点 data_len 100 channels 6 history [deque([0]*data_len, maxlendata_len) for _ in range(channels)] # 创建绘图窗口 fig, axes plt.subplots(2, 3, figsize(12, 8)) # 2行3列显示6个通道 lines [] for i, ax in enumerate(axes.flat): line, ax.plot([], [], lw2) ax.set_title(fChannel {i1}) ax.set_ylim(-2000000, 2000000) # 根据ADS1256输出范围调整 ax.grid(True) lines.append(line) def update(frame): if ser.in_waiting: try: # 假设STM32发送的数据格式为 ch1,ch2,ch3,ch4,ch5,ch6\n line ser.readline().decode(ascii, errorsignore).strip() values list(map(int, line.split(,))) if len(values) channels: for i in range(channels): history[i].append(values[i]) lines[i].set_data(range(len(history[i])), history[i]) except Exception as e: print(fError parsing data: {e}) return lines # 创建动画每50毫秒更新一次 ani animation.FuncAnimation(fig, update, interval50, blitTrue) plt.tight_layout() plt.show() # 记得关闭串口 ser.close()这个脚本会打开一个图形窗口动态绘制6个通道的ADC原始值。你可以根据这个基础扩展成显示真实的力/力矩值、保存数据、或者用Tkinter做一个更友好的界面。软件框架的整体交互关系如下图所示4. 装配、调试与核心难点4.1 机械装配与材料传感器的核心——六梁弹性体是3D打印的。材料选择了PA11尼龙。为什么不用更常见的PLA或ABS因为尼龙材料具有一定的韧性和抗疲劳性在反复受力后形变恢复性好更适合做传感器的弹性元件。STL模型文件可以在项目的model文件夹中找到。应变片需要自己购买并粘贴。这是整个制作过程中手艺要求最高的环节贴得好不好直接决定传感器性能。表面处理粘贴面要用细砂纸打磨平整、清洁并用酒精彻底清洗确保无油污灰尘。粘贴对齐应变片要准确地沿着梁的轴向粘贴。可以使用定位辅助工具。胶水与固化使用专用的应变片胶水如氰基丙烯酸酯快干胶或环氧树脂胶严格按照说明书操作确保完全固化。引线焊接应变片的引线非常细焊接时要快、准避免过热损坏应变片。4.2 电路调试要点电源噪声是第一大敌务必确保给ADS1256和REF5025的模拟电源干净。多用电解电容滤低频和陶瓷电容滤高频并联进行退耦。隔离电源B0505S的“原边”和“副边”地要严格分开。基准电压要稳定用万用表测量REF5025输出的2.5V基准观察其波动。如果波动大检查负载和布线。SPI通信排查先用逻辑分析仪或示波器抓一下STM32发给ADS1256的SPI波形确认片选、时钟、数据线信号是否正确。特别注意SPI的时钟极性和相位CPOL, CPHA是否与ADS1256要求的一致。信号接地模拟地AGND和数字地DGND建议在一点连接通常是ADC芯片下方。采集板上的所有模拟地要星型连接到这个AGND点。4.3 最大的挑战标定与解耦这是本项目原文作者也明确指出尚未完成的部分也是自制六维力传感器最难的一关。你即使完美地做出了硬件读出了六个通道的电压值U1~U6也还不知道它们到底对应多大的Fx, Fy, Fz, Mx, My, Mz。标定你需要一个已知大小和方向的力/力矩源比如精密的砝码、拉力计、或者商用的标定机依次施加单一方向的力例如纯Fz记录下此时6个通道的读数。重复这个过程施加多个不同大小、不同方向的力获得大量数据。解耦得到数据后你需要建立一个数学模型。通常是一个6x6的矩阵叫做标定矩阵或解耦矩阵C。[Fx, Fy, Fz, Mx, My, Mz]^T C * [U1, U2, U3, U4, U5, U6]^T通过数学方法如最小二乘法利用标定数据拟合出这个矩阵C。以后测量时只要将读到的6个电压值组成的向量乘以这个矩阵C就能计算出最终的六维力/力矩了。重要提示由于缺乏专业的标定设备个人完成高精度标定非常困难。这会导致传感器只能定性感知力的方向和相对大小而无法进行定量测量。这是DIY此类传感器需要接受的一个现实。5. 项目总结与展望跟着上面这些步骤走下来你应该能搭建起一个可以输出六路模拟信号的传感器硬件平台并通过上位机看到数据变化。当你在传感器上施加不同的力时六个通道的读数应该会有相应的、规律性的变化——这已经成功了一大半这个开源项目为我们提供了一个极其优秀的低成本六维力传感器实现框架。它涵盖了从机械设计Fusion仿真、3D打印、模拟电路设计电桥、高精度ADC、基准、隔离电源到嵌入式及上位机软件的全链路。给想复现的朋友几点建议降低预期首次尝试目标可以是“做出一个能灵敏反应六维力变化的装置”而非“高精度测量仪器”。分步验证先不贴应变片用精密可调电阻代替验证ADS1256采集电路和软件是否正常工作。再单独测试应变片粘贴和电桥。善用仿真像原作者一样用Fusion或其他软件对弹性体进行受力仿真可以帮助你理解不同力作用下各根梁的形变趋势这对后续分析数据很有帮助。社区求助立创开源平台、各大电子论坛都有很多热心网友。遇到问题把现象、你的电路图、代码片段贴出来往往能获得宝贵的指点。虽然高精度标定是个门槛但这个过程本身会让你对传感器原理、模拟电路设计、嵌入式系统开发有极其深刻的理解。当你亲手做出的传感器随着你的按压而灵敏地改变输出时那种成就感是无可替代的。希望这篇教程能帮你推开六维力传感器DIY的大门。