小淼博客

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、奥比中光 OrbbecSDK

I. 在JetsonNano上安装 OrbbecSDK

  1. 下载 SDK 源代码:
    git clone https://github.com/OrbbecDeveloper/OrbbecSDK.git
  2. 配置相机ID编号基本环境:
    cd OrbbecSDK/misc/scripts
    sudo chmod +x ./install_udev_rules.sh
    ./install_udev_rules.sh
    
  3. 编译生成 SDK 原生支持的例程 --> 测试工具
    cd OrbbecSDK && mkdir build && cd build &&
    cmake .. && cmake --build . --config Release
    
  4. 插入设备测试该 SDK API 功能
    cd OrbbecSDK/build/bin # build output dir
    ./OBMultiStream
    

II. 在工程中调用OrbbecSDK-API

  1. 编写基础相机调用程序: main.c
  2. 编写 CMakeLists.txt 文件:
    cmake_minimum_required(VERSION 3.1.15) # 设置最低的 cmake 版本
    project(OrbbecSDKTest) # 配置工程编译的名称
    
    add_executable(${PROJECT_NAME} main.cpp) # 添加可执行文件
    add_subdirectory("your/path/to/OrbbecSDK") # 添加 奥比中光 SDK API 编译文件路径
    
    target_link_libraries(${PROJECT_NAME} OrbbecSDK::OrbbecSDK) # 链接相关的库文件
    

🌻 Notice: 看清楚是否要使用的是 OrbbecSDK 还是 AstraSDK,其中乐视三合一的深度摄像机需要使用的是 AstraSDK.

二、奥比中光 AstraSDK

I. 在JetsonNano上安装 AstraSDK

  1. 官网 下载 SDK 源代码:
    wget https://dl.orbbec3d.com/dist/astra/v2.1.3/AstraSDK-v2.1.3-Linux-arm.zip
  2. 解压上述SDK压缩包:
    unzip AstraSDK-v2.1.3-Linux-arm.zip
    tar -xzvf AstraSDK-v2.1.3-94bca0f52e-20210611T023312Z-Linux-aarch64.tar.gz
    
  3. 编译生成 SDK 原生支持的例程 --> 测试工具
    cd AstraSDK-v2.1.3-94bca0f52e-20210611T023312Z-Linux-aarch64
    cd samples
    mkdir build && cd build
    cmake .. && make
    
  4. 插入设备测试该 SDK API 功能
    cd /bin # build output dir
    ./SimpleStreamViewer-SFML
    

II. 在工程中调用AstraSDK-API

  1. 编写基础相机调用程序: main.c
  2. 编写 CMakeLists.txt 文件:
    cmake_minimum_required(VERSION 3.1.15) # 设置最低的 cmake 版本
    project(AstraSDKTest) # 配置工程编译的名称
    
    add_executable(${PROJECT_NAME} main.cpp) # 添加可执行文件
    add_subdirectory("your/path/to/AstraSDK") # 添加 奥比中光 SDK API 编译文件路径
    
    target_link_libraries(${PROJECT_NAME} AstraSDK::AstraSDK) # 链接相关的库文件
    

三、 通过 PCL 调用

1. OpenNI 介绍

https://structure.io/openni

2. PCL测试程序

/********************************************************************************
** @ Copyright(c) $year$ $registered organization$ All Rights Reserved.
** @auth: mm1994uestc
** @date: 2023/9/16
** @desc: ***
** @Ver : V1.0.0
*********************************************************************************/
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <opencv2/opencv.hpp>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
#include <OpenNI.h>

using namespace std;

typedef pcl::PointXYZRGB PointT;
typedef pcl::PointCloud<PointT> PointCloud;

// 相机内参
const double camera_factor = 1000;
const double camera_cx = 311.0;
const double camera_cy = 244.0;
const double camera_fx = 593.0;
const double camera_fy = 588.0;

int main(int argc, char* argv[])
{
	//初始化OpenNI SDK
	openni::OpenNI::initialize();

	//打开设备
	openni::Device device;

	device.open(openni::ANY_DEVICE);

	std::cout << device.getDeviceInfo().getUri() << std::endl;

	//创建深度流
	openni::VideoStream depthStream;
	depthStream.create(device, openni::SENSOR_DEPTH);

	//配置深度流的模式
	openni::VideoMode depthMode;
	depthMode.setResolution(640, 480);
	depthMode.setPixelFormat(openni::PIXEL_FORMAT_DEPTH_1_MM);
	depthMode.setFps(30);
	depthStream.setVideoMode(depthMode);

	//打开深度摄像头
	depthStream.start();
	openni::VideoFrameRef depth_frame;
	int iMaxDepth = depthStream.getMaxPixelValue();

	//打开彩色摄像头
	cv::VideoCapture capture;
	capture.open(0);

	cv::Mat depthMat, rgbMat;

	PointCloud::Ptr cloud(new PointCloud);  // 使用智能指针创建一个空点云。
	pcl::visualization::CloudViewer viewer("Cloud Viewer");

	//循环采图
	while (true)
	{
		openni::VideoStream* pstream = &depthStream;
		int changedStreamDummy;
		openni::Status rc = openni::OpenNI::waitForAnyStream(&pstream, 1, &changedStreamDummy, 100); //等待一帧
		if (rc != openni::STATUS_OK)
			continue;

		//获取深度帧数据
		rc = depthStream.readFrame(&depth_frame);
		if (rc == openni::STATUS_OK)
		{
			auto depth = depth_frame.getData();
			auto depthWidth = depth_frame.getWidth();
			auto depthHeight = depth_frame.getHeight();

			//处理并渲染深度帧数据
			cv::Mat rawMat(depthHeight, depthWidth, CV_16UC1, (void*)depth);
			rawMat.convertTo(depthMat, CV_8UC1, 255.0 / iMaxDepth); 
			cv::imshow("Depth Viewer", depthMat); //显示深度图
		}

		capture >> rgbMat;
		cv::flip(rgbMat, rgbMat, 1);
		cv::imshow("rgb Viewer", rgbMat); //显示彩色图
		cv::waitKey(100); //不加waitkey会卡住

		cloud->clear();
		// 遍历深度图
		for (int i = 0; i < depthMat.rows; ++i)
		{
			for (int j = 0; j < depthMat.cols; ++j)
			{
				// 获取深度图中(m,n)处的值
				uchar d = depthMat.ptr<uchar>(i)[j];
				// d 可能没有值,若如此,跳过此点
				if (d == 0)
					continue;
				// d 存在值,则向点云增加一个点
				PointT p;
				// 计算这个点的空间坐标
				p.z = double(d) / camera_factor;
				p.x = (j - camera_cx) * p.z / camera_fx;
				p.y = -(i - camera_cy) * p.z / camera_fy;
				// 从rgb图像中获取它的颜色
				p.b = rgbMat.at<cv::Vec3b>(i, j)[0];
				p.g = rgbMat.at<cv::Vec3b>(i, j)[1];
				p.r = rgbMat.at<cv::Vec3b>(i, j)[2];
				// 把p加入到点云中
				cloud->points.push_back(p);
			}
		}
			
		viewer.showCloud(cloud); //显示点云
		//viewer窗口关闭则退出循环
		if (viewer.wasStopped())
			break;
	}

	//释放资源
	depthStream.stop();
	depthStream.destroy();
	device.close();
	openni::OpenNI::shutdown();
	capture.release();

	return 0;
}

🌻问题: 由于AstraAPI不能与PCL库同时在 JetsonNano上同时调用,因此上述读取传感器的方案尝试了各种办法任然无法实现,根据奥比中光技术反馈表明目前的平台性能可能不足以支持同时调用,需要升级性能更高的系统,只能作罢,后续通过迂回的方式自行实现PCD文件的输出方式完成数据的处理,代码如下:

点击查看代码
#include <cstdio>
#include <stdio.h>
#include <iostream>
#include <string> // for string and to_string()
#include <cstring>
#include <astra/astra.hpp>

#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/console/parse.h>
#include <pcl/common/transforms.h>
#include <pcl/visualization/pcl_visualizer.h>

#include <pthread.h>

using namespace std;

#define PCDFileSave 1
#define PointCloudShow 1

#define PCDFileHeader "# .PCD v.7 - Point Cloud Data file format\n"
#define PCDFileVersion "VERSION .7\n"
#define PCDFileFormat "FIELDS x y z\n" // FIELDS x y z rgb
#define PCDFileSize "SIZE 4 4 4\n" // SIZE 4 4 4 4
#define PCDFileType "TYPE F F F\n" // TYPE F F F F
#define PCDFileCount "COUNT 1 1 1\n" // #define PCDFileType "
#define PCDFileWidth "WIDTH " // WIDTH 213
#define PCDFileHeight "HEIGHT " // HEIGHT 1
#define PCDFileView "VIEWPOINT 0 0 0 1 0 0 0\n"
#define PCDFilePoints "POINTS " // POINTS 213
#define PCDFileData "DATA ascii\n"

char ProcessState = 'R';

void *keyboard_monitor(void *arg)
{
    char Key_Value = 'n';
    while(1)
    {
        Key_Value = getchar();
        if(Key_Value == 'q')
        {
            ProcessState = 'q';
            return NULL;
        }
        if(Key_Value == 'r')
        {
            ProcessState = 'r';
        }
    }
}

int main(int argc, char** argv)
{
    std::cout << "Processing Line:" << __LINE__ << std::endl;

    astra::initialize();

    astra::StreamSet streamSet;
    astra::StreamReader reader = streamSet.create_reader();

    reader.stream<astra::PointStream>().start();
    //Stores the maximum number of frames we're going to process in the loop

    while(!reader.stream<astra::PointStream>().is_available())
    {
        ;
    }

    const int maxFramesToProcess = 3000;
    //Sentinel to count the number of frames that we've processed
    int count = 0;

    //The frame processing loop

    int width, height;
    char PCDFileName[100];

    std::cout << "Processing Line:" << __LINE__ << std::endl;

    pthread_t tid;
    pthread_create(&tid, NULL, keyboard_monitor, NULL);

    char StringArray[100];

    do {
        astra::Frame frame = reader.get_latest_frame();
        const auto pointFrame = frame.get<astra::PointFrame>();

        const int frameIndex = pointFrame.frame_index();
        const short pixelValue = pointFrame.data()[0].x;

        std::cout << std::endl
                << "Point frameIndex: " << frameIndex
                << " pixelValue: " << pixelValue
                << std::endl;
#if PCDFileSave
        if(ProcessState == 'r')
        {
            sprintf(PCDFileName, "FacePCDData-%d.pcd", count);
            ProcessState = 'R';
            width = pointFrame.width();
            height = pointFrame.height();
            // // 准备pcl::PointXYZ类型的点云
            // pcl::PointCloud<pcl::PointXYZ>::Ptr AstraPointCloud(new pcl::PointCloud<pcl::PointXYZ>);

            // AstraPointCloud->width = pointFrame.width();
            // AstraPointCloud->height = pointFrame.height();
            // AstraPointCloud->is_dense = false;
            // AstraPointCloud->resize (AstraPointCloud->width * AstraPointCloud->height);

            // width = AstraPointCloud->width;
            // height = AstraPointCloud->height;

            // for (int i = 0; i < width * height; ++i)
            // {
            //     AstraPointCloud->points[i].x = pointFrame.data()[i].x;
            //     AstraPointCloud->points[i].y = pointFrame.data()[i].y;
            //     AstraPointCloud->points[i].z = pointFrame.data()[i].z;
            // }

            FILE* fd = fopen(PCDFileName, "w+");
            if(fd == NULL)
            {
                cout << "Something Error while creating FILE handle." << endl;
                break;
            }

            size_t WriteCount;
            WriteCount = fwrite(PCDFileHeader, 1, strlen(PCDFileHeader), fd);
            WriteCount = fwrite(PCDFileVersion, 1, strlen(PCDFileVersion), fd);
            WriteCount = fwrite(PCDFileFormat, 1, strlen(PCDFileFormat), fd);
            WriteCount = fwrite(PCDFileSize, 1, strlen(PCDFileSize), fd);
            WriteCount = fwrite(PCDFileType, 1, strlen(PCDFileType), fd);
            WriteCount = fwrite(PCDFileCount, 1, strlen(PCDFileCount), fd);
            sprintf(StringArray, "WIDTH %d\n", width);
            WriteCount = fwrite(StringArray, 1, strlen(StringArray), fd);
            sprintf(StringArray, "HEIGHT %d\n", height);
            WriteCount = fwrite(StringArray, 1, strlen(StringArray), fd);
            WriteCount = fwrite(PCDFileView, 1, strlen(PCDFileView), fd);
            sprintf(StringArray, "POINTS %d\n", width*height);
            WriteCount = fwrite(StringArray, 1, strlen(StringArray), fd);
            WriteCount = fwrite(PCDFileData, 1, strlen(PCDFileData), fd);

            for (int i = 0; i < width * height; ++i)
            {
                sprintf(StringArray, "%.5f %.5f %.5f\n", pointFrame.data()[i].x, pointFrame.data()[i].y, pointFrame.data()[i].z);
                WriteCount = fwrite(StringArray, 1, strlen(StringArray), fd);
            }

            fclose(fd);

            // pcl::io::savePCDFile(PCDFileName, *AstraPointCloud);
            // pcl::io::savePCDFileASCII(PCDFileName, *AstraPointCloud);
            // pcl::io::savePCDFileBinary(PCDFileName, *AstraPointCloud);
            // pcl::io::savePLYFile(PCDFileName, *AstraPointCloud);
        }
#endif

        std::cout << "Count:" << count << std::endl;
        count++;
    } while (ProcessState != 'q'); // count < maxFramesToProcess &&

    std::cout << "Press any key to continue...";
    std::cin.get();

    astra::terminate();

    std::cout << "hit enter to exit program" << std::endl;
    std::cin.get();

    pthread_join(tid, NULL);

    return 0;
}

🚀 上述代码通过使用 fwrite 功能实现 PCD 文件的保存,具体PCD文件格式 参考这里

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(PCLDemo)
set(CMAKE_CXX_STANDARD 14)

include(CheckCXXCompilerFlag)

CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)

set(ASTRA_CXX_FLAGS "-Wall -fPIC")
if (COMPILER_SUPPORTS_CXX14)
    set(ASTRA_CXX_FLAGS "${ASTRA_CXX_FLAGS} -std=c++14")
elseif( COMPILER_SUPPORTS_CXX11)
    set(ASTRA_CXX_FLAGS "${ASTRA_CXX_FLAGS} -std=c++11")
endif()

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ASTRA_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")

# 设置输出根目录为build/Debug
set(OUTPUT_DIRECTORY_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/build/${CMAKE_BUILD_TYPE})
# 设置可执行程序输出到build/Debug/bin目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/bin" CACHE PATH "Runtime directory" FORCE)
# 设置库文件输出到build/Debug/lib目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Library directory" FORCE)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIRECTORY_ROOT}/lib" CACHE PATH "Archive directory" FORCE)

find_package(PCL REQUIRED)
# 包含头文件目录
include_directories(${PCL_INCLUDE_DIRS})
# 设置依赖库链接目录
link_directories(${PCL_LIBRARY_DIRS})
# 添加预处理器和编译器标记
add_definitions(${PCL_DEFINITIONS})

# find_package(OpenCV REQUIRED)
# message("opencv dir = ${OpenCV_INCLUDE_DIRS}")
# message("Opencv lib = ${OpenCV_LIBRARIES}")
# include_directories(${OpenCV_INCLUDE_DIRS})

find_package(OpenCV REQUIRED)
# 包含头文件目录
include_directories(${OPENCV_INCLUDE_DIRS})
# 设置依赖库链接目录
link_directories(${OPENCV_LIBRARY_DIRS})

# find_package(OpenNI REQUIRED)
# 包含头文件目录
# include_directories(${OPENNI_INCLUDE_DIRS})
# 设置依赖库链接目录
# link_directories(${OPENNI_LIBRARY_DIRS})

# Library For AstraSDK API Call
set(ASTRA_SDK_PATH "/home/nvidia-mm/WorkSpace/PCL_Develop/PCL_Learning")

set(ASTRA_SDK_LIB ${ASTRA_SDK_PATH}/lib)
set(ASTRA_SDK_INCLUDE ${ASTRA_SDK_PATH}/include)
set(ASTRA_SDK_JAVA ${ASTRA_SDK_PATH}/java)

# include_directories(${ASTRA_INCLUDE_DIRS})
include_directories(${ASTRA_SDK_INCLUDE})
message("IncludePATH=${ASTRA_SDK_INCLUDE}")

# 设置依赖库链接目录
# link_directories(${ASTRA_LIBRARY_DIRS})
link_directories(${ASTRA_SDK_LIB} ${ASTRA_SDK_JAVA})

message("LibraryPATH=${ASTRA_SDK_LIB}")

set(ASTRA_LIB "${ASTRA_SDK_LIB}/libastra.so")
set(ASTRA_CORE_LIB "${ASTRA_SDK_LIB}/libastra_core.so")
set(ASTRA_CORE_API_LIB "${ASTRA_SDK_LIB}/libastra_core_api.so")

set(ASTRA_JNI_LIB "${ASTRA_SDK_JAVA}/libastra_jni.so")

message("Library=${ASTRA_LIB}")

add_executable(PCLDemo  AstraAPIPointStream.cpp key_handler.h) # CloudViewer.cpp
# set_target_properties(PCLDemo PROPERTIES) # COMPILE_FLAGS "-pthread" LINK_FLAGS "-pthread"
target_link_libraries(PCLDemo ${PCL_LIBRARIES} ${OpenCV_LIBS} ${ASTRA_JNI_LIB} ${ASTRA_LIB} ${ASTRA_CORE_LIB} ${ASTRA_CORE_API_LIB}) # ${OpenNI_LIBS}

✌️运行结果:

三、PCL数据采集输出

通过使用上述程序完成了人头 \(360°\) 环绕数据的采集工作,数据如下:
image

Reference

奥比中光官网

posted on 2023-10-26 11:33  小淼博客  阅读(73)  评论(0)    收藏  举报

大家转载请注明出处!谢谢! 在这里要感谢GISPALAB实验室的各位老师和学长学姐的帮助!谢谢~