Warm-up
Introduction
Zivid 카메라를 Warm-up하려면 배포할 애플리케이션과 동일한 캡처 주기 및 설정으로 10분 동안 반복해서 포인트 클라우드를 캡처하여, 의미 있는 3D 데이터를 생성하기 전에 카메라가 열 평형 상태에 도달하도록 해야 합니다. 카메라를 예열하여 열 평형 상태에 도달하도록 하면 애플리케이션의 전반적인 정확도와 성공률을 향상시킬 수 있습니다. 이 글에서는 Zivid 카메라를 예열해야 하는 이유와 방법을 설명합니다.
Why warm-up is needed
Zivid 카메라에는 사용 시 열을 발생시키는 능동형 전자 부품(예: 프로젝터)이 있습니다. 내부 발열로 인해 부품의 본체 온도가 변하면 기계적 변형이 발생합니다.
이러한 기계적 변화는 포인트 클라우드에서 포인트의 위치를 계산할 때 삼각 측량 정확도에 영향을 미치는 필수 광학 구성 요소의 작은 변위를 유발합니다. 카메라의 3D 보정은 warming(가열) 또는 cooling(냉각) 단계에서 정확도가 떨어지는데, 이는 각 내부 구성 요소에서 발생할 수 있는 많은 알 수 없는 온도 상태 때문입니다. 우리는 이 현상을 warm-up drift 라고 부릅니다.
카메라 온도를 제어하고 모든 구성 요소가 일정한 온도(열 평형)에 도달할 수 있도록 충분한 시간을 허용하는 것이 좋습니다. 결과적으로 Zivid 3D 보정은 카메라의 물리적 상태를 인식하고 정확하게 예측하여 정확한 포인트 클라우드를 생성할 수 있습니다.
참고
Zivid 카메라는 Thermal Stabilization 을 사용하여 안정적인 온도를 유지하고 Warm-up 필요성을 줄입니다.
Temperature change - camera vs. environment
Warm-up 테스트 결과, Zivid 카메라의 내부 온도는 가장 빠른 촬영 주기(실시간 모드)에서 분당 최대 1.5°C(10분 동안 15°C)까지 일시적으로 변할 수 있습니다. 일반적인 촬영 주기, 예를 들어 5초마다 촬영하는 경우, 내부 온도 변화는 분당 0.5~1°C로 2~3배 더 느립니다.
반면에 일별 기온은 최대 0.03 °C/min(예: 6시간에 10 °C)까지 변합니다. 이것은 워밍업 중 카메라의 일시적인 온도 변화보다 100배 더 느립니다.
Zivid 카메라는 주변 온도 변화를 보상하는 유동 보정 기능을 갖추고 있어 권장 온도 범위에서 안정성을 보장합니다. Warm-up 후 올바르게 사용하는 카메라는 지정된 주변 온도 범위 전체에서 안정적인 성능을 발휘합니다.
When to perform warm-up
다음 조건에서 카메라를 Warm-up하는 것이 좋습니다.
애플리케이션에는 다음과 같은 엄격한 허용 오차가 필요합니다.
카메라에서 미터당 거리가 5mm 미만인 피킹 애플리케이션.
< 0.5%의 진위 오차 요건을 갖춘 검사 애플리케이션입니다.
응용 프로그램에서 적당한 캡처 주기(10초마다 한 번 이상의 캡쳐)를 요구할 경우.
Infield Correction 전.
Hand-eye calibration 전.
생산을 실행하기 전에.
카메라를 전원에 연결하지 않은 경우(예: 카메라를 기계적으로 설치한 후).
카메라 전원이 켜져 있지만 지난 20분 동안 포인트 클라우드를 캡처하지 않은 경우(예: 아침에 생산을 시작할 때).
How to perform warm-up
카메라를 예열하려면 고정된 캡처 주기를 사용하여 10분 동안 포인트 클라우드를 캡처합니다.
Connect
첫 번째 단계는 카메라에 연결하는 것입니다.
Set Capture cycle and settings
Warm-up 동안 캡처 주기는 애플리케이션의 캡처 주기와 최대한 유사해야 합니다(빈 피킹, 팔레트 제거 등).
배포된 카메라가 예를 들어 5초마다 한 번씩 캡처하는 경우 Warm-up 단계에서 동일한 간격으로 캡처해야 합니다. 카메라에 안정적인 캡처 주기가 없는 경우 Warm-up 단계 중 캡처 주기는 극한 값 사이여야 합니다. 예를 들어 애플리케이션에서 5~11초에 한 번 캡처하는 경우 Warm-up 중에 8초에 한 번 캡처해야 합니다. 응용 프로그램에서 캡처 사이의 지연이 10초보다 길면 카메라를 예열할 필요가 없습니다.
워밍업 중에 사용하는 카메라 설정은 애플리케이션의 설정(사전 구성, YML 파일에서 로드 또는 Capture Assistant)과 가능한 한 유사해야 합니다.
const auto warmupTime = std::chrono::minutes(10);
const auto captureCycle = std::chrono::seconds{ captureCycleSeconds };
const auto settings = loadOrDefaultSettings(settingsPath);
Load or suggest settings
Zivid::Settings loadOrDefaultSettings(const std::string &settingsPath)
{
if(!settingsPath.empty())
{
std::cout << "Loading settings from file" << std::endl;
return Zivid::Settings(settingsPath);
}
std::cout << "Using default 3D settings" << std::endl;
auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
return settings;
static Zivid.NET.Settings LoadOrDefaultSettings(string settingsPath)
{
if (settingsPath != null)
{
Console.WriteLine("Loading settings from file");
return new Zivid.NET.Settings(settingsPath);
}
Console.WriteLine("Using default 3D settings");
var settings = new Zivid.NET.Settings
{
Acquisitions = { new Zivid.NET.Settings.Acquisition { } }
};
return settings;
}
def _load_or_default_settings(settings_path: str) -> zivid.Settings:
"""Load settings from YML file or use default settings.
Args:
settings_path: Path to the YML file that contains camera settings
Returns:
Camera Settings
"""
if settings_path:
print("Loading settings from file")
return zivid.Settings.load(Path(settings_path))
print("Using default 3D settings")
settings = zivid.Settings(acquisitions=[zivid.Settings.Acquisition()])
return settings
참고
이 Warm-up 방식은 프로젝터를 발열체로 사용하여 안정적인 촬영 주기 동안 카메라를 예열합니다. 프로젝터가 켜진 시간과 전체 촬영 주기 시간의 비율을 듀티 사이클이라고 합니다. 듀티 사이클은 카메라가 예열되는 속도뿐만 아니라 예열 단계 후 카메라의 최종 정상 온도에도 영향을 미칩니다.
Warm up the camera
카메라를 Warm-up하려면 온도가 안정화될 때까지 10분 동안 계속해서 3D 포인트 클라우드를 캡처합니다.
std::cout << "Starting warm up for: " << warmupTime.count() << " minutes" << std::endl;
const auto beforeWarmup = SteadyClock::now();
while(SteadyClock::now() - beforeWarmup < warmupTime)
{
const auto beforeCapture = SteadyClock::now();
// Use the same capture method as you would use in production
// to get the most accurate results from warmup
camera.capture3D(settings);
const auto afterCapture = SteadyClock::now();
const auto captureTime = afterCapture - beforeCapture;
if(captureTime < captureCycle)
{
std::this_thread::sleep_for(captureCycle - captureTime);
}
else
{
std::cout << "Your capture time is longer than your desired capture cycle."
<< "Please increase the desired capture cycle." << std::endl;
}
const auto remainingTime = warmupTime - (SteadyClock::now() - beforeWarmup);
const auto remainingTimeMinutes = std::chrono::duration_cast<std::chrono::minutes>(remainingTime);
const auto remainingTimeSeconds =
std::chrono::duration_cast<std::chrono::seconds>(remainingTime - remainingTimeMinutes);
std::cout << "Remaining time: " << remainingTimeMinutes.count() << " minutes, "
<< remainingTimeSeconds.count() << " seconds." << std::endl;
}
std::cout << "Warm up completed" << std::endl;
DateTime beforeWarmup = DateTime.Now;
Console.WriteLine("Starting warm up for: {0} minutes", warmupTime.Minutes);
while (DateTime.Now.Subtract(beforeWarmup) < warmupTime)
{
var beforeCapture = DateTime.Now;
// Use the same capture method as you would use in production
// to get the most accurate results from warmup
using (camera.Capture3D(settings)) { }
var afterCapture = DateTime.Now;
var captureTime = afterCapture.Subtract(beforeCapture);
if (captureTime < captureCycle)
{
Thread.Sleep(captureCycle.Subtract(captureTime));
}
else
{
Console.WriteLine(
"Your capture time is longer than your desired capture cycle. Please increase the desired capture cycle.");
}
var remainingTime = warmupTime.Subtract(DateTime.Now.Subtract(beforeWarmup));
var remainingMinutes = Math.Floor(remainingTime.TotalMinutes);
var remainingSeconds = Math.Floor(remainingTime.TotalSeconds) % 60;
Console.WriteLine("Remaining time: {0} minutes, {1} seconds.", remainingMinutes, remainingSeconds);
}
Console.WriteLine("Warm up completed");
before_warmup = datetime.now()
print(f"Starting warmup for {warmup_time} minutes")
while (datetime.now() - before_warmup) < warmup_time:
before_capture = datetime.now()
# Use the same capture method as you would use in production
# to get the most accurate results from warmup
camera.capture_3d(settings)
after_capture = datetime.now()
duration = after_capture - before_capture
if duration.seconds <= capture_cycle.seconds:
sleep(capture_cycle.seconds - duration.seconds)
else:
print(
"Your capture time is longer than your desired capture cycle. \
Please increase the desired capture cycle."
)
remaining_time = warmup_time - (datetime.now() - before_warmup)
remaining_time_minutes = remaining_time.seconds // 60
remaining_time_seconds = remaining_time.seconds % 60
print(f"Remaining time: {remaining_time_minutes} minutes, {remaining_time_seconds} seconds.")
print("Warmup completed")
구현 예는 아래의 전체 Warm-up 코드 샘플을 참조하세요.
/*
Short example of a basic way to warm up the camera with specified time and capture cycle.
For more information on warming up the camera, check out this tutorial:
https://support.zivid.com/en/latest/camera/academy/camera/warmup.html
*/
#include <Zivid/Zivid.h>
#include <clipp.h>
#include <chrono>
#include <iostream>
#include <thread>
using SteadyClock = std::chrono::steady_clock;
using Duration = std::chrono::nanoseconds;
namespace
{
Zivid::Settings loadOrDefaultSettings(const std::string &settingsPath)
{
if(!settingsPath.empty())
{
std::cout << "Loading settings from file" << std::endl;
return Zivid::Settings(settingsPath);
}
std::cout << "Using default 3D settings" << std::endl;
auto settings = Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} } };
return settings;
}
} // namespace
int main(int argc, char **argv)
{
try
{
std::string settingsPath;
size_t captureCycleSeconds = 5;
auto settingsOption = clipp::option("--settings-path")
& clipp::value("Path to the YML file that contains camera settings", settingsPath);
auto captureCycleOption =
clipp::option("--capture-cycle") & clipp::value("Capture cycle in seconds", captureCycleSeconds);
auto cli = (settingsOption, captureCycleOption);
if(!parse(argc, argv, cli))
{
auto fmt = clipp::doc_formatting{}.alternatives_min_split_size(1).surround_labels("\"", "\"");
std::cout << clipp::usage_lines(cli, "Warmup", fmt) << std::endl;
throw std::runtime_error{ "Invalid usage" };
}
Zivid::Application zivid;
std::cout << "Connecting to camera" << std::endl;
auto camera = zivid.connectCamera();
const auto warmupTime = std::chrono::minutes(10);
const auto captureCycle = std::chrono::seconds{ captureCycleSeconds };
const auto settings = loadOrDefaultSettings(settingsPath);
std::cout << "Starting warm up for: " << warmupTime.count() << " minutes" << std::endl;
const auto beforeWarmup = SteadyClock::now();
while(SteadyClock::now() - beforeWarmup < warmupTime)
{
const auto beforeCapture = SteadyClock::now();
// Use the same capture method as you would use in production
// to get the most accurate results from warmup
camera.capture3D(settings);
const auto afterCapture = SteadyClock::now();
const auto captureTime = afterCapture - beforeCapture;
if(captureTime < captureCycle)
{
std::this_thread::sleep_for(captureCycle - captureTime);
}
else
{
std::cout << "Your capture time is longer than your desired capture cycle."
<< "Please increase the desired capture cycle." << std::endl;
}
const auto remainingTime = warmupTime - (SteadyClock::now() - beforeWarmup);
const auto remainingTimeMinutes = std::chrono::duration_cast<std::chrono::minutes>(remainingTime);
const auto remainingTimeSeconds =
std::chrono::duration_cast<std::chrono::seconds>(remainingTime - remainingTimeMinutes);
std::cout << "Remaining time: " << remainingTimeMinutes.count() << " minutes, "
<< remainingTimeSeconds.count() << " seconds." << std::endl;
}
std::cout << "Warm up completed" << std::endl;
}
catch(const std::exception &e)
{
std::cerr << "Error: " << Zivid::toString(e) << std::endl;
std::cout << "Press enter to exit." << std::endl;
std::cin.get();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
return 1;
}
return 0;
}
static Zivid.NET.Settings LoadOrDefaultSettings(string settingsPath)
{
if (settingsPath != null)
{
Console.WriteLine("Loading settings from file");
return new Zivid.NET.Settings(settingsPath);
}
Console.WriteLine("Using default 3D settings");
var settings = new Zivid.NET.Settings
{
Acquisitions = { new Zivid.NET.Settings.Acquisition { } }
};
return settings;
}
/*
Short example of a basic way to warm up the camera with specified time and capture cycle.
For more information on warming up the camera, check out this tutorial:
https://support.zivid.com/en/latest/camera/academy/camera/warmup.html
*/
using CommandLine;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Duration = Zivid.NET.Duration;
class Program
{
public class Options
{
[Option('s', "settings-path", Required = false, HelpText = "Path to the YML file that contains camera settings.")]
public string SettingsPath { get; set; }
[Option('c', "capture-cycle", Required = false, HelpText = "Capture cycle in seconds.")]
public int? CaptureCycle { get; set; }
}
static int Main(string[] args)
{
return Parser.Default.ParseArguments<Options>(args)
.MapResult(
(Options opts) => RunWarmupWithOptionsAndReturnExitCode(opts),
errs => 1);
}
static int RunWarmupWithOptionsAndReturnExitCode(Options opts)
{
try
{
var zivid = new Zivid.NET.Application();
Console.WriteLine("Connecting to camera");
var camera = zivid.ConnectCamera();
var warmupTime = TimeSpan.FromMinutes(10);
var captureCycle = opts.CaptureCycle.HasValue ? TimeSpan.FromSeconds(opts.CaptureCycle.Value) : TimeSpan.FromSeconds(5);
var settings = LoadOrDefaultSettings(opts.SettingsPath);
DateTime beforeWarmup = DateTime.Now;
Console.WriteLine("Starting warm up for: {0} minutes", warmupTime.Minutes);
while (DateTime.Now.Subtract(beforeWarmup) < warmupTime)
{
var beforeCapture = DateTime.Now;
// Use the same capture method as you would use in production
// to get the most accurate results from warmup
using (camera.Capture3D(settings)) { }
var afterCapture = DateTime.Now;
var captureTime = afterCapture.Subtract(beforeCapture);
if (captureTime < captureCycle)
{
Thread.Sleep(captureCycle.Subtract(captureTime));
}
else
{
Console.WriteLine(
"Your capture time is longer than your desired capture cycle. Please increase the desired capture cycle.");
}
var remainingTime = warmupTime.Subtract(DateTime.Now.Subtract(beforeWarmup));
var remainingMinutes = Math.Floor(remainingTime.TotalMinutes);
var remainingSeconds = Math.Floor(remainingTime.TotalSeconds) % 60;
Console.WriteLine("Remaining time: {0} minutes, {1} seconds.", remainingMinutes, remainingSeconds);
}
Console.WriteLine("Warm up completed");
}
"""
Short example of a basic way to warm up the camera with specified time and capture cycle.
For more information on warming up the camera, check out this tutorial:
https://support.zivid.com/en/latest/camera/academy/camera/warmup.html
"""
import argparse
from datetime import datetime, timedelta
from pathlib import Path
from time import sleep
import zivid
def _options() -> argparse.Namespace:
"""Function to read user arguments.
Returns:
Arguments from user
"""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--settings-path",
required=False,
help="Path to the YML file that contains camera settings",
)
parser.add_argument(
"--capture-cycle",
required=False,
type=float,
default=5.0,
help="Capture cycle in seconds",
)
return parser.parse_args()
def _load_or_default_settings(settings_path: str) -> zivid.Settings:
"""Load settings from YML file or use default settings.
Args:
settings_path: Path to the YML file that contains camera settings
Returns:
Camera Settings
"""
if settings_path:
print("Loading settings from file")
return zivid.Settings.load(Path(settings_path))
print("Using default 3D settings")
settings = zivid.Settings(acquisitions=[zivid.Settings.Acquisition()])
return settings
def _main() -> None:
user_options = _options()
app = zivid.Application()
print("Connecting to camera")
camera = app.connect_camera()
warmup_time = timedelta(minutes=10)
capture_cycle = timedelta(seconds=user_options.capture_cycle)
settings = _load_or_default_settings(user_options.settings_path)
before_warmup = datetime.now()
print(f"Starting warmup for {warmup_time} minutes")
while (datetime.now() - before_warmup) < warmup_time:
before_capture = datetime.now()
# Use the same capture method as you would use in production
# to get the most accurate results from warmup
camera.capture_3d(settings)
after_capture = datetime.now()
duration = after_capture - before_capture
if duration.seconds <= capture_cycle.seconds:
sleep(capture_cycle.seconds - duration.seconds)
else:
print(
"Your capture time is longer than your desired capture cycle. \
Please increase the desired capture cycle."
)
remaining_time = warmup_time - (datetime.now() - before_warmup)
remaining_time_minutes = remaining_time.seconds // 60
remaining_time_seconds = remaining_time.seconds % 60
print(f"Remaining time: {remaining_time_minutes} minutes, {remaining_time_seconds} seconds.")
print("Warmup completed")
if __name__ == "__main__":
_main()
카메라를 예열하기 위해 사이클 타임과 카메라 설정 경로를 사용하여 코드 샘플을 실행할 수 있습니다.
Sample: warmup.py
python /path/to/warmup.py --settings-path /path/to/settings.yml --capture-cycle 6.0