树莓派IIC通讯获取BMI08x IMU数据进行姿态解算,并通过UART/TCP在rviz上显示

2023-05-16

截至2021年,树莓派出的最新款应该是Raspberry Pi 400,设计得跟键盘一样,很难想象到这是个树莓派,尤其是它的标语写的很好“你的下一个电脑,何必是电脑”,不言而喻。反正我看到后就有很想买的冲动,它对于很多硬件通讯方面的测试以及开发一些小玩意还是很有帮助的,如下图所示。

(图片来源自Buy a Raspberry Pi 400 Personal Computer Kit – Raspberry Pi)

最近刚好玩了一个小项目,通过树莓派IIC采集Bosch BMI08x IMU数据并进行姿态解算,然后通过串口或TCP的方式传输至PC端进行可视化。具体过程如下:

目录

1. 树莓派的IIC通讯

2. 博世BMI08x IMU数据获取及其姿态解算

2.1 IMU数据获取

2.2 姿态解算算法

3. UART或TCP数据传输并在rviz上显示


1. 树莓派的IIC通讯

树莓派运行的是MPU级别的处理器,因此可以运行桌面级系统,也就是RaspberryPi-OS (previously called Raspbian),应该是基于Linux内核又封装的一层UI。最新的树莓派系统可以在https://www.raspberrypi.com/software/下载到,然后通过Raspberry Pi Imager软件将系统烧录至一个SD卡上。SD再插在树莓派上就可以开始用了。

树莓派预留有40个引脚的GPIO PIN,可以进行IIC,SPI,UART等通讯的开发,还支持WIFI,蓝牙,网卡等。在终端输入

sudo raspi-config

可设置将哪些接口使能。尤其SSH使能还是挺方便的,因为默认树莓派默认是装有VNC server。PC端再安装VNC viewer便可以远程至树莓派,而不需要额外添加显示器。

 在Linux下的i2c-tools是一个很方便的i2c调试工具(通过sudo apt install i2c-tools安装)。可以查看接入了几个IIC设备,设备地址,读写寄存器等。

比如上图,我通过sudo i2cdetect -l可以查看到总线1接入了设备。再通过sudo i2cdetect -y 1可查询到总线1上又连有两个IIC slave,地址分别为0x19和0x69。用i2cdump指令可获取当前设备地址的寄存器0x0~0xFF的值。这个工具还是挺方便,尤其是获取IIC的设备地址。

  • 如果是python开发,用smbus库可实现对IIC硬件的读写。
import smbus
bus = smbus.SMBus(1)
address = 0x68
bus.write_byte_data(address, 0x6B, 0x00)  #写寄存器
bus.read_byte_data(address, 0x0)          #读寄存器
  • 如果是C语言开发,目前主要wiringpi库,也非常容易。主要操作如下。
wiringPiSetup();

int dev_handle = wiringPiI2CSetup(0x19);  //0x19为设备地址
uint8_t register = 0x0;
uint8_t value = wiringPiI2CReadReg8(dev_handle, register); //读寄存器0,value为读取到的值
uint8_t value_write = 0xF;
value = wiringPiI2CReadReg8(dev_handle, register, value_write); //写寄存器0的值为0xF
  • 当然我也看到网上有大神通过GPIO操作手写IIC协议, 详见

树莓派之GPIO模拟i2c读取MPU6050数据https://blog.csdn.net/weixin_36181191/article/details/114020192icon-default.png?t=LA92https://blog.csdn.net/weixin_36181191/article/details/114020192

树莓派的IIC通讯方法和调试就总结到这。

2. 博世BMI08x IMU数据获取及其姿态解算

2.1 IMU数据获取

以前比较火的IMU是InvenSense公司出的MPU系列,后来Bosch也开始做。这次用BMI08x系列采集数据(BMI088和bmi085引脚和寄存器定义几乎一样,只是量程等方面有差别),它的数据手册可以在Inertial Measurement Unit BMI088 | Bosch Sensortec下载到。官方还给出了其示例程序(https://github.com/BoschSensortec/BMI08x-Sensor-API),但需要配合他们所开发的Application board 3.0开发板才能用。不过正如它的README所说,在自己的工程中添加bmi08a.c, bmi08g.c,bmi08x_defs.h and bmi08x.h就好了。此外,我还看到另外一个大神用STM32基于SPI通讯方式实现的例程GitHub - SEASKY-Master/BMI088_Master,及其演示效果。开源BMI088六轴姿态传感器_哔哩哔哩_bilibili-#BMI088# #六轴姿态传感器# #STM32f405RGT6#开源一款BMI088六轴姿态传感器,相比普通姿态传感器有何优势大家自己百度一下,就会知道了开源仓库如下:https://github.com/SEASKY-Master/BMI088_Masterhttps://www.bilibili.com/video/BV1Dp4y1C7rC

这里我参考官方给的read_sensor_data例程。在common.c文件下对IIC寄存器的读写进行了修改,如下。

common.h

/**\
 * Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 **/

#ifndef COMMON_H
#define COMMON_H


/*! CPP guard */
#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include "bmi08x_defs.h"

/*!
 *  @brief Function for reading the sensor's registers through I2C bus.
 *
 *  @param[in] reg_addr     : Register address.
 *  @param[out] reg_data    : Pointer to the data buffer to store the read data.
 *  @param[in] length       : No of bytes to read.
 *  @param[in] intf_ptr     : Interface pointer
 *
 *  @return Status of execution
 *  @retval = BMI08X_INTF_RET_SUCCESS -> Success
 *  @retval != BMI08X_INTF_RET_SUCCESS  -> Failure Info
 *
 */
BMI08X_INTF_RET_TYPE bmi08x_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr);

/*!
 *  @brief Function for writing the sensor's registers through I2C bus.
 *
 *  @param[in] reg_addr     : Register address.
 *  @param[in] reg_data     : Pointer to the data buffer whose value is to be written.
 *  @param[in] length       : No of bytes to write.
 *  @param[in] intf_ptr     : Interface pointer
 *
 *  @return Status of execution
 *  @retval = BMI08X_INTF_RET_SUCCESS -> Success
 *  @retval != BMI08X_INTF_RET_SUCCESS  -> Failure Info
 *
 */
BMI08X_INTF_RET_TYPE bmi08x_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr);

/*!
 * @brief This function provides the delay for required time (Microsecond) as per the input provided in some of the
 * APIs.
 *
 *  @param[in] period_us    : The required wait time in microsecond.
 *  @param[in] intf_ptr     : Interface pointer
 *
 *  @return void.
 *
 */
void bmi08x_delay_us(uint32_t period, void *intf_ptr);

/*!
 *  @brief Function to select the interface between SPI and I2C.
 *
 *  @param[in] bma      : Structure instance of bmi08x_dev
 *  @param[in] intf     : Interface selection parameter
 *                          For I2C : BMI08X_I2C_INTF
 *                          For SPI : BMI08X_SPI_INTF
 *  @param[in] variant  : Sensor variant parameter
 *                          For BMI085 : BMI085_VARIANT
 *                          For BMI088 : BMI088_VARIANT
 *
 *  @return Status of execution
 *  @retval 0 -> Success
 *  @retval < 0 -> Failure Info
 */
int8_t bmi08x_interface_init(struct bmi08x_dev *bma, uint8_t intf, uint8_t variant);

/*!
 *  @brief Prints the execution status of the APIs.
 *
 *  @param[in] api_name : Name of the API whose execution status has to be printed.
 *  @param[in] rslt     : Error code returned by the API whose execution status has to be printed.
 *
 *  @return void.
 */
void bmi08x_error_codes_print_result(const char api_name[], int8_t rslt);


#ifdef __cplusplus
}
#endif /* End of CPP guard */

#endif /* COMMON_H */

common.c

/**
 * Copyright (C) 2021 Bosch Sensortec GmbH. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "common.h"

/******************************************************************************/
/*!                       Macro definitions                                   */

#define BMI08X_READ_WRITE_LEN  UINT8_C(46)

int dev_acc;
int dev_gyro;

/******************************************************************************/

/*!                Static variable definition                                 */
/*! Variable that holds the I2C device address or SPI chip selection for accel */
uint8_t acc_dev_add;

/*! Variable that holds the I2C device address or SPI chip selection for gyro */
uint8_t gyro_dev_add;

/******************************************************************************/

/*!                User interface functions                                   */
/*!
 * I2C read function map to COINES platform
 */
BMI08X_INTF_RET_TYPE bmi08x_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{

    uint8_t dev_addr = *(uint8_t*)intf_ptr;
    int fd;
    if(dev_addr == 0x18 || dev_addr == 0x69)
        fd = dev_acc;
    else if (dev_addr == 0x68 || dev_addr == 0x69)
        fd = dev_gyro;
    else
        return -1;

    if(len == 1)
    {
        int tmp = wiringPiI2CReadReg8(fd, reg_addr);
        printf("Device %d read register 0x%x value = 0x%x\n", fd, reg_addr, tmp);
        reg_data[0] = ((uint8_t)tmp);
        return 0;
    }

    printf("The reading length is %d \n", len);

    int i = 0;
    for(i = 0; i < len; i++)
    {
        int tmp = wiringPiI2CReadReg8(fd, reg_addr+i);
        printf("Device %d read register 0x%x value = 0x%x\n", fd, reg_addr+i, tmp);
        reg_data[i] = ((uint8_t)tmp);
    }

    return 0;
}

/*!
 * I2C write function map to COINES platform
 */
BMI08X_INTF_RET_TYPE bmi08x_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
    uint8_t dev_addr = *(uint8_t*)intf_ptr;
    int fd;
    if(dev_addr == 0x18 || dev_addr == 0x69)
        fd = dev_acc;
    else if (dev_addr == 0x68 || dev_addr == 0x69)
        fd = dev_gyro;
    else
        return -1;

    if(len == 1)
    {

        int tmp = wiringPiI2CWriteReg8(fd, reg_addr, reg_data[0]);
        printf("Device %d write register 0x%x value = 0x%x\n", fd, reg_addr, reg_data[0]);

        return 0;
    }

    printf("The writing length is %d \n", len);

    int i = 0;
    for(i = 0; i < len; i++)
    {
        int tmp = wiringPiI2CWriteReg8(fd, reg_addr+i, reg_data[i]);
        printf("Device %d write register 0x%x value = 0x%x\n", fd, reg_addr+i, reg_data[i]);
    }

    return 0;
}

/*!
 * Delay function map to COINES platform
 */
void bmi08x_delay_us(uint32_t period, void *intf_ptr)
{
    usleep(period*10);
}

/*!
 *  @brief Function to select the interface between SPI and I2C.
 *  Also to initialize coines platform
 */
int8_t bmi08x_interface_init(struct bmi08x_dev *bmi08x, uint8_t intf, uint8_t variant)
{

    int8_t rslt = BMI08X_OK;

    if (bmi08x != NULL)
    {
        /* Bus configuration : I2C */
        if (intf == BMI08X_I2C_INTF)
        {
            printf("I2C Interface \n");

            /* To initialize the user I2C function */
            acc_dev_add = BMI08X_ACCEL_I2C_ADDR_PRIMARY;
            gyro_dev_add = BMI08X_GYRO_I2C_ADDR_PRIMARY;
            bmi08x->intf = BMI08X_I2C_INTF;
            bmi08x->read = bmi08x_i2c_read;
            bmi08x->write = bmi08x_i2c_write;
        }

        /* Selection of bmi085 or bmi088 sensor variant */
        bmi08x->variant = variant;

        /* Assign accel device address to accel interface pointer */
        bmi08x->intf_ptr_accel = &acc_dev_add;


        /* Assign gyro device address to gyro interface pointer */
        bmi08x->intf_ptr_gyro = &gyro_dev_add;

        /* Configure delay in microseconds */
        bmi08x->delay_us = bmi08x_delay_us;

        /* Configure max read/write length (in bytes) ( Supported length depends on target machine) */
        bmi08x->read_write_len = BMI08X_READ_WRITE_LEN;
    }
    else
    {
        rslt = BMI08X_E_NULL_PTR;
    }

    return rslt;
}

/*!
 *  @brief Prints the execution status of the APIs.
 */
void bmi08x_error_codes_print_result(const char api_name[], int8_t rslt)
{
    if(rslt == BMI08X_OK)
        printf("%s --- successful\r\n", api_name);

    if (rslt != BMI08X_OK)
    {
        printf("%s\t", api_name);
        if (rslt == BMI08X_E_NULL_PTR)
        {
            printf("Error [%d] : Null pointer\r\n", rslt);
        }
        else if (rslt == BMI08X_E_COM_FAIL)
        {
            printf("Error [%d] : Communication failure\r\n", rslt);
        }
        else if (rslt == BMI08X_E_DEV_NOT_FOUND)
        {
            printf("Error [%d] : Device not found\r\n", rslt);
        }
        else if (rslt == BMI08X_E_OUT_OF_RANGE)
        {
            printf("Error [%d] : Out of Range\r\n", rslt);
        }
        else if (rslt == BMI08X_E_INVALID_INPUT)
        {
            printf("Error [%d] : Invalid input\r\n", rslt);
        }
        else if (rslt == BMI08X_E_CONFIG_STREAM_ERROR)
        {
            printf("Error [%d] : Config stream error\r\n", rslt);
        }
        else if (rslt == BMI08X_E_RD_WR_LENGTH_INVALID)
        {
            printf("Error [%d] : Invalid Read write length\r\n", rslt);
        }
        else if (rslt == BMI08X_E_INVALID_CONFIG)
        {
            printf("Error [%d] : Invalid config\r\n", rslt);
        }
        else if (rslt == BMI08X_E_FEATURE_NOT_SUPPORTED)
        {
            printf("Error [%d] : Feature not supported\r\n", rslt);
        }
        else if (rslt == BMI08X_W_FIFO_EMPTY)
        {
            printf("Warning [%d] : FIFO empty\r\n", rslt);
        }
        else
        {
            printf("Error [%d] : Unknown error code\r\n", rslt);
        }
    }
}

当然还要加入树莓派初始化和板子初始化(设置一些高低电平,带宽,量程等)的部分,这个可以放在一个文件下定义。

bool rasp_init()
{
    // raspberry pi setting
    if(-1 == wiringPiSetup())
    {
        std::cout << "setup error\n";
        return false;
    }

    // Handle of acc
    dev_acc = wiringPiI2CSetup(0x19);
    if (dev_acc == -1)
    {
        std::cout << "no accel(BMI088) i2c device found \n";
        return false;
    }
    else
    {
        std::cout << "accel(BMI088) i2c device found: " << dev_acc << "\n";
    }

    // Handle of gyro
    dev_gyro = wiringPiI2CSetup(0x69);
    if (dev_gyro == -1)
    {
        std::cout << "no gyro(BMI088) i2c device found \n";
        return false;
    }
    else
    {
        std::cout << "gyro(BMI088) i2c device found: " << dev_gyro << "\n";
    }

    return true;
}

/*!
 *  @brief This internal API is used to initializes the bmi08x sensor
 *  settings like power mode and OSRS settings.
 *
 *  @param[in] void
 *
 *  @return void
 *
 */
static int8_t init_bmi08x(void)
{
    int8_t rslt;

    rslt = bmi08a_init(&bmi08xdev);
    bmi08x_error_codes_print_result("bmi08a_init", rslt);

    if (rslt == BMI08X_OK)
    {
        rslt = bmi08g_init(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08g_init", rslt);
    }

    if (rslt == BMI08X_OK)
    {
        printf("Uploading config file !\n");
        rslt = bmi08a_load_config_file(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08a_load_config_file", rslt);
    }

    if (rslt == BMI08X_OK)
    {
        bmi08xdev.accel_cfg.odr = BMI08X_ACCEL_ODR_1600_HZ;

        if (bmi08xdev.variant == BMI085_VARIANT)
        {
            bmi08xdev.accel_cfg.range = BMI085_ACCEL_RANGE_16G;
        }
        else if (bmi08xdev.variant == BMI088_VARIANT)
        {
            bmi08xdev.accel_cfg.range = BMI088_ACCEL_RANGE_24G;
        }

        bmi08xdev.accel_cfg.power = BMI08X_ACCEL_PM_ACTIVE; /*user_accel_power_modes[user_bmi088_accel_low_power]; */
        bmi08xdev.accel_cfg.bw = BMI08X_ACCEL_BW_NORMAL; /* Bandwidth and OSR are same */

        rslt = bmi08a_set_power_mode(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08a_set_power_mode", rslt);

        rslt = bmi08a_set_meas_conf(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08a_set_meas_conf", rslt);

        bmi08xdev.gyro_cfg.odr = BMI08X_GYRO_BW_230_ODR_2000_HZ;
        bmi08xdev.gyro_cfg.range = BMI08X_GYRO_RANGE_250_DPS;
        bmi08xdev.gyro_cfg.bw = BMI08X_GYRO_BW_230_ODR_2000_HZ;
        bmi08xdev.gyro_cfg.power = BMI08X_GYRO_PM_NORMAL;

        rslt = bmi08g_set_power_mode(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08g_set_power_mode", rslt);

        rslt = bmi08g_set_meas_conf(&bmi08xdev);
        bmi08x_error_codes_print_result("bmi08g_set_meas_conf", rslt);
    }

    return rslt;

}

初始化完成后便可直接调用bmi08a.c和bmi08g.c里面的bmi08a_get_data和bmi08g_get_data函数分别获取加速度和角速度数据了。我仔细看了官方的例程,不得不说写得真是严谨和规范。

不过比较坑的是,上传配置文件和获取加速度计和陀螺仪状态是有问题,我查了下手册,压根就没有定义上传配置操作的寄存器,以至于我还在官方论坛咨询,也有可能是保留以后用吧。

 总之,IIC数据可以通过树莓派获取到了。

2.2 姿态解算算法

姿态解算是指通过6轴或者9轴数据通过融合等方式得到三维空间中刚体的姿态。描述三维空间中刚体姿态的方法有欧拉角,旋转矩阵和四元数,这三者之间都能相互转换。不过欧拉角存在奇异值问题(万向锁),旋转矩阵存在不是正交阵的问题且运算量大,一般在计算机中采用四元数的方式去描述。

关于姿态解算,常用的算法有以下几种:

  1. 卡尔曼滤波器EKF
  2. Mahony的方向余弦矩阵算法
  3. Madgwick的梯度下降姿态解算算法

关于这几种方法好坏,可参见如下。从零开始的无人船制作-姿态解算算法的选择 - 知乎主流的姿态解算算法有那么多种,无人船\车\机究竟选哪一种比较合适呢 前言简单介绍一下制作的无人船的结构: 用树莓派或者英伟达Jetson Nano做上位机,STM32做下位机。上位机跑的是Python程序,负责和地面站通信,…https://zhuanlan.zhihu.com/p/82973264

这些算法网上都可以找到现成的,无论是python,matlab还是C。我目前暂时只试验了complementary filter方法,可在https://github.com/ccny-ros-pkg/imu_tools上找到,仅一个ComplementaryFilter类就实现了,也很方便移植,输出得到的是四元数四个参数,以及角速度修正之后的值。

3. UART或TCP数据传输并在rviz上显示

3.1 UART通讯

前面经过树莓派的数据采集以及姿态解算,我们需要将数据再次上传至PC端进行原始数据分析和可视化。树莓派可进行的方法有TTL串口通讯,有线或者无线wifi的形式通过tcp/ip协议进行传输。无论是串口还是TCP都可采用标准的modbus serial/tcp协议,如python下的modbus_tk库,C语言实现的libmodbus库等,后面再专门花时间说这两个库。

我直接使用的自由协议,也是很多IMU模块公司采用的,比如以0x55开头,然后计算长度和校验位。

树莓派有两个串口,默认将 serial0 映射到外接GPIO的15、16脚,将 serial1 映射到BT蓝牙上。

网上很多说需要将这两个映射互换一下,也就是通过更改/boot/config.txt文件,在后面加入dtoverlay=pi3-miniuart-bt。

不过请先看官方在/boot/overlays/README里面的一句话。也就是如果使用树莓派系统我们可以直接通过打开/dev/serial0的方式打开串口即可。

互换后通过/dev/ttyS0和/dev/ttyAMA0的方式去获取串口,下篇博文做了一个很好的测试。树莓派教程 - 1.5 树莓派GPIO库wiringPi 使用硬件串口ttyAMA0与ttyS0_Mark_md的博客-CSDN博客上一篇介绍 ttyS0串口的用法,也说到了此串口利弊,可能会出现乱码,但绝对能满足绝大部分的要求。本节使用 /dev/ttyAMA0 的方法,实际使用过程中慎用。可能造成无法启动的情况,概不负责,仅供参考。本节硬件连接和c程序,参考我的上一篇:https://blog.csdn.net/Mark_md/article/details/107143057这篇文章https://shumeipai.nxez.com/2016/08/08/solution-raspberry-pi3-seri..https://blog.csdn.net/Mark_md/article/details/107181151?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~aggregatepage~first_rank_ecpm_v1~rank_v31_ecpm-1-107181151.pc_agg_new_rank&utm_term=ttyama0+%E6%A0%91%E8%8E%93%E6%B4%BE&spm=1000.2123.3001.4430

结论是:

  • /dev/ttyS0 更像是单片机中的一个一个字节去查询串口中断。
  • /dev/ttyAMA0 更像是串口开启了硬件的 FIFO/DMA 。

 也就是即便不互换也可以直接用。更何况官方说直接使用/dev/serial0是推荐的用法。

同样打开串口和发送数据也可以用wiringPI库。

int fd;
if(wiringPiSetup() < 0)
	return NULL;
if((fd = serialOpen("/dev/serial0", 115200))<0)
{
	printf("Serial Open Failed!\n");
	return NULL;
}
else
	printf("Serial Open Succeed!\n");

不过用wiringPI库写指令的时候,会默认将0当作终止符。好在Linux下基本都是文件操作,

可直接使用ssize_t write (int fd, const void * buf, size_t count)和ssize_t read(int fd, void * buf, size_t count)进行读写操作。

3.2 rviz显示

rviz是ROS里面的可视化工具,还是非常强大了,基于了轻量级的游戏引擎OGRE。如果是使用虚拟机安装的ROS。可设置串口将PC端的物理串口地址映射到虚拟机的Linux系统,这里的COM1就对应Linux下的ttyS0。当然我们还需要打开它的可执行权限,sudo chmod 777 /dev/ttyUSB0。

 ROS的serial包仅在kinetic和melodic下有,不过可以下载源码编译后放在自己的工程下用。

至于显示,则继续使用imut_tools下的rviz_imu_plugin工具,编译后打开rviz的add plugin可以找到rviz_imu_plugin。

 最终效果如下:

Enjoy!

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

树莓派IIC通讯获取BMI08x IMU数据进行姿态解算,并通过UART/TCP在rviz上显示 的相关文章

随机推荐

  • docker离线安装

    1 下载离线包 docker官网下载地址 本示例下载的是 xff1a docker 19 03 14 tgz 2 解压到对应目录 解压文件 span class token function tar span xzvf docker 19
  • 2013年:一个技术领导的启程

    作者 xff1a 朱金灿 来源 xff1a http blog csdn net clever101 又到一年总结时 总的来说 xff0c 这一年忙碌而充实 xff0c 现在有点胸中有千言却又不知从何说起 可能每一个希望有所作为的开发人员都
  • STM32——硬件IIC从机通信

    前言 xff1a 根据网上的资料 xff0c 大部分网友表示STM32自带的硬件IIC存在bug xff0c 读写时很容易卡死 自己在调试的时候也出现卡死的情况 xff0c 最后一点一点调试 xff0c 也还是调通了 本文将记录自己调试ST
  • HI3516的编译参数-mcpu=cortex-a7、-mfloat-abi=softfp和-mfpu=neon-vfpv4

    前言 Hi3516A具有浮点运算单元和neon 文件系统中的库是采用软浮点和neon编译而成 xff0c 因此所有Hi3516A板端代码编译时需要在Makefile里面添加选项 mcpu 61 cortex a7 mfloat abi 61
  • 算法移植优化基础

    PS xff1a 为了面试准备的 xff0c 总结的有点粗糙 ARM xff1a Advanced RISC Machines xff0c ARM架构是面向低预算市场设计的第一款RISC微处理器 xff0c 基本是32位单片机的行业标准 x
  • DBoW2在windows上的vs工程搭建方法

    xfeff xfeff 注释 xff1a 蓝体字是 opencv249 专用的修改 xff1b 黑体字是 opencv3 需要的修改 环境 xff1a vs2012 32bit 叙述比较简略 不明之处可以留言 1 配置 opencv 2 4
  • MSCKF_vio学习笔记

    最近因为项目需求 xff0c 对MSCKF vio的论文和代码进行了一番研读 xff0c 现将学习过程记下 MSCKF vio是一种基于多状态约束卡尔曼滤波器的双目视觉里程计 其中多状态约束是指将多帧图像的相机位姿加入卡尔曼状态向量中 xf
  • MSCKF2.0(Mingyang Li-IJRR2013) 论文要点总结

    论文 xff1a Li M Mourikis A I High precision consistent EKF based visual inertial odometry J The International Journal of R
  • SLAM,SLAM+IMU的状态估计问题描述-个人理解

    1 状态估计问题 令状态变量为x 61 x1 x2 x3 观测变量为z 61 z1 z2 z3 状态估计问题等同于求解条件概率分布 xff1a P x z xff0c 在当前观测状态z下的状态x分布 xff0c 也就是最可能的状态是什么 由
  • VINS-Fusion运行时的段错误(核心已转储)解决方法

    平台 ubuntu16 04 43 ROS 问题描述 xff1a 前两天VINS的原作者开源了VINS Fusion的双目版以及给出了和GPS融合的一个demo xff0c 所以试着运行下数据集 每次单目运行10s左右 xff0c 双目1
  • Jetson Xavier NX 刷机+更换清华源完美讲解

    当你拿到梦寐以求的NVIDIA Jetson Xavier NX开发板时 xff0c 第一个工作就是要刷机 究竟要怎么做呢 xff1f Let s go 这种板子有两种 xff0c 一种是带Micro SD卡槽的 xff0c 可以插入小型S
  • vmware下vmdk文件越来越大的解决方法探讨

    前段时间在vmware下面安装了ubuntu镜像 xff0c 用了一段时间后发现ubuntu的vmdk文件越来越大 xff0c 达到了31 6GB xff0c 如下图所示 而且随着继续安装新的软件仍然在增大中 即使在ubuntu里面删除了文
  • 工作日志的作用

    朱金灿 公司提倡我们每天都写工作日志 这使得我思考工作日志的作用 我想了一下 xff0c 工作日志应该要起两个作用 xff1a 1 计划作用 俗话说 xff1a 凡事预则立 xff0c 不预则废 最好在每天开始工作前先在工作日志上写下今天要
  • Tracealyzer for FreeRTOS(FreeRTOS+Trace) 详解(源码解析+移植)

    原文 xff1a http blog csdn net zcshoucsdn article details 54670473 最近公司搞新项目 xff0c 需要使用FreeRTOS xff0c 之前只有Linux的基础 xff08 学了个
  • 职场 | Intel因特尔2019届软件工程师秋招笔试题

    同样的本文是对笔试过程中 xff0c 仍然记得的考点的查漏补缺 一共分为三部分 xff1a 选择题 编程题 附加题 时间是两个小时 xff0c 个人感觉因特尔的笔试题对于内核 xff0c 内存管理等方面的考点较多 xff0c 具体属于什么科
  • Linux-LCD驱动实现

    一 帧缓冲设备驱动在Linux子系统中的结构 xff1a 二 帧缓冲相关的重要数据结构 从帧缓冲设备驱动程序结构 看 xff0c 该驱动主要跟fb info结构体有关 xff0c 该结构体记录了帧缓冲设备的全部信息 xff0c 包括设备的设
  • ROS之命名空间

    已经学ROS快两个月了 xff0c 一开始对ROS 命名空间 xff0c 参数 xff0c 参数服务器 xff0c 重映射没认真看 xff0c 后来发现很重要 xff0c 它是学习ROS代码的基础 我们都知道ros以topic通信 xff0
  • CircleProgressBar 一个界面漂亮功能强大的圆形进度条,支持多种属性

    CircleProgressBar 项目地址 xff1a HuTianQi CircleProgressBar 简介 xff1a 一个界面漂亮功能强大的圆形进度条 xff0c 支持多种属性 xff0c 如圆环的大小颜色 xff0c 进度条的
  • Android 串口调试助手

    Android SerialPort Tool 项目地址 xff1a licheedev Android SerialPort Tool 简介 xff1a Android 串口调试助手 更多 xff1a 作者 提 Bug 标签 xff1a
  • 树莓派IIC通讯获取BMI08x IMU数据进行姿态解算,并通过UART/TCP在rviz上显示

    截至2021年 xff0c 树莓派出的最新款应该是Raspberry Pi 400 xff0c 设计得跟键盘一样 xff0c 很难想象到这是个树莓派 xff0c 尤其是它的标语写的很好 你的下一个电脑 xff0c 何必是电脑 xff0c 不