这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 使用TMF8821的角度测量

共1条 1/1 1 跳转至

使用TMF8821的角度测量

高工
2026-04-20 07:53:51     打赏
硬件系统介绍dToF模块本系统所采用的dToF模块是以TMF8821芯片为核心设计的一款直接飞行时间(direct Time - of - Flight,dToF)传感器模块。dToF技术凭借其独特的原理,在距离检测领域展现出卓越性能。该模块基于单光子雪崩二极管(Single Photon Avalanche Diode,SPAD)、时间数字转换器(Time - Digital Converter,TDC)以及直方图技术,能够实现长达5000mm的检测范围,为远距离测量提供了可靠支持。

在数据输出方面,TMF8821具备多区域输出能力,支持3x3、4x4和3x6等多种输出模式,可满足不同场景下对多区域距离信息的需求。同时,它拥有宽广且动态可调的视野,能够灵活适应各种复杂环境。该模块通过I2C接口向外提供距离信息以及置信度值,方便与其他设备进行数据交互。此外,其测量频率可达10Hz,能够快速响应并更新测量数据,确保信息的实时性。


主控模块主控模块选用的是RP2040 Game Kit,这是一款基于树莓派RP2040芯片的嵌入式系统学习平台。它采用USB Type - C接口进行供电,不仅保证了充足的电力供应,还具有通用性强的特点。以RP2040作为主控芯片,赋予了该平台强大的性能,能够高效处理各类数据和任务。在编程支持方面,RP2040 Game Kit表现出极大的灵活性,支持MicroPython、C/C++等多种编程语言。这使得开发者可以根据自身技能和项目需求,选择最适合的编程方式,极大地提高了开发效率和便捷性。主控与传感器连接方式

主控模块与dToF传感器之间采用I2C连接方式进行通讯。I2C(Inter - Integrated Circuit)总线是一种简单、双向二线制同步串行总线,它仅需两根线(串行数据线SDA和串行时钟线SCL)即可实现主控与传感器之间的数据传输。这种连接方式具有硬件实现简单、占用资源少、通信效率较高等优点,能够有效保障主控与传感器之间稳定、高效的数据交互。


功能说明

主要的功能具体内容是将板卡组合后进行固定,并确保其与面前的平面形成一定夹角。通过编写程序,利用dToF模块获取相关数据,进而测算出板卡与屏幕之间的夹角以及垂直方向上的最小距离。这一任务旨在充分发挥dToF模块的测量功能以及主控模块的数据处理能力,实现对特定几何参数的精确测量。


拿到硬件时,一眼便见传感器十分小巧,外围元件寥寥无几。电子森林设计的排母插座与 RP2040 Game Kit 完美贴合。官网虽有 TMF8821 说明文档与 Arduino 例程,可老师突然规定不能用 Arduino,一时犯了难。英文文档读得懵懂,好在知乎有相关驱动文章,参照着各方讲解,艰难地用 micropython 啃起了芯片驱动。


image.png

TMF8821可以提供10Hz的测量速度,为解决传感器获得数据跳动问题,使用50组数据进行均值,也就是当传感器稳定5秒,即可计算出距离和角度信息。mpy中不知道如何使用pandas,手工计算矩阵还是挺烦的。


代码

from tmf8821_utility import Tmf8821Utility
from i2c_com import I2C_com, I2C_Settings
from tmf8821_device import Tmf8821Device
from tmf8821_app import Tmf8821App
import utime
import uos
import machine
import st7789 as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_bold_16x16 as font2
from fonts import vga1_16x32 as font3
import random
import framebuf
import math

#系统的初始化
st7789_res = 0#定义res引脚
st7789_dc  = 1#定义dc引脚
disp_width = 240#设置显示屏的宽度
disp_height = 240#设置显示屏的高度
spi_sck=machine.Pin(2)#定义SPI的SCK引脚
spi_tx=machine.Pin(3)#定义SPI的MOSI引脚
spi0=machine.SPI(0,baudrate=4000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)#初始化spi0
display = st7789.ST7789(spi0, disp_width, disp_width,
                          reset=machine.Pin(st7789_res, machine.Pin.OUT),
                          dc=machine.Pin(st7789_dc, machine.Pin.OUT),
                          xstart=0, ystart=0, rotation=1)#初始化我们的显示屏幕
display.fill(st7789.BLACK)

# 创建 I2C 通信对象
ic_com = I2C_com()
# 创建 Tmf8821Utility 实例
tof = Tmf8821Utility(ic_com)
if Tmf8821App.Status.OK != tof.open():
    tof.error("Error open FTDI device")
    raise RuntimeError("Error open FTDI device")
else:
    tof.log("Opened connection")

tof.init_bootloader_check()   #载入固件 使用3X3测量方式
#获得距离信息
groupnum=30     #均值组个数
allowdev=0.045  #允许偏差值
maxallowdev=0.08   #最大允许偏差
distancegroup=[]			#距离信息数组,使用10组数据,后期做均值处理
distanceinfo={"distance":0}      #距离信息

def getDistance():
    frames = tof.measure_frame(1)  # 获取帧数据
    distance=[]			#距离信息
    framelist=[]
    if frames:
        for idx, frame in enumerate(frames):
            # 获取前 9 个测量结果
            for result_idx, result in enumerate(frame.results):
                if result_idx < 9:  # 只取前 9 个
                    framelist.append(result.distanceInMm)
        #调整一下顺序,使数据顺序与实际情况相符
        if len(framelist)==9:
            distance.append(framelist[6])
            distance.append(framelist[7])
            distance.append(framelist[8])
            distance.append(framelist[3])
            distance.append(framelist[4])
            distance.append(framelist[5])
            distance.append(framelist[0])
            distance.append(framelist[1])
            distance.append(framelist[2])
            if len(distancegroup)>=groupnum:
                distancegroup.pop(0) 
            distancegroup.append(distance)        

#显示距离信息
def dispDistance(offstat):
    if len(distancegroup)==groupnum:
        distance=[0,0,0,0,0,0,0,0,0]     #计算一个均值矩阵 3X3
        for pos in range(9):
            for i in range(groupnum):
                distance[pos]=distance[pos]+distancegroup[i][pos]
        for pos in range(9):
            distance[pos]=distance[pos]/groupnum           

        #串口显示  和 屏幕显示
        for i in range(3):
            for j in range(3):
                print("%.1f" % (distance[i*3+j]), end=' ')
                if offstat==0:
                    display.text(font2, "%3.0f" % (distance[i*3+j]/10), i*70+30, j*25+10,color=st7789.GREEN)   #使用CM做单位
                elif offstat==1:
                    display.text(font2, "%3.0f" % (distance[i*3+j]/10), i*70+30, j*25+10,color=st7789.YELLOW)   #使用CM做单位
                else:
                    display.text(font2, "%3.0f" % (distance[i*3+j]/10), i*70+30, j*25+10,color=st7789.RED)   #使用CM做单位
            print()
        print()
    print()

            

#当所有距离 小于偏差时 返回0  大于偏差小于最大偏差时 返回1 否则返回9
def distanceIsStand():
    if len(distancegroup)>=groupnum:
        distance=[0,0,0,0,0,0,0,0,0]     #计算一个均值矩阵 3X3
        for pos in range(9):
            for i in range(groupnum):
                distance[pos]=distance[pos]+distancegroup[i][pos]
        for pos in range(9):
            distance[pos]=distance[pos]/groupnum
        #print(distancegroup)
        #print(distance)
        groupavg=sum(distance)/9				#距离均值
        groupoffset=groupavg*allowdev    #计算偏差值
        maxgroupoffset=groupavg*maxallowdev    #计算偏差值
        print("offset   %.1f,%.1f,%.1f" %(groupavg,groupoffset,maxgroupoffset))
        #检查平均组里的每个数据 是否是落在偏差值内
        echoval=0
        for idx in range(9):
            if abs(distance[idx]-groupavg)>maxgroupoffset :
                distanceinfo["distance"]=0    #偏差太大时就 不可信距离值
                distanceinfo["angle"]=0
                return 9
            if abs(distance[idx]-groupavg)>groupoffset:
                echoval=1
        #依据偏差值的结果来计算距离和角度
        if echoval==0 :    #当偏差小于指定值时,就取中心点的距离为距离
            if distanceinfo["distance"]==0:
                distanceinfo["distance"]=distance[4]   
                distanceinfo["angle"]=0
            else:
                if distanceinfo["distance"]<distance[4]:
                    distanceinfo["angle"]=math.degrees(math.acos(distanceinfo["distance"]/distance[4]))
                else:
                    distanceinfo["distance"]=distance[4]
                    distanceinfo["angle"]=0                   

        elif echoval==1 :  #当偏差值较小,则计算偏角
            if distanceinfo["distance"]<distance[4]:
                distanceinfo["angle"]=math.degrees(math.acos(distanceinfo["distance"]/distance[4]))
            else:
                distanceinfo["angle"]=0          
        return echoval

               

#显示距离和角度信息           
def dispinfo(offstat):
    if offstat==0:
        display.text(font3, "%3.0f CM" % ((distanceinfo["distance"])/10), 120, 100,color=st7789.GREEN)   #使用CM做单位
        display.text(font2, " DIST: " , 10, 105,color=st7789.WHITE)   
        display.text(font3, "%3.0f " % (distanceinfo["angle"]), 120, 160,color=st7789.GREEN)
        display.text(font2, "ANGLE: " , 10, 160,color=st7789.WHITE)
    elif offstat==1:
        display.text(font3, "%3.0f CM" % ((distanceinfo["distance"]+5)/10), 120, 100,color=st7789.YELLOW)   #使用CM做单位
        display.text(font2, " DIST: " , 10, 105,color=st7789.WHITE)
        if distanceinfo["angle"]==0:
            display.text(font3, "---", 120, 160,color=st7789.YELLOW)
            display.text(font2, "ANGLE: " , 10, 165,color=st7789.WHITE)
        else:
            display.text(font3, "%3.0f " % (distanceinfo["angle"]), 120, 160,color=st7789.YELLOW)
            display.text(font2, "ANGLE: " , 10, 165,color=st7789.WHITE)
    else:
        display.text(font3, "---         " , 120, 100,color=st7789.RED)   #使用CM做单位
        display.text(font2, " DIST: " , 10, 105,color=st7789.WHITE)
        display.text(font3, "---", 120, 160,color=st7789.YELLOW)
        display.text(font2, "ANGLE: " , 10, 165,color=st7789.WHITE)            
  
while True:
    getDistance()
    offstat=distanceIsStand()
    print("echo= ",offstat)
    dispDistance(offstat)
    #如果计算距离小于预期偏差,则显示距离信息 以均值为距离信息,并且开始计算
    dispinfo(offstat)
    #print("distanceval=",distanceinfo)
    utime.sleep_ms(100)  # 延迟 1 秒,便于查看每次显示


实现效果

由于使用了50组数据做的均值,所以系统反应是比较缓慢的,需要缓慢移动,直至数据不再跳动,再继续旋转。

image.png

小角度旋转,测量角度开始变化。小角度变化,感觉还是比较准确。

image.png


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]