关于多个Zivid相机同时工作的性能的考虑因素

下面是两个使用了顺序捕获的应用的示例:

  • 使用多个相机从不同侧面对物体进行成像(检测)

  • 使用多个相机对一个或多个料箱进行成像来规避遮挡问题(料箱拣选/单品拣选)

此类应用中的多台相机之间存在重叠的FOV。因此,为避免投影的白光图案在相机之间产生干扰,需要一次使用一台相机进行拍摄。

对于上面提到的两个应用,我们推荐相同的捕获策略。为了解释这个策略,让我们以检测传送带上的物体为例。

使用具有重叠FOV的多个相机检测传送带上的物体

当检测对象位于相机的视野内时,传送带停止。为了优化捕获过程的速度,我们建议顺序调用捕获(capture )函数,因为捕获(capture )API会在采集(acquisition )完成时返回。

跳转到源码

源码

std::vector<Zivid::Frame> frames;

for(auto &camera : connectedCameras)
{
    std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl;
    const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{
        Zivid::CaptureAssistant::SuggestSettingsParameters::AmbientLightFrequency::none,
        Zivid::CaptureAssistant::SuggestSettingsParameters::MaxCaptureTime{ std::chrono::milliseconds{ 800 } }
    };
    const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, parameters);
    const auto frame = camera.capture(settings);
    frames.push_back(frame);
}

小技巧

如果您执行 2D 捕获而不是 3D 捕获,建议采用相同的策略。但是,如果您同时需要 2D 和 3D,那么建议您查看我们的 2D+3D捕获策略

一旦所有相机都完成了捕获,场景就可以相对于相机移动了。在本文的示例中,即为运输物体的传送带可以开始移动了。

这也是复制数据、拼接点云和检测对象等过程可以开始进行的时候。这些操作可以顺序发生或并行发生。在下面的实施示例中,我们展示了如何复制数据并将其并行保存到ZDF文件。

备注

为了优化速度,重要的是在捕获函数返回之后和调用API以获取点云之前触发移动传送带。

跳转到源码

源码

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

    std::vector<Zivid::Camera> connectToAllAvailableCameras(const std::vector<Zivid::Camera> &cameras)
    {
        std::vector<Zivid::Camera> connectedCameras;
        for(auto camera : cameras)
        {
            if(camera.state().status() == Zivid::CameraState::Status::available)
            {
                std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl;
                camera.connect();
                connectedCameras.push_back(camera);
            }
            else
            {
                std::cout << "Camera " << camera.info().serialNumber() << "is not available. "
                          << "Camera status: " << camera.state().status() << std::endl;
            }
        }
        return connectedCameras;
    }
} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA>>> futureData;

        for(auto &frame : frames)
        {
            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

要进一步优化整个过程的速度,您可以执行以下操作。一旦捕获函数返回,就启动一个不同的线程,在frame上执行以下操作:

  • 获取点云

  • 将点云复制到CPU内存

  • 可能需要的其它点云操作,例如,将点云转换到某个坐标系

查看下面的实施示例。

跳转到源码

源码

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

    std::vector<Zivid::Camera> connectToAllAvailableCameras(const std::vector<Zivid::Camera> &cameras)
    {
        std::vector<Zivid::Camera> connectedCameras;
        for(auto camera : cameras)
        {
            if(camera.state().status() == Zivid::CameraState::Status::available)
            {
                std::cout << "Connecting to camera: " << camera.info().serialNumber() << std::endl;
                camera.connect();
                connectedCameras.push_back(camera);
            }
            else
            {
                std::cout << "Camera " << camera.info().serialNumber() << "is not available. "
                          << "Camera status: " << camera.state().status() << std::endl;
            }
        }
        return connectedCameras;
    }
} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA>>> futureData;

        for(auto &camera : connectedCameras)
        {
            std::cout << "Capturing frame with camera: " << camera.info().serialNumber() << std::endl;
            const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{
                Zivid::CaptureAssistant::SuggestSettingsParameters::AmbientLightFrequency::none,
                Zivid::CaptureAssistant::SuggestSettingsParameters::MaxCaptureTime{ std::chrono::milliseconds{ 800 } }
            };
            const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, parameters);
            const auto frame = camera.capture(settings);

            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

然后您需要等到所有数据都可用,才能执行进一步的处理,例如,拼接点云。

跳转到源码

源码

std::vector<Zivid::Array2D<Zivid::PointXYZColorRGBA>> allData;

for(size_t i = 0; i < cameras.size(); ++i)
{
    std::cout << "Waiting for processing and saving to finish for camera " << cameras[i].info().serialNumber()
              << std::endl;
    const auto data = futureData[i].get();
    allData.push_back(data);
}

下面是一个使用并行捕获的应用的示例:

  • 使用多台相机对不适合单台相机FOV尺寸的物体进行成像(检测)

此类应用中的多台相机之前没有重叠的FOV(使用多台相机的目的是扩展FOV)。因此,可以并行捕获以节省时间。

为了解释我们推荐的捕获策略,让我们以检测传送带上的大型物体为例。

使用多个相机检测传送带上的物体,不重叠 FOV

当待检物体位于相机的组合视场内时,传送带停止。为了优化捕获过程的速度,我们建议在单独的线程中只调用每个相机的捕获函数并阻塞,直到所有线程执行结束。我们建议只调用捕获(capture )函数,因为捕获(capture )API会在采集(acquisition )完成时返回。

跳转到源码

源码

namespace
{
    Zivid::Frame captureInThread(Zivid::Camera &camera)
    {
        const auto parameters = Zivid::CaptureAssistant::SuggestSettingsParameters{
            Zivid::CaptureAssistant::SuggestSettingsParameters::AmbientLightFrequency::none,
            Zivid::CaptureAssistant::SuggestSettingsParameters::MaxCaptureTime{ std::chrono::milliseconds{ 800 } }
        };
        const auto settings = Zivid::CaptureAssistant::suggestSettings(camera, parameters);

        std::cout << "Capturing frame with camera: " << camera.info().serialNumber().value() << std::endl;
        auto frame = camera.capture(settings);

        return frame;
    }

} // namespace

        std::vector<std::future<Zivid::Frame>> futureFrames;

        for(auto &camera : connectedCameras)
        {
            std::cout << "Starting to capture (in a separate thread) with camera: "
                      << camera.info().serialNumber().value() << std::endl;
            futureFrames.emplace_back(std::async(std::launch::async, captureInThread, std::ref(camera)));
        }

        std::vector<Zivid::Frame> frames;

        for(size_t i = 0; i < connectedCameras.size(); ++i)
        {
            std::cout << "Waiting for camera " << connectedCameras[i].info().serialNumber() << " to finish capturing"
                      << std::endl;
            const auto frame = futureFrames[i].get();
            frames.push_back(frame);
        }

小技巧

如果您执行 2D 捕获而不是 3D 捕获,建议采用相同的策略。但是,如果您同时需要 2D 和 3D,那么建议您查看我们的 2D+3D捕获策略

一旦所有相机都完成了捕获,场景就可以相对于相机移动了。在本文的示例中,即为运输物体的传送带可以开始移动了。

这也是复制数据、拼接点云和检测对象等过程可以开始进行的时候。这些操作可以顺序发生或并行发生。在下面的实施示例中,我们展示了如何复制数据并将其并行保存到ZDF文件。

备注

为了优化速度,重要的是在捕获函数返回之后和调用API以获取点云之前触发移动传送带。

跳转到源码

源码

namespace
{
    Zivid::Array2D<Zivid::PointXYZColorRGBA> processAndSaveInThread(const Zivid::Frame &frame)
    {
        const auto pointCloud = frame.pointCloud();
        auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA>();

        // This is where you should run your processing

        const auto dataFile = "Frame_" + frame.cameraInfo().serialNumber().value() + ".zdf";
        std::cout << "Saving frame to file: " << dataFile << std::endl;
        frame.save(dataFile);

        return data;
    }

} // namespace

        std::vector<std::future<Zivid::Array2D<Zivid::PointXYZColorRGBA>>> futureData;

        for(auto &frame : frames)
        {
            std::cout << "Starting to process and save (in a separate thread) the frame captured with camera: "
                      << frame.cameraInfo().serialNumber().value() << std::endl;
            futureData.emplace_back(std::async(std::launch::async, processAndSaveInThread, frame));
        }

然后您需要等到所有数据都可用,才能执行进一步的处理,例如,拼接点云。

跳转到源码

源码

std::vector<Zivid::Array2D<Zivid::PointXYZColorRGBA>> allData;

for(size_t i = 0; i < frames.size(); ++i)
{
    std::cout << "Waiting for processing and saving to finish for camera "
              << frames[i].cameraInfo().serialNumber().value() << std::endl;
    const auto data = futureData[i].get();
    allData.push_back(data);
}

版本历史

SDK

变更

2.11.0

Zivid 2 和2+现在支持 3D ➞ 2D 和 3D ➞ 2D 的并发处理和采集,并且捕获模式之间的切换已得到优化。