基于水位传感器的无接触式称重装置(arduino)
项目概述
本课程项目基于Arduino单片机和非接触连续液位传感器等,不使用应变片和压力传感器的情况下进行重量测量,量程为100g~700g。
项目成员及分工
张志毅:主要负责代码部分以及数据处理部分。
黄河洋:主要负责硬件搭建及改进。
还有些部分两人合力完成。
一、非接触连续液位传感器
采用 TM601AWLCOR 多通道电容传感芯片,它可以作为一个连续液位检测的控制器。通过检测液位不同高度时,不同通道的采样值变化,计算出相应的液位高度。
采集板需紧密贴合在非导体的容器外壁。当液体没过感应焊盘时,芯片感应到的电容发生变化,从而检测到液体覆盖感应焊盘的位置。为了保证检测的准确性,有 4 个阈值需要采集并且设置到芯片内。
采集板共有五个引脚
其中,PRG是用来烧录的,不用太关注。接下来介绍一下如何使用
①将标定板与采集板连接,采集板板紧密贴合到需要检测的容器上。把容器内水清空,并按下 KEY1 按键 1 次,设置记录空水状态,此时控制板读取 TH1E阈值,并且写入 TM601AWLCOR 片内,同时按新的阈值进行液位检测计算。按下按键之后,数码管显示数字 1,如果上述操作成功数码管会闪烁 3 次,如果操作失败数码管会闪烁 6 次。
②加少量水,将液位上升至零刻度线,连续按下 KEY1 按键 2 次,此时控制板读取 TH0Z 阈值, 并且写入 TM601AWLCOR 芯片内,同时按新的阈值进行液位检测计算。按下按键之后,数码管显示数字 2, 如果上述操作成功数码管会闪烁 3 次,如果操作失败数码管会闪烁 6 次。
③加水,将液位上升至满刻度线,连续按下 KEY1 按键 3 次,此时控制板读取 TH0F 和 TH1F 阈值,并且写入 TM601AWLCOR 芯片内,同时按新的阈值进行液位检测计算。按下按键之后,数码管显示数字 3,如果上述操作成功数码管会闪烁 3 次,如果操作失败数码管会闪烁 6 次。
这样就标定完成了,在瓶中注水就可以通过传感器表示高度了
二、实验原理
我们所使用的原理很简单,就是初中学习的排水法,m=ρ液gV排,如下图所示,在初始状态,我们有一个大盒子里面装有一些水,水中还有一个小盒子,此时传感器会记录一个液位的高度;然后我们在小盒子里面放上一个重物,小盒子会下沉一些,此时又会有一个液面的高度,两次液面的高度差就是我们所要求的ΔH,V排=ΔH*S,因为我们使用的是正方形的盒子,所以S可求,ρ液和g都是已知量,所以即可得到物体的重量,这便是我们的实验原理。
二、实验过程
1.装置改进
这是我们最初的装置,但这个装置有个很大的问题,当我们放入物体时小盒子会倾斜,这就会导致我们唯一的测量量高度测量不准确且需要较长时间来稳定,所以需要改进。
这是改进后的装置,我们加了几个曲面L型支柱,将小盒子的活动范围固定在了里面,同时,使用曲面,我们还克服了摩擦的问题,可谓是一举两得,这就是我们最终的装置。
2.代码部分
代码如下(示例):
#include <Wire.h>
#include <SimpleKalmanFilter.h>
//#define I2C_BUS_SPEED 205000UL
#define TM601_I2C_ADDRESS 0x40
void TM601_begin(void);
void TM601_writeRegister(uint8_t address, uint8_t data);
void TM601_burstRead(uint8_t baseAddress, uint8_t *buffer, uint8_t length);
int i=0;
float diff=0;
byte rx_buf[7] = {0};
float a=0;
float b=0;
float waterLevelbefore=0;
float waterlevelafter=0;
float weight =0;
float estimated_x=0;
SimpleKalmanFilter kf = SimpleKalmanFilter(1, 1, 0.01);
void setup() {
Serial.begin(9600);
Serial.println("init...");
TM601_begin();
Serial.println("init ok!");
TM601_burstRead(rx_buf,4);
a=rx_buf[0];
Serial.print("a");
waterLevelbefore = a/255*10;
Serial.println(waterLevelbefore);
delay(5000);
}
void loop() {
char i = 0;
//TM601_writeRegister(0x80,0);
TM601_burstRead(rx_buf,4);
b= rx_buf[0];
estimated_x = kf.updateEstimate(b);
Serial.println(estimated_x);
waterlevelafter = b /255 *10;
//Serial.println(waterlevelafter );
diff=waterlevelafter-waterLevelbefore;
weight=((14.4*14.4)*diff*0.6999+26.81808)*1.00718+0.1477;
Serial.print("weight:");
//Serial.println(weight);
delay(50);
}
void TM601_begin(void)
{
Wire.begin();
delay(600);
// Wire.setClock(I2C_BUS_SPEED);
}
void TM601_writeRegister(uint8_t address, uint8_t data)
{
Wire.beginTransmission(TM601_I2C_ADDRESS);
Wire.write(address);
Wire.write(data);
Wire.endTransmission();
}
void TM601_burstRead(uint8_t *buffer, uint8_t length)
{
Wire.beginTransmission(TM601_I2C_ADDRESS);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom((uint8_t)TM601_I2C_ADDRESS, length,false);
uint8_t idx = 0;
while (Wire.available()) {
buffer[idx++] = Wire.read();
}
}
// void TM601_writeRegister(uint8_t address, uint8_t data)
// {
// Wire.beginTransmission(TM601_I2C_ADDRESS);
// Wire.write(address);
// Wire.write(data);
// Wire.endTransmission();
//}
//
// void TM601_burstRead(uint8_t baseAddress, uint8_t *buffer, uint8_t length)
// {
// Wire.beginTransmission(TM601_I2C_ADDRESS);
// Wire.write(baseAddress);
// Wire.endTransmission(false);
// Wire.requestFrom
这是最终版本的代码,代码的改进过程会在下面的数据处理详细介绍。
数据处理
首先我们只用公式m=ρ液体gV排在代码中来进行数据测量,测得了一些数据,并用excel花了图如下所示
系列1是原始的数据,系列2是我们测得的数据,可以看出差别还是比较大的,具体原因我们也没有弄清楚,但是从上面可以看出,重量的增长总体上还是呈现线性的,并且也本应是线性的,所以我们使用了origin的线性拟合,如下图所示
总体上还是比较好的,在上图中可以得到斜率和截距,将它们的值应用到代码里面之后,我们又测量了一组数据,再次用excel画图,如下
可以看出已经比较符合,但还是不够完美,这是我们考虑了滤波,因为传感器是0~255的,所以精度并不是很高,再加上抖动的话,经过计算,我们发现误差最大可能为5g,所以加上了滤波操作。下面是滤波前后对比。
达到了一个比较好的效果。
以上便是数据处理的整个过程,最终的误差可以减小到1%以内。
总结
总体来说,这次项目所完成的水称结果简单,测量相对精确,数据稳定较快,可以基本完成任务。
展望
①可以采用精度更高的传感器。
②3D打印定制固定柱,减小误差。
③换用更高更细的大盒子以提高精度。