目录
一、目标
将Hi3569的视觉检测模块融入原有的ROS通讯系统中。由于手头Hi3569原生支持NNIE加速的网络为YOLOv3,因此可以视作将Hi3569的YOLOv3替换掉ROS中使用的YOLOv4。
二、方案
- Hi3569提供的原生图像格式为YUV(420),YOLOv3模型输入图像格式为RGB,因此需要适配
- Hi3569存在大小核(A53和A73各两个),同时将LiteOS和HiLinux两个系统分别跑在了两个核上,其中LiteOS跑摄像头硬件,HiLinux跑YOLOv3,因此需要在两个核间完成数据交换
- Hi3569上无法运行ROS,因此需要其他方案传输数据到Xavier端,这里我们选择使用UDP
- 最终解析传输到Xavier端的UDP包,并转换成相应的ROSmsgs发出
三、 实现过程
1. 图像格式适配
摄像头给出的图像为YUV格式,且尺寸为1080P,因此我们需要解决格式和尺寸两个问题。
方案一:
HiLinux端获取到数据后调用ISP模块的API完成处理
方案二:
在HiLinux端编译安装OpenCV库
方案三:
硬件端完成图像裁剪(416*416),YOLO模型通过RuyiStudio转换
显然,最终选择了方案三。其中YOLO的转换需要在RuyiStudio(只有win端)中完成转换。
2. Hi3569双核内的数据交换
这里核心思路为利用Hi3569自带的dataFIFO模块实现数据交换。下面只展示核心代码:
#include "hi_datafifo.h"
...
// HI_CHAR *buf;
const HI_S32 BLOCK_LEN = 259584;
HI_CHAR *pBuf;
static HI_DATAFIFO_HANDLE hDataFifo = HI_DATAFIFO_INVALID_HANDLE;
...
int datafifo_init(HI_U64 phyAddr)
{
HI_S32 s32Ret = HI_SUCCESS;
HI_DATAFIFO_PARAMS_S params = {3, BLOCK_LEN, HI_TRUE, DATAFIFO_READER};
s32Ret = HI_DATAFIFO_OpenByAddr(&hDataFifo, ¶ms, phyAddr);
if (HI_SUCCESS != s32Ret)
{
SAMPLE_SVP_TRACE_INFO("open datafifo error:%x\n", s32Ret);
return -1;
}
SAMPLE_SVP_TRACE_INFO("datafifo_init finish\n");
return 0;
}
static HI_S32 SAMPLE_SVP_NNIE_FillSrcData_FIFO(SAMPLE_SVP_NNIE_CFG_S *pstNnieCfg,
SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,
SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S *pstInputDataIdx,
HI_CHAR *buf)
{
HI_S32 s32Ret = HI_SUCCESS;
HI_U8 *pu8PicAddr = NULL;
HI_U32 u32SegIdx = pstInputDataIdx->u32SegIdx;
HI_U32 u32NodeIdx = pstInputDataIdx->u32NodeIdx;
pu8PicAddr = (HI_U8 *)(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64VirAddr);
memcpy(pu8PicAddr, buf, BLOCK_LEN);
return HI_SUCCESS;
}
...
void SAMPLE_SVP_NNIE_Yolov3(HI_U64 phyAddr)
{
...
s32Ret = datafifo_init(phyAddr);
...
while (1)
{
readLen = 0;
s32Ret = HI_DATAFIFO_CMD(hDataFifo, DATAFIFO_CMD_GET_AVAIL_READ_LEN, &readLen);
if (HI_SUCCESS != s32Ret)
{
printf("get available read len error:%x\n", s32Ret);
break;
}
if (readLen <= 0)
{
SAMPLE_SVP_TRACE_INFO("read length not enough********************\n");
usleep(10000);
continue;
}
s32Ret = HI_DATAFIFO_Read(hDataFifo, (HI_VOID **)&pBuf);
if (HI_SUCCESS != s32Ret)
{
printf("read error:%x\n", s32Ret);
usleep(10000);
continue;
}
/*Fill src data*/
SAMPLE_SVP_TRACE_INFO("Yolov3 start!\n");
stInputDataIdx.u32SegIdx = 0;
stInputDataIdx.u32NodeIdx = 0;
s32Ret = SAMPLE_SVP_NNIE_FillSrcData_FIFO(&stNnieCfg, &s_stYolov3NnieParam[task_id], &stInputDataIdx, pBuf);
SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret, YOLOV3_FAIL_0, SAMPLE_SVP_ERR_LEVEL_ERROR,
"Error,SAMPLE_SVP_NNIE_FillSrcData_FIFO failed!\n");
// 3. Read Done
s32Ret = HI_DATAFIFO_CMD(hDataFifo, DATAFIFO_CMD_READ_DONE, pBuf);
if (HI_SUCCESS != s32Ret)
{
printf("read done error:%x\n", s32Ret);
continue;
}
...
}
}
其中phyAddr即图像数据存放地址的头,也可以理解为头指针地址,会由摄像头驱动部分对应模块给出。
3. UDP数据传递
4. UDP数据解析与ROS消息转换
5. 提升YOLOv3检测效率(FPS从5到15)
6. 实测结果与总结
P.S. CSDN的写作工具实在是太难用了,不知道哪里开启markdown= =先存个坑