多目深度相机(二) mipi多目相机取流

一、前言

本项目的最终目标是实现多目mipi相机在RDK Module上的接入,以实现多目视觉、多目深度等功能,此项目也是2023年研究生电子设计竞赛作品《基于旭日x3派的垃圾分拣机器人》中视觉部分的延伸。-
在此说明下为何选择mipi相机接入而不是usb相机,usb总线是由usb host控制器控制的,带宽有限,一般来说一个PC上的usb host控制器能够接入两个usb 2.0的低分辨率摄像头,如果PC的usb host控制器较多就能接入多个摄像头,否则就只能通过PCIE采集卡进行采集。-
旭日X3M芯片只具备一个usb host控制器,同时经过简单测试,在同时取流的情况下,最多只能对一个usb摄像头取流,不符合多目相机的实时要求。-
mipi相机接入在旭日X3M芯片内部是有硬件加速的,原理上来说能够满足多个摄像头接入并同时取流,因此实现多mipi相机接入是必须的。-
本项目所有内容开源在:https://github.com/307321587/tros_code-
本项目目前实现的结构如下图所示:-

多目相机取流结构-
包括两个部分,多目相机取流TROS通信,本章首先就多目相机取流进行叙述。

二、RDK Module硬件分析

RDK Module底板原理图

旭日X3M芯片总共具备4个MIPI host输入,即MIPI host 0,1,2,3,每个host具备2数据Lane的带宽,但按照官方的说法,MIPI host0和MIPI host2是能够单独驱动使用2数据Lane带宽的,而MIPI host1和MIPI host3需要组成4数据Lane一起使用。

因此,旭日X3M芯片最多接入3个MIPI摄像头,而RDK Module将三个摄像头的接口全部引出,比较便于我们使用,其中需要注意的是,引出的CAM0和CAM1是兼容树莓派接口摄像头的,因此理论上来说,市面上各种各样的树莓派接口摄像头都可以接入RDK Module。

我们选择了一款非地平线推荐的红外树莓派相机,目前测试也是正常的,链接如下:微雪 英伟达 Jetson Nano 摄像头 IMX219模块 800万像素 红外夜视-tmall.com天猫-

红外夜视相机-
接入的效果如下图所示,我们暂时实现了双目红外mipi相机的接入:-
双目相机效果

三、双目取流程序

3.1 程序运行

本程序参考的是地平线官方例程/app/multimedia_samples/get_sif_data。

具体程序为:

https://github.com/307321587/tros_code/tree/main/mipi_camera/mipi_camera_board

注:首先需要按照多目深度相机(一) RDK Module tros消息通信编译示例 (horizon.cc)搭建好交叉编译平台

在电脑端运行:

cd mipi_camera_board
bash build.sh

在板端运行:

bash start.sh

即可看到打印log如下所示:

msg send completed!
try to get img...
normal pipe_id (1)type(11)frame_id(0)buf_index(2)w x h(1920x1080) data_type 11 img_format 0
mat success
get img cost time 2 ms

3.2 程序分析

文件结构如下所示:

--- mipi_camera_board
        --- include 
                --- encoder.h 定义了jpeg编码类encode
                --- image_publish.h 定义了tros发布者,用于发布图像消息
                --- sensor.h 定义了相机类SensorProcess
                --- sp_codec.h 地平线官方编码头文件
        --- lib 必须的库,包括相机驱动库和isp库
        --- sensor
                --- sensor_imx219.cpp 定义sensor接入的驱动口
        --- src
                --- encoder.cpp 编码类实现
                --- main.cpp 主程序
                --- sensor.cpp 相机类实现

3.2 mipi摄像头接入分析

地平线API分析

要实现多个mipi摄像头接入,需要使用地平线的底层多媒体接口VIN接口,该接口实现mipi摄像头接入最少需要以下文件:

sensor_xxx.cpp
libxxx.so
libxxx_linear.so
  • sensor_xxx.cpp定义了VIN api启动mipi相机的一些必须参数,如I2C总线号、帧率、mipi索引等,以imx219为例,一些重点的配置如下所示:

    MIPI_SENSOR_INFO_S SENSOR_2LANE_IMX219_30FPS_10BIT_LINEAR_INFO_2 = {
    .deseEnable = 0,
    .inputMode = INPUT_MODE_MIPI,
    .sensorInfo = {
    .port = 1, // sensor的逻辑编号,必须从0开始
    .dev_port = 1, // 每路 sensor 操作的驱动节点,一个驱动支持多个节点。 snsinfo 中的dev_port 必须等于pipeId,多目摄像头设置的时候需要特别注意
    .bus_type = 0,
    .bus_num = 0, // 总线号,根据具体板子硬件原理图确定 , 不配置默认 i2c5
    .fps = 30,
    .resolution = 1080,
    .sensor_addr = 0x10,
    .entry_index = 2, // sensor 使用的 mipi 索引, 0~3,对应mipi host的序号
    .sensor_mode = NORMAL_M,
    .reg_width = 16,
    .sensor_name = “imx219”,
    }
    };

我们使用的是两颗相同的imx219摄像头,因此配置中的大部分都是相同,但需要注意的是上面给出备注的几个参数,port、bus_num、entry_index等等,这些由于硬件接口的原因,都是不一样的,比如CAM2 接入的是I2C 0,mipi host2,此外图像内部的流动是基于pipe管道进行的,因此CAM1占用了pipe0即dev_port=0,CAM2就需要使用pipe1即dev_port=1

  • libxxx.so中定义了寄存器操作相关的函数,比如开流、关流、修改曝光时间等。
  • libxxx_linear.so定义了isp校正相关的参数。

这三个文件,在地平线github上的hobot-camera项目中都可以获得。

但值得注意的是,在进行双目取流的时候,文件并不能直接照搬,在使用CAM2 host2 接入摄像头时,需要将.mclk由2400修改为24,此外在相机启动时,host2在启动脚本。

.mclk = 24, // mipi 模块主时钟,目前默认是24MHz

此外在相机启动时,host2需在启动脚本中添加stop命令,使sensor在初始化前配置为stop状态

echo 1 > /sys/class/vps/mipi_host1/param/stop_check_instart

否则会报错:

[ERROR]["LOG"][src/hb_vin_mipi_host.c:286] !!! host2 MIPIHOSTIOC_INIT error, ret = -1
[ERROR]["LOG"][src/hb_vin.c:124] mipi_host 2 init error!
[ERROR]["mipi"][mipi/hb_mipi_api.c:461] hb_vin_init fail
hb mipi set mipi attr error!
[ERROR]["venc"][video/src/hb_venc.c:302] [9857.657464]HB_VENC_CreateChn[302]: [HB_VENC] HB_VENC_CreateChn:302 Failed to FindCtxByChn VeChn = 0 s32Ret = -268958736 

2023/08/20 17:12:11.622 ERROR [x3_venc_init][0173]HB_VENC_CreateChn 0 failed, eff803f0.

自封装库

通过上面的分析就很好将地平线官方的API进行封装,封装如下:

class SensorProcess {
public:
    SensorProcess(SensorSetting sensor);
    ~SensorProcess();
    int sensorSifDevInit();
    void print_sensor_info(MIPI_SENSOR_INFO_S *snsinfo);

    cv::Mat getImage();
    cv::Mat transRgbToYuv(cv::Mat rgb_img);

    int getSensorWidth(){return width_;};
    int getSensorHeight(){return height_;};

private:
    SensorSetting sensor_setting_;
    bool sensor_init_flag = false;
    int width_=0;
    int height_=0;
};

类初始化时,将sensor_xxx.cpp中的数据打包为SensorSetting传入,调用sensorSifDevInit进行初始化,完成初始化后,通过getImage()即可获得该sensor当前的图像,且是以opencv的mat格式呈现,最终效果如下所示:-

双目采图效果

x3pi上能接多个相机吗

两个相机能同步吗

没做,不过目前同时取图的话,两个相机的图像基本没有延迟,能够保持一致,后续准备做下软件同步吧