MLX90640测温系统的实现

1 背景

疫情蔓延,在家无事,看着别人玩动态测温挺好玩,自己也试着玩一把,160*120的红外热电堆太贵,就拿MLX90640试试吧,终于,在一个星期忙碌后,完成了基于MLX90640和STM32的动态测温,包括上位机的调试,在这里给大家分享下。
在这里插入图片描述
在这里插入图片描述

2硬件调试

我采用的是STM32F103的核心版,买来很早了,便宜,把传感器插到I2C1上,PB6&PB7,vcc和GND 也分别接上去,就可以调试了,在此比较郁闷的是MLX90640的资料太少了,没办法只能看英文,勉勉强强搞定了,推介大家如果STM32 用的版本比较老,还是用固件好一些,STM32CUBEMX太坑了,我差点死在USB驱动上,后来才发现,还是串口好用。。。接下来就分享下代码吧:
int main(void)
{
int i;
static uint16_t eeMLX90640[832];
uint16_t frame[834];
float Ta,tr;
float emissivity=0.95;
static float mlx90640To[768];
u32 CheckCode;
paramsMLX90640 mlx90640;
char szMsg[64];
/test led/
LEDFlash(2);
/end test led/
//UART1_PutStr(USART1, “@Start to Run…\r\n”);

MLX90640_SetRefreshRate(MLX_I2C_ADDR, RefreshRate);
//UART1_PutStr(USART1, "@1..\r\n");
MLX90640_SetChessMode(MLX_I2C_ADDR);
//UART1_PutStr(USART1, "@2..\r\n");
MLX90640_DumpEE(MLX_I2C_ADDR, eeMLX90640);
//UART1_PutStr(USART1, "@3..\r\n");
MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
//UART1_PutStr(USART1, "@4..\r\n");

for(i=0;i<3;i++)
{
		MLX90640_GetFrameData(MLX_I2C_ADDR, frame);
		delay_ms(500);
}

delay_ms(1000);

while (1)
{
MLX90640_GetFrameData(MLX_I2C_ADDR, frame);
Ta = MLX90640_GetTa(frame, &mlx90640);
tr = Ta - TA_SHIFT;
MLX90640_CalculateTo(frame, &mlx90640, emissivity, tr, mlx90640To);

		//LSB first, MSB behind
		CheckCode=0x5A5A;
		CheckCode+=0x0602;
		for(i=0;i<768;i++)
		{
			UsartBuff[i*2+4]= (u16)(mlx90640To[i]*100)&0xFF;
			UsartBuff[i*2+5]= ((u16)(mlx90640To[i]*100)>>8)&0xFF;
			CheckCode+=(u16)(mlx90640To[i]*100);
		}
		UsartBuff[1540]= (u16)(Ta*100)&0xFF;
		UsartBuff[1541]= ((u16)(Ta*100)>>8)&0xFF;
		CheckCode+=(u16)(Ta*100);
		UsartBuff[1542]= (u16)CheckCode&0xFF;
		UsartBuff[1543]= ((u16)CheckCode>>8)&0xFF;

		//sprintf(szMsg, "%.02f  %.02f  %.02f  %.02f  %.02f\r\n",	
		//mlx90640To[178],mlx90640To[180],mlx90640To[188],mlx90640To[188], mlx90640To[199]);
		UART1_PutBytes(USART1, UsartBuff, 1544);

		LEDFlash(1);
	}

}
这里要说明的是,MLX90640的各类模式还是要搞清楚, 比较复杂,我也还没有完全吸收,只是看懂怎么用了。

3 上位机软件

本来想用C++写的,但碍于c#太省心了,花了两个小时搞定,但里面的滑头也不少,用温度值的成像,把3224 做成360300效果一直不是非常好,后来调整了好久好久好久,才调整到现在的样子,因为物体大好处理,关键是如何把人和环境都放到32*24里,再插值出来得到好效果,我把AI的均值算法和用上了,感觉做的效果已经不容易了,代码分享如下:
private SerialPort sp = null;
public static Byte[] receiveBuffer = new byte[115200];
public static int index = 0;

    public static Bitmap KiResizeImage(Bitmap bmp, int newW, int newH)
    {
        try
        {
            Bitmap b = new Bitmap(newW, newH);
            Graphics g = Graphics.FromImage(b);

            // 插值算法的质量
            g.InterpolationMode = InterpolationMode.Bilinear;
            g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
            g.Dispose();
            return b;
        }
        catch
        {
            return null;
        }
    }

    public static int ReadData(MemoryStream curStream, int startPosition, int length)
    {
        int result = -1;

        byte[] tempData = new byte[length];
        curStream.Position = startPosition;
        curStream.Read(tempData, 0, length);
        result = BitConverter.ToInt32(tempData, 0);

        return result;
    }

    public static void WriteData(MemoryStream curStream, int startPosition, int length, int value)
    {
        curStream.Position = startPosition;
        curStream.Write(BitConverter.GetBytes(value), 0, length);
    }

    public static Bitmap CreateBitmap(ushort[] originalImageData, int originalWidth, int originalHeight)
    {
        Bitmap resultBitmap = new Bitmap(originalWidth, originalHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
        MemoryStream curImageStream = new MemoryStream();
        resultBitmap.Save(curImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
        curImageStream.Flush();
        int curPadNum = ((originalWidth * 8 + 31) / 32 * 4) - originalWidth;
        int bitmapDataSize = ((originalWidth * 8 + 31) / 32 * 4) * originalHeight;
        int dataOffset = ReadData(curImageStream, 10, 4);
        int paletteStart = 54;
        int paletteEnd = dataOffset;
        int color = 0;
        for (int i = paletteStart; i < paletteEnd; i += 4)
        {
            byte[] tempColor = new byte[4];
            tempColor[0] = (byte)color;
            tempColor[1] = (byte)color;
            tempColor[2] = (byte)color;
            tempColor[3] = (byte)0;
            color++;
            curImageStream.Position = i;
            curImageStream.Write(tempColor, 0, 4);
        }
        byte[] destImageData = new byte[bitmapDataSize];
        int destWidth = originalWidth + curPadNum;
        for (int originalRowIndex = originalHeight - 1; originalRowIndex >= 0; originalRowIndex--)
        {
            int destRowIndex = originalHeight - originalRowIndex - 1;
            for (int dataIndex = 0; dataIndex < originalWidth; dataIndex++)
            {
                destImageData[destRowIndex * destWidth + dataIndex] = (byte)(originalImageData[originalRowIndex * originalWidth + dataIndex]);
            }
        }
        curImageStream.Position = dataOffset;
        curImageStream.Write(destImageData, 0, bitmapDataSize);
        curImageStream.Flush();
        resultBitmap = new Bitmap(curImageStream);
        return resultBitmap;
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        sp = new SerialPort();
        sp.PortName = "COM6";
        sp.BaudRate = 115200;
        sp.StopBits = StopBits.One;
        sp.DataBits = 8;
        sp.Parity = Parity.None;
        sp.ReadTimeout = -1;
        sp.RtsEnable = true;
        sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
        sp.Open();
        button1.Enabled = false;
    }

    private void sp_DataReceived(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(50);  //延迟100ms等待接收完成数据
        this.Invoke((EventHandler)(
            delegate 
            {
                Byte[] readBytes = new Byte[sp.BytesToRead];
                sp.Read(readBytes, 0, readBytes.Length);
                if (readBytes.Length>=2 && readBytes[0]==0x5A && readBytes[1] == 0x5A)  //Start 
                {
                    index = 0;
                }

                readBytes.CopyTo(receiveBuffer, index);
                index += readBytes.Length;

                if (index >= 1543)
                {
                    ushort[] tempdata = new ushort[768];
                    for (int i=0;i<768;i++)
                    {
                        tempdata[i] = (ushort)(receiveBuffer[2 * i + 5] * 256 + receiveBuffer[2 * i + 4]);
                    }

                    long mean = 0;
                    long max = 0;
                    long min = 0;
                    int maxi = 0;
                    for (int i=0;i<768;i++)
                    {
                        mean += tempdata[i];
                        if (tempdata[i] > max)
                        {
                            max = tempdata[i];
                            maxi = i;
                        }
                        if (tempdata[i] < min) min = tempdata[i];
                    }
                    mean = mean / 768;

                    long scale = 0;
                    if ((max - mean) > (mean - min))
                        scale = max - mean;
                    else scale = mean - min;

                    ushort[] imgdata = new ushort[768];
                    for (int j=0; j<768;j++)
                    {
                        imgdata[j] = (ushort)((tempdata[j]  - mean)*255.0/scale);
                    }

                    System.Drawing.Bitmap bmptemp = new System.Drawing.Bitmap(32, 24);
                    bmptemp = CreateBitmap(imgdata, 32, 24);
                    System.Drawing.Bitmap bmptemp1 = new System.Drawing.Bitmap(pictureBox1.Width, pictureBox1.Height);
                    bmptemp1 = KiResizeImage(bmptemp, pictureBox1.Width, pictureBox1.Height);
                    int ix = (int)(maxi* pictureBox1.Width / (32.0*32));
                    int iy = (int)(maxi % 32* (pictureBox1.Height/24.0));

                    Graphics g = Graphics.FromImage(bmptemp1);
                    g.DrawEllipse(new Pen(Color.Red, 1), iy - 5, ix - 5, 10, 10);
                    Font drawFont = new Font("Arial", 16);
                    SolidBrush drawBrush = new SolidBrush(Color.Red);
                    g.DrawString(String.Format("{0:00.00}", max/100.0+3.9), drawFont, drawBrush, iy, ix);
                    g.Flush();

                    pictureBox1.Image = bmptemp1;

                    index = 0;
                }
            }

            ));
    }
}

}

4 关于精度的测试

实话讲,MLX90640的精度还是很不错的,不比lepton3.5差,应该和国产的差不多,一开始动态变化较大, 距离变化的时候动态变化也大,其实这一点,只要是红外非制冷的,应该都一样,所以,用40度的水,在不同距离,不同运行时间,做个二维修正表,一般都可以到0.5以内的,要想0.1-0.3, 还是要标温动态演算。

有问题或交流可以随时联系~

  • 12
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值