ロボットサイクルタイムの最適化
ロボット搭載カメラを使用したピック アンド プレイス アプリケーションの場合、ロボットがオブジェクトをピックした後にキャプチャすることをお勧めします。この戦略では、ロボットは 取得時間 中にのみ停止する必要があります。取得が完了すると、つまりキャプチャ API が返された時点で、ロボットはオブジェクトの配置を開始できます。並行して、次のサイクルのピックポーズを取得するための点群 処理 を開始できます。以下の実装例をご覧ください。
注釈
速度を最適化するには、キャプチャ関数が返った後、API を呼び出して点群を取得する前にロボットを移動することが重要です。
#include <Zivid/Zivid.h>
#include <future>
#include <iostream>
namespace
{
struct DummyRobot
{
void Move(Zivid::Matrix4x4 pose, std::chrono::milliseconds duration)
{
std::this_thread::sleep_for(duration);
}
};
DummyRobot robot;
Zivid::Matrix4x4 computePickPose(const Zivid::Frame &frame)
{
const auto pointCloud = frame.pointCloud();
const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();
// This is where you should run your processing to get a pick pose from the point cloud
return Zivid::Matrix4x4{};
}
void placeObject()
{
const auto placePose = Zivid::Matrix4x4{};
robot.Move(placePose, std::chrono::milliseconds(2000));
const auto outOfCameraFOVPose = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOVPose, std::chrono::milliseconds(1000));
}
} // namespace
int main()
{
try
{
Zivid::Application zivid;
auto camera = zivid.connectCamera();
std::cout << "Moving the robot mounted camera to the capture pose" << std::endl;
const auto capturePose = Zivid::Matrix4x4{};
robot.Move(capturePose, std::chrono::milliseconds(2000));
std::cout << "Capturing frame" << std::endl;
const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
const auto frame = camera.capture(settings);
std::cout << "Processing to get the first pick pose" << std::endl;
auto pickPose = computePickPose(frame);
while(true)
{
std::cout << "Picking the new object" << std::endl;
robot.Move(pickPose, std::chrono::milliseconds(2000));
std::cout << "Object picked" << std::endl;
std::cout << "Moving the robot mounted camera to the capture pose" << std::endl;
robot.Move(capturePose, std::chrono::milliseconds(2000));
// At this moment the robot should be at the capture pose
std::cout << "Capturing frame" << std::endl;
const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
const auto frame = camera.capture(settings);
std::cout << "Acquisition completed" << std::endl;
std::cout
<< "Starting, at the same time (in two threads), robot motion to place the object and capturing and processing to get the new pick pose"
<< std::endl;
auto futurePickPose = std::async(std::launch::async, computePickPose, frame);
auto placingCompleted = std::async(std::launch::async, placeObject);
pickPose =
futurePickPose
.get(); // This (processing to get the new pick pose) should be faster so the robot would not have to stop and wait
placingCompleted.get();
std::cout
<< "Both robot motion to place the object and capturing and processing to get the new pick pose completed"
<< std::endl;
}
}
catch(const std::exception &e)
{
std::cerr << "Error: " << Zivid::toString(e) << std::endl;
return EXIT_FAILURE;
}
}
Tip
3D キャプチャの代わりに 2D キャプチャを実行する場合にも、同じ戦略をお勧めします。ただし、2D と 3D の両方が必要な場合は、 2D + 3D キャプチャ戦略 をご覧ください。
オブジェクトを配置するロボットの動作が、次のサイクルのピックポーズを取得するための点群 処理 よりも速い場合、ロボットは待機する必要があります。
ロボットの停止を防ぐ別の方法は、サイクルごとにロボットが前のサイクルから計算されたピックポーズに移動することです。この戦略により、ロボットがオブジェクトをピックしている間もデータの 処理 を続けることができます。以下の実装例をご覧ください。この戦略は、複数のピックポーズを常に推定できることを前提としています。また、現在のサイクルでピックされるオブジェクトが前のピックサイクル中に移動していないことも前提としています。現在のピックサイクルのオブジェクトが、前のサイクルでピックされたオブジェクトとは異なるビン内の領域に配置されていることを確認してください。このようなオブジェクトの選択により、ピッキングの成功率が高まります。
#include <Zivid/Zivid.h>
#include <future>
#include <iostream>
namespace
{
struct DummyRobot
{
void Move(Zivid::Matrix4x4 pose, std::chrono::milliseconds duration)
{
std::this_thread::sleep_for(duration);
}
};
DummyRobot robot;
Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB> getPointCloudInThread(const Zivid::Frame &frame)
{
const auto pointCloud = frame.pointCloud();
const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();
return data;
}
void placeObjectInThread()
{
const auto placePose = Zivid::Matrix4x4{};
robot.Move(placePose, std::chrono::milliseconds(2000));
const auto outOfCameraFOVPose = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOVPose, std::chrono::milliseconds(1000));
}
Zivid::Matrix4x4 computePickPoseInThread(const Zivid::Array2D<Zivid::PointXYZColorRGBA_SRGB> &data)
{
// This is where you should run your processing to get a pick pose from the point cloud
return Zivid::Matrix4x4{};
}
} // namespace
int main()
{
try
{
Zivid::Application zivid;
auto camera = zivid.connectCamera();
std::cout << "Moving the robot mounted camera to the capture pose" << std::endl;
const auto capturePose = Zivid::Matrix4x4{};
robot.Move(capturePose, std::chrono::milliseconds(2000));
// At this moment the robot should be at the capture pose
std::cout << "Capturing frame" << std::endl;
const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
const auto frame = camera.capture(settings);
std::cout << "Acquisition completed" << std::endl;
std::cout
<< "Starting, at the same time (in two threads), robot motion to place the object and waiting for the point cloud to be processed and copied"
<< std::endl;
auto futureData = std::async(std::launch::async, getPointCloudInThread, frame);
auto placingCompleted = std::async(std::launch::async, placeObjectInThread);
const auto data =
futureData
.get(); // This (remaining time to process the point cloud) should be faster so the robot would not have to stop and wait
placingCompleted.get();
std::cout << "Both robot motion to place the object and point cloud processing and copying completed"
<< std::endl;
std::cout << "Starting the processing (in a separate thread) to get the pick pose for the next cycle"
<< std::endl;
auto nextPickPose = std::async(std::launch::async, computePickPoseInThread, data);
std::cout
<< "Picking the object from the previous pick cycle (becasue computing the pick new pose is not completed yet at this moment)"
<< std::endl;
const auto previousCyclePickPose = Zivid::Matrix4x4{};
robot.Move(previousCyclePickPose, std::chrono::milliseconds(2000));
std::cout << "Object picked" << std::endl;
}
catch(const std::exception &e)
{
std::cerr << "Error: " << Zivid::toString(e) << std::endl;
return EXIT_FAILURE;
}
}
固定マウントカメラを使用したピック アンド プレイス アプリケーションの場合、ロボットがオブジェクトをピックしてカメラの FOV の外に移動した後にキャプチャすることをお勧めします。この戦略では、次のどちらの操作の実行に時間がかかるかに応じて、カメラのキャプチャのためにロボットを停止する必要がなくなる場合があります。
合計キャプチャ時間と、オブジェクトを検出してピックポーズを推定するためにデータを 処理 する時間の合計。
カメラの FOV 外のポーズから配置ポーズまで、そして再びカメラの FOV に入る前のポーズに戻るまでのロボットの動作。
ロボットの動作時間が長い場合、ロボットは停止して待機する必要はありません。ただし、前者(キャプチャ + 検出 + ポーズ推定)の実行に時間がかかる場合、ロボットは停止する必要があります。以下の実装例をご覧ください。
#include <Zivid/Zivid.h>
#include <future>
#include <iostream>
namespace
{
struct DummyRobot
{
void Move(Zivid::Matrix4x4 pose, std::chrono::milliseconds duration)
{
std::this_thread::sleep_for(duration);
}
};
DummyRobot robot;
Zivid::Matrix4x4 captureAndProcess(Zivid::Camera &camera)
{
const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
const auto frame = camera.capture(settings);
const auto pointCloud = frame.pointCloud();
const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();
const auto pickPose =
Zivid::Matrix4x4{}; // This is where you should run your processing to get a pick pose from the point cloud
return pickPose;
}
void placeObject()
{
const auto placePose = Zivid::Matrix4x4{};
robot.Move(placePose, std::chrono::milliseconds(2000));
const auto outOfCameraFOV = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOV, std::chrono::milliseconds(1000));
}
} // namespace
int main()
{
try
{
Zivid::Application zivid;
auto camera = zivid.connectCamera();
auto pickPose = captureAndProcess(camera);
while(true)
{
std::cout << "Picking the new object" << std::endl;
robot.Move(pickPose, std::chrono::milliseconds(2000));
std::cout << "Object picked" << std::endl;
std::cout << "Moving the robot with a picked object outside the FOV of the stationary camera" << std::endl;
const auto outOfCameraFOV = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOV, std::chrono::milliseconds(2000));
// At this moment the robot should be outside the camera FOV
std::cout
<< "Starting, at the same time (in two threads), robot motion to place the object and capturing and processing to get the new pick pose"
<< std::endl;
auto futurePickPose = std::async(std::launch::async, captureAndProcess, std::ref(camera));
auto placingCompleted = std::async(std::launch::async, placeObject);
pickPose =
futurePickPose
.get(); // This (capturing and processing to get the new pick pose) should be faster so the robot would not have to stop and wait
placingCompleted.get();
std::cout
<< "Both robot motion to place the object and capturing and processing to get the new pick pose completed"
<< std::endl;
}
}
catch(const std::exception &e)
{
std::cerr << "Error: " << Zivid::toString(e) << std::endl;
return EXIT_FAILURE;
}
}
ロボットの停止を防ぐ別の方法は、サイクルごとにロボットが前のサイクルから計算されたピックポーズに移動することです。この戦略により、ロボットがオブジェクトをピックしている間もデータの 処理 を続けることができます。この場合、オブジェクトを配置する際に、キャプチャのみを行うか、キャプチャして点群を取得し、それを CPU メモリにコピーすることができます。以下の実装例をご覧ください。この戦略は、複数のピックポーズを常に推定できることを前提としています。また、現在のサイクルでピックされるオブジェクトが前のピックサイクル中に移動していないことも前提としています。現在のピックサイクルのオブジェクトが、前のサイクルでピックされたオブジェクトとは異なるビン内の領域に配置されていることを確認してください。このようなオブジェクトの選択により、ピッキングの成功率が高まります。
#include <Zivid/Zivid.h>
#include <future>
#include <iostream>
namespace
{
struct DummyRobot
{
void Move(Zivid::Matrix4x4 pose, std::chrono::milliseconds duration)
{
std::this_thread::sleep_for(duration);
}
};
DummyRobot robot;
Zivid::Frame captureInThread(Zivid::Camera &camera)
{
const auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
const auto frame = camera.capture(settings);
return frame;
}
void placeObjectInThread()
{
const auto placePose = Zivid::Matrix4x4{};
robot.Move(placePose, std::chrono::milliseconds(2000));
const auto outOfCameraFOVPose = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOVPose, std::chrono::milliseconds(1000));
}
Zivid::Matrix4x4 computePickPoseInThread(const Zivid::Frame &frame)
{
const auto pointCloud = frame.pointCloud();
const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA_SRGB>();
// If you know your robot motion to place the object is slow enough, you can move the above two lines to captureInThread
// This is where you should run your processing to get a pick pose from the point cloud
return Zivid::Matrix4x4{};
}
} // namespace
int main()
{
try
{
Zivid::Application zivid;
auto camera = zivid.connectCamera();
std::cout << "Moving the robot with a picked object outside the FOV of the stationary camera" << std::endl;
const auto outOfCameraFOV = Zivid::Matrix4x4{};
robot.Move(outOfCameraFOV, std::chrono::milliseconds(2000));
// At this moment the robot should be outside the camera FOV
std::cout
<< "Starting, at the same time (in two threads), robot motion to place the object and capturing the point cloud"
<< std::endl;
auto futureFrame = std::async(std::launch::async, captureInThread, std::ref(camera));
auto placingCompleted = std::async(std::launch::async, placeObjectInThread);
const auto frame =
futureFrame
.get(); // This (capturing the point cloud) should be faster so the robot would not have to stop and wait
placingCompleted.get();
std::cout << "Both robot motion to place the object and capturing the point cloud completed" << std::endl;
std::cout << "Starting the processing (in a separate thread) to get the pick pose for the next cycle"
<< std::endl;
auto nextPickPose = std::async(std::launch::async, computePickPoseInThread, frame);
std::cout
<< "Picking the object from the previous pick cycle (becasue computing the pick new pose is not completed yet at this moment)"
<< std::endl;
const auto previousCyclePickPose = Zivid::Matrix4x4{};
robot.Move(previousCyclePickPose, std::chrono::milliseconds(2000));
std::cout << "Object picked" << std::endl;
}
catch(const std::exception &e)
{
std::cerr << "Error: " << Zivid::toString(e) << std::endl;
return EXIT_FAILURE;
}
}
次のセクションでは、Zivid カメラを本番運用向けに準備するために実施を推奨するマシンビジョンのプロセスを 生産準備プロセス で説明します。