ADIS16505——树莓派下数据采集

前情提要

因为项目需要,买了一块ADIS16505-2惯导(官网链接)来使用,买的时候考虑到自己极差的硬件能力,所以是连着开发板一起买的(开发板)。买回来之后对datasheet进行了一波硬啃…虽然在网上看了五花八门各种各样的材料,但还是看的一知半解。
同时因为组里还有树莓派,所以想着相比直接用主机通过串口转SPI这种复杂的(我这种菜鸡认为复杂)的方式来进行数据采集,不如就用树莓派先进行数据采集,因此,就有了这篇博客。
本人硬件背景极差,博客内可能有叙述不对之处,还请见谅。
ADIS16505的测试代码可见我的Github仓库:ADIS16505_RPi
但是因为最后是和相机一起采集的,所以这里的代码比较粗糙,相对严谨一些的代码在和相机一起的程序里,只需要看对应的文件就可以了:flir_adis16505_rpi

ADIS16505

先来看一下这个惯导单元,作为MEMS-IMU的著名厂家,之前也在很多论文中看到别人使用过很多型号的ADIS16XXX系列的惯导了。我这次买的ADIS16505从陀螺量程上看分为三次,其中我选择了500量程的型号,也就是ADIS16505-2
在这里插入图片描述
关于IMU的精度指标,可以参考牛小骥老师团队的文章

新手入门系列2——如何读懂MEMS惯性器件的精度指标?

因为是同一家的产品吧,这里的内容相差不多。

时间特性

时间延迟特性
datasheet里给了一张延迟相关的图片,其中比较重要的是在拉低CS信号后的这个延时,如果不加这个延迟的话直接读取寄存器是无法获得数据值的;还有就是多次拉低拉高之间的tSTALL,这个延迟不加的话可能会影响下一次读取寄存器的值。在datasheet的表格里也给了这两个值,直接拿来用就可以了
在这里插入图片描述

滤波

滤波主要包括两个部分,分别是BARTLETT WINDOW FILTER以及DECIMATION FILTER,控制这两个选项的寄存器分别为FILT_CTRL以及DEC_RATE
前者是数字滤波器的一种,我的理解是用来降低噪声的
在这里插入图片描述
后者我的理解就是用来控制传感器的更新频率的
在这里插入图片描述
关于DEC_RATE后边也给了详细的解释,具体来讲因为ADIS16505原始的采样频率是2000Hz,最终的更新频率就会设置为2000/(1+DEC_RATE),比如设为3这里就是500Hz了
在这里插入图片描述

寄存器结构

ADIS16505是支持寄存器的读写的,其中可以读到的传感器包括最新的数据、错误标志以及硬件信息等,而像之前提到的滤波等级、采样频率等也是可以通过向寄存器写数据/命令进行控制的。
当然datasheet里也提到了寄存器由两个地址组成,即高位(upper)和低位(lower)组成,所以在读写寄存器的时候都会涉及到高低位的数据合并过程。

SPI

关于SPI协议,我之前也没有了解,因此也是通过网上的知识现学了一下。总的看下来SPI协议的优点在于支持全双工通信(又发又收),通信模式较为简单,而且数据传输率比较快。缺点的话就是大部分情况你无法判断你收没收到数据其实,包括后边调用的bcm2835库也是,为硬件调试和代码测试带来了一定的难度。
SPI模式下有一个主设备Master,以及一个从设备Slave,需要连接的线在datasheet里面也给了,其中33Ω的电阻由于不是必需的,所以之后的开发中也就没有使用
在这里插入图片描述
同时datasheet里面对SPI涉及到的传输频率、SPI模式等也进行了规定,在后面的开发中也要将对应的选项进行设置
在这里插入图片描述

读取寄存器数据

之前也提到过,每一个寄存器都是由一个高地址和一个低地址组成,同时在全双工模式下,我们向一个寄存器地址发送请求(其实就是通过SPI发送地址),我们就能接收到对应寄存器的OUTPUT数据

在这里插入图片描述

但需要注意的是,这里需要发送的也是16位的数据,所以要将寄存器的地址和0x00(可以理解为就是填充长度用的)先进行合并,再进行发送,这样才可以进行数据的读取,datasheet中以PROD_ID(常值,应为16505)为例,后续的开发也可以以这个变量作为测试。
在这里插入图片描述

BURST READ

根据信息来看,这应该是一种连续进行读取的功能,也就是发送一次,可以连续读到多次数据,这里的操作是比较方便的,不过看其他博客说这里的精度可能不如连续读取高,所以最终是没有选择这种模式。
在这里插入图片描述
根据这里的数据组成就可以得到对应的值了
在这里插入图片描述

程序开发

树莓派与bcm2835

bcm2835库是树莓派实现SPI、IIC、GPIO等通信协议与控制的一个有力开源库bcm2835,我们最终的程序开发也是基于这个库实现的。首先我们要先明确一下树莓派的GPIO引脚(图片引自知乎树莓派的GPIO控制)
在这里插入图片描述
其中物理引脚就是排列顺序,在BCM库中使用就要对应到BCM的编码方式,像我们这里要使用SPI模式,因此这里就要选择MOSI、MISO、SCLK以及CE0和CE1中的一个,这里需要注意的是我本来以为片选信号的引脚随便选一个输出的就可以,所以连了几天都没有连上,后来才发现CE0和CE1这两个引脚
关于bcm2835库的安装,在官网上也给出了,只需要按照对应的步骤来就可以
在这里插入图片描述
安装好之后,可以写一个自发自收的程序测试一下,没什么问题的话就可以正常使用了。
还需要注意的是bcm2835库是需要sudo权限的,所以最后使用的时候要用到sudo权限才能正常运行

连接

刚开始我使用树莓派直接供电,但后来搜了下发现树莓派的一个引脚的电流才几毫安,但这个设备的正常工作电流要几十毫安,所以我又单独搞了一个USB-TTL的头用来供电,其他的连接就是把对应的引脚连接起来就行
在这里插入图片描述

SPI模式设置

前文中也提到了,ADIS16505对SPI的模式进行了严格的要求,所以这里也是要先把这些值和要求对应起来

        /* 高位优先 */
        bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
        /* 模式2,CPOL = 1 (polarity), CPHA = 1 (phase) */
        bcm2835_spi_setDataMode(BCM2835_SPI_MODE3);
        /* 时钟分频 */
        bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256);

寄存器读取

单个寄存器的读取按照datasheet图40中的示意来就可以,先把地址和0x00合并,然后再发给SPI,不过这里需要注意的是,我是用的函数是bcm2835_spi_transfernb(),这个是在发送的同时还会返回OUTPUT值,不过根据我的测试来看这里接收到的返回值是上一次发送得到的值,这好像和图39也是可以对应上的,所以我这里采取了很笨的方法,就是测试的时候,因为只是要看值对不对,所以就发两次就好了;后边因为是连续读取寄存器,所以我在这次发送的值,OUTPUT值我就再下一次transfetnb()后再赋值给这个变量。

uint16_t ADIS16505::adisReadReg(uint16_t addr) {
    // 因为在SPI读取的时候,要延迟一帧才能接收到数据,所以在外面也要transfernb一次
    if (addr == BURST_CMD)
        return false;

    uint8_t wd[2] = {addr, 0x00};
    uint8_t rdat[2] = {0, 0};
    CS_LOW();
    bcm2835_spi_transfernb(to_char(wd), to_char(rdat), sizeof(wd));
    CS_HIGH();
    bcm2835_delayMicroseconds(tSTALL);
    uint16_t val = (rdat[0] << 8) | rdat[1];
    return val;
}

向寄存器写数据

一些寄存器,比如滤波等级、更新频率,以及可以选择的BURST READ模式,都是要我们向对应的寄存器写值才行的,所以要像寄存器写一定的值,相对麻烦的是写的时候,分别要写地址和数据两个内容,而且每个数据也是要用0x00填满的
在这里插入图片描述

void ADIS16505::adisWriteReg(uint16_t addr, uint16_t val) {
    // 写地址的话是要写两个内容进去
    uint16_t txBuf1 = ((addr | 0x80) << 8) | (val & 0xFF);
    uint8_t tx_data1[2] = {txBuf1 >> 8, txBuf1 & 0xFF};
    uint16_t txBuf2 = ((addr + 1 | 0x80) << 8) | (0 & 0xFF);
    uint8_t tx_data2[2] = {txBuf2 >> 8, txBuf2 & 0xFF};

    uint8_t rdat[2] = {0, 0};
    CS_LOW();
    bcm2835_spi_transfernb(to_char(tx_data1), to_char(rdat), sizeof(tx_data1));
    CS_HIGH();
    bcm2835_delayMicroseconds(tSTALL);  
    CS_LOW();
    bcm2835_spi_transfernb(to_char(tx_data2), to_char(rdat), sizeof(tx_data2));
    CS_HIGH();
    bcm2835_delayMicroseconds(tSTALL);
}

连续读取

连续读取就是像之前说的,用上一次发送时接收的数据赋给下一次就好了,所以在函数里面第一次接收到的值是没用的

void ADIS16505::adisSingleRead() {
    // 先读取,都是两个16位合并成一个32位
    auto unused = adisReadReg(X_GYRO_LOW);  // 这里这里读到的是上一次发送的指令的内容
    auto x_g_l = adisReadReg(X_GYRO_OUT);   // X轴角速度
    auto x_g_o = adisReadReg(Y_GYRO_LOW);    
    auto y_g_l = adisReadReg(Y_GYRO_OUT);   // Y轴角速度
    auto y_g_o = adisReadReg(Z_GYRO_LOW);
    auto z_g_l = adisReadReg(Z_GYRO_OUT);   // Z轴角速度
    auto z_g_o = adisReadReg(X_ACCL_LOW);
    auto x_a_l = adisReadReg(X_ACCL_OUT);   // X轴加速度
    auto x_a_o = adisReadReg(Y_ACCL_LOW);
    auto y_a_l = adisReadReg(Y_ACCL_OUT);   // Y轴加速度
    auto y_a_o = adisReadReg(Z_ACCL_LOW);
    auto z_a_l = adisReadReg(Z_ACCL_OUT);   // Z轴加速度
    auto z_a_o = adisReadReg(TEMP_OUT);

    // 合并OUT和LOW
    gyro_raw_[0] = ((int32_t(x_g_o) << 16) + int32_t(x_g_l)) * M_PI / 180.0 / GYRO_SENSITIVITY;
    gyro_raw_[1] = ((int32_t(y_g_o) << 16) + int32_t(y_g_l)) * M_PI / 180.0 / GYRO_SENSITIVITY;
    gyro_raw_[2] = ((int32_t(z_g_o) << 16) + int32_t(z_g_l)) * M_PI / 180.0 / GYRO_SENSITIVITY;
    acc_raw_[0] = ((int32_t(x_a_o) << 16) + int32_t(x_a_l))  / ACCL_SENSITIVITY;
    acc_raw_[1] = ((int32_t(y_a_o) << 16) + int32_t(y_a_l))  / ACCL_SENSITIVITY;
    acc_raw_[2] = ((int32_t(z_a_o) << 16) + int32_t(z_a_l))  / ACCL_SENSITIVITY;
}

采集框架

采集流程我觉得还是非常简单的,主要就分为两步

  1. 先进行整体的初始化,包括SPI模式设置、检验是否连接成功,以及滤波等级、更新频率的设置等
  2. 利用ROS里的loop rate控制单次采集的频率
    最终可以采集得到加速度和角速度,下面的图是基于ROS sensors::msg类型的数据
    在这里插入图片描述
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值