一、前言
本项目的最终目标是实现多目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格式呈现,最终效果如下所示:-




