SDK 示例

本章将介绍一些 DaoAI AOI 3D Camera SDK 的常用功能示例代码,以帮助您更好地使用。

前提条件

  • 安装DaoAI AOI 软件

  • 遵循环境配置中的步骤,确保环境正确配置

引入库

在示例中,我们使用了以下几个头文件,其中aoi_ad3dcamera 开头的是用于引入DaoAI AOI 3D Camera SDK的库。

#include <cstdint>
#include <aoi_ad3dcamera/array2d.h>
#include <aoi_ad3dcamera/camera.h>
#include <aoi_ad3dcamera/frame.h>
#include <aoi_ad3dcamera/pointcloud.h>
#include <aoi_ad3dcamera/settings2d.h>
#include <aoi_ad3dcamera/settings3d.h>
#include <exception>
#include <filesystem>
#include <iostream>
#include <opencv2/core/mat.hpp>
#include <opencv2/imgcodecs.hpp>
#include <vector>

连接相机

使用 DaoAI AOI 3D Camera SDK 连接到指定类型相机。

创建相机对象 camera,并尝试连接 DaoAI::CameraMode::AD类型设备。

相机类型CameraMode分为:

  • AS: 单目相机,支持型号AS470/AS080

  • AD: 双目相机,支持型号AD470/AD080

  • AQ: 高精度相机,支持型号AQ060

如果连接失败,会捕获并输出错误信息,并终止程序返回 -1。

以下是代码示例:

std::shared_ptr<DaoAI::Camera> camera = nullptr;
    try {
            camera = std::make_shared<DaoAI::Camera>();
            auto model = DaoAI::CameraMode::AD;
            camera->connect(model);
    std::cout << "Connected to camera " << model << std::endl;
    // we can check the camera is connected or not
    if (!camera->connected()) {
        std::cerr << "Camera is not connected" << std::endl;
        return -1;
    }
    }
    catch (const std::exception& e) {
            std::cerr << "Failed to connect camera. Cause: " << e.what() << std::endl;
            return -1;
    }

预览

检查相机当前的视野,并调整帧配置以获得理想的图像。

首先,定义了一个 DaoAI::Settings3D::FrameConfig 对象 frame_config,用于设置帧配置参数,其中包括:

  • 曝光补偿(exposure_stop):设置为 -1

  • 亮度(brightness):设置为 3

  • 增益(gain):设置为 0

接着,调用 camera->preview(frame_config) 方法,获取相机在当前配置下的预览图像,并将结果存储在 preview_images 向量中。

然后,通过循环遍历 preview_images 向量,将每个图像保存为 PNG 文件。文件名格式为 "ad_3d_preview_" + std::to_string(i) + ".png",其中 i 表示图像的索引。

以下是代码示例:

try
{
    DaoAI::Settings3D::FrameConfig frame_config(-1, 3.0, 0.0);
    auto preview_images = camera->preview(frame_config);
    for (size_t i = 0; i < preview_images.size(); i++) {
        std::filesystem::path frame_path = std::filesystem::current_path() / ("ad_3d_preview" + std::to_string(i) + ".png");
        DaoAISample::saveImage(preview_images[i], frame_path);
    }
}
catch (const std::exception& e)
{
    std::cerr << "preview Error: " << e.what() << std::endl;
    std::cout << "Program execution finished, press any key to exit..." << std::endl;
    std::cin.get();
    return -1;
}

2D 拍照

使用相机(或者触发光源)拍照,并调整采集配置以获得理想的图像。

首先,定义了一个 DaoAI::Settings2D 对象 settings_2d,用于设置配置参数,其中包括:

  • 相机触发模式(trigger_mode):设置为 DaoAI::Settings2D::TriggerMode::Software

  • 曝光时间(exposure_time):设置为 std::chrono::milliseconds(600)

  • 增益(gain):设置为 0

接着,调用 camera->capture2D(settings_2d) 方法,获取相机在当前配置下的2D帧数据frame2d,其中包含获取图像数据image()、获取宽高等方法。

然后,通过frame2d.image()获取图像数据,并将图像保存为 PNG 文件。文件名格式为 "ad_2d_image.png"。

以下是代码示例:

try
{
    DaoAI::Settings2D settings_2d(DaoAI::Settings2D::TriggerMode::Software, std::chrono::milliseconds(600), 0);
    auto frame2d = camera->capture2D(settings_2d);
    std::filesystem::path frame_path = std::filesystem::current_path() / "ad_2d_image.png";
    DaoAISample::saveImage(frame2d.image(), frame_path);
}
catch (const std::exception& e)
{
    std::cerr << "capture2D Error: " << e.what() << std::endl;
    std::cout << "Program execution finished, press any key to exit..." << std::endl;
    std::cin.get();
    return -1;
}

3D 拍照

使用相机进行3D拍照,请根据需要调整对应过滤器以获得理想的点云。

首先,定义了一个 DaoAI::Settings3D 对象 settings_3d,不同型号相机支持采集帧数不同,设置失败后会抛出异常,以下是一帧参数定义包括:

  • 曝光档(exposure_stop):设置为 -1

  • 亮度(brightness):设置为 3.0

  • 增益(gain):设置为 0.0

设置帧数组到DaoAI::Settings3D对象中 settings_3d.setFrameConfigs(frame_configs); 接着,调用 camera->capture3D(settings_3d) 方法,进行3D采集,采集完成后会返回一个 DaoAI::Frame 对象 frame3d, 可以调用frame3d.getPointCloud()异步获取点云数据。

以下是代码示例:

try
{
    DaoAI::Settings3D settings_3d;
    DaoAI::Settings3D::FrameConfig frame_config(/*exposure_stop*/-1, /*brightness*/3.0, 0.0);
    std::vector<DaoAI::Settings3D::FrameConfig> frame_configs;
    frame_configs.push_back(frame_config);
    settings_3d.setFrameConfigs(frame_configs);
    // set filters
    //settings_3d.setFilter(DaoAI::Settings3D::FillHole(1.0f, 1.0f));
    //settings_3d.setFilter(DaoAI::Settings3D::ContrastDistortion(DaoAI::Settings3D::ContrastDistortion::Treatment::Off));
    //settings_3d.setFilter(DaoAI::Settings3D::Median(false));
    //settings_3d.setFilter(DaoAI::Settings3D::FaceNormal(false));
    //settings_3d.setFilter(DaoAI::Settings3D::Smooth(false));
    //settings_3d.setFilter(DaoAI::Settings3D::PhaseQuality(false));
    //settings_3d.setFilter(DaoAI::Settings3D::Intensity(3.0f));
    //settings_3d.setFilter(DaoAI::Settings3D::Outlier(1.0f));
    //settings_3d.setFilter(DaoAI::Settings3D::Gaussian(40));
    //settings_3d.setFilter(DaoAI::Settings3D::Cluster(5.0f, 2.0f));
    auto frame3d = camera->capture3D(settings_3d);
    DaoAI::PointCloud point_cloud = frame3d.getPointCloud();
    std::cout
        << "Pointcloud dimension -- #points: " << point_cloud.getSize()
        << ", width: " << point_cloud.getWidth()
        << ", height: " << point_cloud.getHeight() << std::endl;
}
catch (const std::exception& e)
{
    std::cerr << "capture3D Error: " << e.what() << std::endl;
    std::cout << "Program execution finished, press any key to exit..." << std::endl;
    std::cin.get();
    return -1;
}

点云保存及加载

点云保存支持PLY、PCD、BGRD格式,且支持PLY、PCD的二进制格式保存。 点云加载暂时只支持BGRD格式,可以通过DaoAI::PointCloud::load方法加载到点云对象。 BGRD格式是一种自定义的二进制格式,它极大地减少了点云数据的存储空间,同时保持了数据的完整性。 以下表格展示了各型号相机支持格式:

相机型号支持格式

型号

PLY / binary PLY save

PCD / binary PCD save

BGRD save

BGRD load

AS470 / AS080

AD470 / AD080

AQ060

通过上面获取的frame3d对象参数保存及加载点云,以下是代码示例:

// Save ply text and binary format file
void savePly(DaoAI::Frame& frame) {
    try
    {
        auto start = std::chrono::high_resolution_clock::now();
        std::string filename = "ad_3d_pointcloud.ply";
        std::filesystem::path frame_path = std::filesystem::current_path() / filename;
        std::cout << "Saving ply " << frame_path.string() << std::endl;
        frame.save(frame_path, false);
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "Save ply time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;

        filename = "ad_3d_pointcloud-binary.ply";
        std::cout << "Saving binary ply" << frame_path.string() << std::endl;
        start = std::chrono::high_resolution_clock::now();
        frame.save(filename);
        end = std::chrono::high_resolution_clock::now();
        std::cout << "Save binary ply time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;

    }
    catch (const std::exception& e)
    {
        std::cerr << "Error saving ply: " << e.what() << std::endl;
    }
}

// Save pcd text and binary format file
void savePcd(DaoAI::Frame& frame) {
    try {
        auto start = std::chrono::high_resolution_clock::now();
        std::string filename = "ad_3d_pointcloud.pcd";
        std::filesystem::path frame_path = std::filesystem::current_path() / filename;
        std::cout << "Saving pcd " << frame_path.string() << std::endl;
        frame.save(filename, false);
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "Save pcd time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;

        filename = "ad_3d_pointcloud-binary.pcd";
        std::cout << "Saving binary pcd" << filename << std::endl;
        start = std::chrono::high_resolution_clock::now();
        frame.save(filename);
        end = std::chrono::high_resolution_clock::now();
        std::cout << "Save binary pcd time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;
    } catch (const std::exception&) {
        std::cerr << "Error saving pcd" << std::endl;
    }
}

// load bgrd format file
void loadBgrd(const std::string& filename, const std::string& to_filename_ply) {
    std::cout << "Loading frame " << filename << std::endl;
    DaoAI::Frame frame_bgrd;
    frame_bgrd.load(filename);

    std::cout << "Frame loaded" << std::endl;
    try {
        std::cout << "Saving to ply " << to_filename_ply << std::endl;
        frame_bgrd.save(to_filename_ply, false);
        std::cout << "Frame Saving to ply Ok" << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "Error saving ply from bgrd: " << e.what() << std::endl;
    }
}

// Save bgrd format file and load, then save to ply, for test
void saveBgrd(DaoAI::Frame& frame) {
    try
    {
        auto start = std::chrono::high_resolution_clock::now();
        std::string filename = "ad_3d_pointcloud.bgrd";
        std::filesystem::path frame_path = std::filesystem::current_path() / filename;
        std::cout << "Saving bgrd " << frame_path.string() << std::endl;
        frame.save(frame_path, false);
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "Save bgrd time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms" << std::endl;

    } catch (const std::exception& e) {
        std::cerr << "Error saving bgrd: " << e.what() << std::endl;
    }

    // load bgrd
    loadBgrd("ad_3d_pointcloud.bgrd", "ad_3d_pointcloud_bgrd2ply.ply");
}

Settings3D参数

Settings3D类,用于设置3D采集的参数,包括采集帧配置、点云过滤器配置等。 它提供了参数的保存和加载功能,可以通过save和load方法将参数保存到文件或从文件加载。

以下是代码示例:

try
{
    DaoAI::Settings3D settings_3d;
    settings_3d.save("ad_3d_settings.yaml");
    settings_3d.load("ad_3d_settings.yaml");
}
catch (const std::exception& e)
{
    std::cerr << "Error saving or loading settings: " << e.what() << std::endl;
}

现场校准

我们提供了现场校准功能,可以通过现场校准功能对相机进行标定,以提高测量精度。

它需要使用标定板进行标定,需要已知如下参数:标定板的圆行列数、两个圆的圆心距离。

首先,使用captureInfieldCalibrationData方法采集3-5次标定板数据,并将结果保存到std::vector<DaoAI::TargetResult> target_result_vector中。

然后,使用infieldCalibration方法对采集到的标定板数据进行校准,校准结果返回百分比改进(范围 0 - 1.0),值越大表示校准效果越好。

最后,使用evaluateAccuracy方法评估校准结果,如果校准效果不好,可以重新采集标定板数据,重新进行校准。

以下是代码示例:

// Adjust each parameter as needed.
try
{
    std::vector<DaoAI::TargetResult> target_result_vec;

    // capture infield calibration data for 5 times
    DaoAI::Settings3D::FrameConfig frame_config_calibration(/*exposure_stop*/-1, /*brightness*/3.0, /*gain*/0.0);
    int pattern_column = 11;
    int pattern_row = 9;
    DaoAI::Size pattern_size(pattern_column, pattern_row); // 11x9 calibration board
    float center_distance = 2.5f; // Distance between the center of the calibration board circles

    for (size_t i = 0; i < 5; i++) {
        auto target_result = camera->captureInfieldCalibrationData(frame_config_calibration, pattern_size);
        target_result_vec.push_back(target_result);
    }

    // save infield calibration data
    auto percentage_improvement = camera->infieldCalibration(target_result_vec, pattern_size, center_distance);
    std::cout << "Infield calibration improvement: " << percentage_improvement << "%" << std::endl;

    // evaluate infield calibration
    DaoAI::Settings3D settings_3d;
    DaoAI::Settings3D::FrameConfig frame_config(/*exposure_stop*/-1, /*brightness*/3.0, /*gain*/0.0);
    std::vector<DaoAI::Settings3D::FrameConfig> frame_configs;
    frame_configs.push_back(frame_config);
    settings_3d.setFrameConfigs(frame_configs);

    // we can set filters here
    auto start = std::chrono::high_resolution_clock::now();
    auto frame3d = camera->capture3D(settings_3d);
    DaoAI::PointCloud point_cloud = frame3d.getPointCloud();

    // capture preview image of the calibration board
    DaoAI::Settings3D::FrameConfig frame_config_preview(/*exposure_stop*/-1, /*brightness*/3.0, /*gain*/0.0);
    auto preview_images = camera->preview(frame_config_preview);

    DaoAI::AccuracyResult accuracy_result = camera->evaluateAccuracy(point_cloud, preview_images.at(0), pattern_size, center_distance);
    std::cout << "Infield calibration accuracy max error: " << accuracy_result.max_err_perc << "%" << std::endl;
    std::cout << "Infield calibration accuracy avg error: " << accuracy_result.avg_err_perc << "%" << std::endl;
}
catch (const std::exception& e)
{
    std::cerr << "Infield Calibration Error: " << e.what() << std::endl;
}