本次参加活动使用的板卡是RP2040游戏机搭配了基于TMF8821的dToF传感器模块,TMF8821 dToF传感器模块与RP2040游戏机管脚匹配,插上直接可以使用,并在侧面预留了扩展接口,可以自由焊接/调试/抓取数据。
TMF8821采用单个模块化封装,带有相关的 VCSEL(垂直腔面发射激光器)。dToF 设备基于 SPAD、TDC 和直方图技术,可实现 5000 mm 的检测范围。由于它的镜头位于 SPAD 上,它支持 3x3、4x4 和 3x6 多区域输出数据以及宽广的、动态可调的视野。VCSEL 上方的封装内的多透镜阵列 (MLA) 拓宽了 FoI(照明场)。原始数据的所有处理都在片上进行,TMF8821在其 I2C 接口上提供距离信息和置信度值。
本次要实现的题目是第一题:将板卡组合后固定,并保证与面前的平面存在一定夹角,通过程序测算出板卡距离屏幕的夹角和垂直最小距离。分解一下题目需求,可以分为以下几步:
- 参加活动直播,学习本次活动的具体流程和题目细节
- 阅读芯片手册和其他技术文档
- 在RP2040上完成TMF8821模块驱动
- 读取传感器的测量数据
- 加入一些数学计算,计算出板卡与平面的夹角和最小距离
- 将计算结果显示出来
设计方案
本次使用microPython进行开发,所以主程序就一直循环采集数据后进行计算。
传感器原理
假定传感器的感光元件为xy平面,光线照射和反射的方向为z轴方向,便可以建立出立体空间关系,当传感器的z轴垂直对着一个均匀平面时,传感器就应该检测到中间的距离最短,越远离光学中心的像素测出的距离越大,
接收端的内部是一个二维的SPAD阵列,能感受到的入射光线的角度和SPAD的位置有关,如下图,
可以利用3D建模软件验证手册中提到的空间关系,上方半透明的是传感器封装上的镜头,下方的矩阵就是SPAD阵列。
传感器驱动
按照题目要求,使用amsOSRAM的官方驱动库,并对其进行移植,但是本次平台是RP2040,可以使用micropython进行编程,这样就可以利用一些移植上的优势,在网络上搜寻一番后,果然找到了前辈开发的TMF8821驱动库,只需要一些CV工作即可(后面发现还需要刷写系统成circuitpython模式才可以使用)
虽然不用手写驱动,但是驱动的流程还是要走一遍,对照着一篇文档(AN001015.pdf),可以找到如下介绍
这里详细的描述了上电后TMF8821加载片内固件的流程,在已有的驱动库中也可以找到对应片段,
# 发送DOWNLOAD_INIT
self._write_bl_command(_TMF882X_BL_DOWNLOAD_INIT, bytes(b"\x29"))
self._check_bl_cmd_executed(verbose)
# 设置写地址初始位置
self._write_bl_command(_TMF882X_BL_SET_ADDR,
bytes([(_TMF882X_BL_FW_ADDR >> 8) & 0xFF, _TMF882X_BL_FW_ADDR & 0xFF]))
self._check_bl_cmd_executed(verbose)
size_downloaded = 0
image = _tof_image3
# 开始分段发送固件
if verbose:
print('Downloading firmware: ', end='')
while size_downloaded < len(image):
chunk_bytes = min(_TMF882X_BL_MAX_FW_DATA, len(image) - size_downloaded)
data = image[size_downloaded:size_downloaded + chunk_bytes]
self._write_bl_command(_TMF882X_BL_W_RAM, data)
self._check_bl_cmd_executed()
size_downloaded += chunk_bytes
if verbose:
print(f'{size_downloaded / len(image) * 100:3.0f}%\b\b\b\b', end='')
if verbose:
print('100% -- Done.')
# 加载完成固件后设置指令,传感器开始执行固件
self._write_byte(_TMF882X_REG_ENABLE, 0x21) # set bit 0 (general enable) and bit 5 (run from RAM)
self._write_bl_command(_TMF882X_BL_RAMREMAP_RESET, bytes()) # Instruct device to run firmware
# 延时一段时间后验证程序下载完成
timeout = ticks_add(ticks_ms(), 3) # Wait for maximum 3ms
while ticks_less(ticks_ms(), timeout):
self.app_id = self._read_byte(_TMF882X_REG_APPID)
if self.app_id == _TMF882X_MODE_APP:
sleep(0.006) # Should wait 6ms before reading patch version
self.minor, self.patch, self.build = self._read_bytes(_TMF882X_REG_MINOR, 3)
if verbose:
print(f'App v{self.minor}.{self.patch} running, build_type: {self.build:08b}.')
break
else:
raise Exception('Device is still in bootloader mode 3ms after running firmware!')
self._active_range = 'long'
主程序中需要进入实例化一个TMF8821对象即可调用初始化部分
tof = TMF8821(i2c)
tof.config.iterations = 3.5e6
tof.config.period_ms = 1 # as small as possible for repeated measurements
tof.config.spad_map = '3x3_normal_mode'
tof.write_configuration()
print('Starting measurements..\n')
tof.start_measurements()
while True:
t_start = ticks_ms()
measurement = tof.wait_for_measurement(timeout_ms=500)
# Calibration
distances = [m for m in measurement.distances]
print(tof.config.spad_map[:3], ' '.join(map(str, distances)), end='\n')
print(distances, end='\n')
t_end = ticks_ms()
这样程序就可以完成配置和采集。
此时设置了传感器的SPAD模式为3x3模式。视野范围为33° * 32°,9个点的夹角就依次为
(-16.5,16) (0,16) (16.5,16)
(-16.5,0) (0,0) (16.5,0)
(-16.5,-16)(0,-16)(16.5,-16)
数学推导
如图,过同一点作三条射线,交远处一直线,已知左右射线夹角为32度,可以通过传感器知道三个射线交远处直线,所截线段的长度,求中间直线与远处直线的夹角,以及三条射线的起点距离直线的垂直距离。
1. 建立坐标系和几何关系
将远直线设为水平直线,并令射线公共点 P 在直线正上方,其垂直距离为 d(即 P=(0,d)P=(0,d)P=(0,d))。令射线分别以 P 为起点,方向角以“与垂直线的偏离角”记,正右偏为正。
- 左射线: 与垂直方向偏离角为
- 中射线: 偏离角为
- 右射线: 偏离角为
(这里的 φ为未知角度,反映了整个系统相对于“正垂直”的转动。)
由于直线为水平,任一射线与直线的交点可求。对任一射线,其方向为
(β为该射线相对于垂直方向的偏离角,注意由于直线在y=0,交点满足 d−tcosβ=0得 ),因此交点的水平坐标为
于是三射线在直线上的交点坐标为:
- A:xA=dtan(φ−16∘)
- B:xB=dtan(φ))
- C:xC=dtan(φ+16∘)
2. 利用差角公式化简
利用差角公式有:
故可得:
这就是一个关于 φ 的方程,通过已知 L1与 L2可求出 φ。
3. 求解中射线与直线的夹角
在本坐标系中,远直线为水平线(与水平夹角为 0°),而中射线的方向角(相对于垂直向下)为 φ。故中射线与水平方向的夹角为
90∘−φ
这就是要求的中射线与远直线的夹角。
4. 求射线起点到直线的垂直距离 d
由上式可得
这样就算完了所有的参数
a = math.atan2(math.cos(math.pi*8/180)*(1-distances[1]/distances[7]),math.sin(math.pi*8/180)*(1+distances[1]/distances[7]))
d = distances[4]*math.cos(a)
a=180*a/math.pi#为了方便理解,将角度转为角度制
上图可以看出,蓝色线为角度值,黄色线为距离,在一定角度内摇动,距离没有发生大变化。
项目总结
本次艾迈斯欧司朗与硬禾科技联合主办的2024 dToF传感器光电设计竞赛,是一次多方面的综合能力提升活动,理论与编程结合,强化了学习的效果。
开发过程中,使用了部分AI工具,让我感觉到虽然有了AI工具,但是需要结合自己的思考才行,全部交给AI的话,对自己的提升就很有限了;此外一些3D建模和计算工具也方便了自己对项目的理解,本次活动内容非常丰富,希望以后还可以继续参加此类活动!