parse_depth_mode returned NEURAL for any unrecognised string, so a typo silently changed depth quality, and NEURAL_PLUS and NEURAL_LIGHT could not be selected at all. All six modes are now reachable and an unknown name exits non-zero listing the accepted values. Parsing moves into Config::from_env() and main reads the configuration before init_dora_node(), so a bad value fails with its own message instead of after the node has registered with the dataflow. Add ZED_CONFIDENCE_THRESHOLD (60) and ZED_TEXTURE_CONFIDENCE_THRESHOLD (100), which were never set, and change the defaults to 1080 / 200 mm / 600 mm to match the reference driver perception was tuned against. The resolved settings are logged at startup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y7g2cBJ5e3r15ZbJ9WZn8c
195 lines
6.2 KiB
C++
195 lines
6.2 KiB
C++
#include "zed/camera.h"
|
|
#include "util/env.h"
|
|
|
|
#include <chrono>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
|
|
sl::RESOLUTION parse_resolution(int resolution) {
|
|
switch (resolution) {
|
|
case 1080:
|
|
return sl::RESOLUTION::HD1080;
|
|
case 2000:
|
|
return sl::RESOLUTION::HD2K;
|
|
case 720:
|
|
default:
|
|
return sl::RESOLUTION::HD720;
|
|
}
|
|
}
|
|
|
|
const std::pair<const char*, sl::DEPTH_MODE> DEPTH_MODES[] = {
|
|
{"NEURAL", sl::DEPTH_MODE::NEURAL},
|
|
{"NEURAL_PLUS", sl::DEPTH_MODE::NEURAL_PLUS},
|
|
{"NEURAL_LIGHT", sl::DEPTH_MODE::NEURAL_LIGHT},
|
|
{"PERFORMANCE", sl::DEPTH_MODE::PERFORMANCE},
|
|
{"QUALITY", sl::DEPTH_MODE::QUALITY},
|
|
{"NONE", sl::DEPTH_MODE::NONE},
|
|
};
|
|
|
|
// A typo here silently changes depth quality if it falls back to a default, and
|
|
// the resulting depth is wrong in a way that only shows up downstream, so an
|
|
// unrecognised name stops the node instead.
|
|
sl::DEPTH_MODE parse_depth_mode(const std::string& mode) {
|
|
for (const auto& entry : DEPTH_MODES) {
|
|
if (mode == entry.first) {
|
|
return entry.second;
|
|
}
|
|
}
|
|
std::cerr << "Unknown ZED_DEPTH_MODE '" << mode << "'. Accepted values:";
|
|
for (const auto& entry : DEPTH_MODES) {
|
|
std::cerr << " " << entry.first;
|
|
}
|
|
std::cerr << std::endl;
|
|
std::exit(1);
|
|
}
|
|
|
|
sl::FLIP_MODE parse_flip(const std::string& mode) {
|
|
if (mode == "OFF") {
|
|
return sl::FLIP_MODE::OFF;
|
|
}
|
|
return sl::FLIP_MODE::ON;
|
|
}
|
|
|
|
void list_available_cameras() {
|
|
auto devices = sl::Camera::getDeviceList();
|
|
std::cout << "Detected ZED cameras: " << devices.size() << std::endl;
|
|
for (std::size_t i = 0; i < devices.size(); i++) {
|
|
const auto& dev = devices[i];
|
|
std::cout << " - [" << i << "] model=" << sl::toString(dev.camera_model)
|
|
<< ", serial=" << dev.serial_number << std::endl;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace zed {
|
|
|
|
Config Config::from_env() {
|
|
Config c;
|
|
c.resolution = util::get_env_int("ZED_RESOLUTION", c.resolution);
|
|
c.fps = util::get_env_int("ZED_FPS", c.fps);
|
|
c.depth_mode = parse_depth_mode(util::get_env_str("ZED_DEPTH_MODE", "NEURAL"));
|
|
c.depth_min_mm = util::get_env_int("ZED_DEPTH_MIN_MM", c.depth_min_mm);
|
|
c.depth_max_mm = util::get_env_int("ZED_DEPTH_MAX_MM", c.depth_max_mm);
|
|
c.depth_fill = util::get_env_bool("ZED_DEPTH_FILL", c.depth_fill);
|
|
c.confidence_threshold = util::get_env_int("ZED_CONFIDENCE_THRESHOLD", c.confidence_threshold);
|
|
c.texture_confidence_threshold =
|
|
util::get_env_int("ZED_TEXTURE_CONFIDENCE_THRESHOLD", c.texture_confidence_threshold);
|
|
c.flip = util::get_env_str("ZED_FLIP", c.flip);
|
|
c.warmup_frames = util::get_env_int("ZED_WARMUP_FRAMES", c.warmup_frames);
|
|
int retry_seconds = util::get_env_int("ZED_RETRY_INTERVAL_SECONDS", 5);
|
|
c.retry_interval_ms = retry_seconds * 1000;
|
|
return c;
|
|
}
|
|
|
|
Camera::Camera(const Config& config) : config_(config) {
|
|
// Process-wide, and must be set before any camera is opened. Without it the
|
|
// epoch of the timestamps published alongside every frame depends on the SDK
|
|
// default rather than on anything this node states.
|
|
sl::setTimestampClock(sl::TIMESTAMP_CLOCK::SYSTEM_CLOCK);
|
|
|
|
init_params_.camera_resolution = parse_resolution(config_.resolution);
|
|
init_params_.camera_fps = config_.fps;
|
|
init_params_.depth_mode = config_.depth_mode;
|
|
init_params_.coordinate_units = sl::UNIT::MILLIMETER;
|
|
init_params_.camera_image_flip = parse_flip(config_.flip);
|
|
init_params_.depth_minimum_distance = config_.depth_min_mm;
|
|
init_params_.depth_maximum_distance = config_.depth_max_mm;
|
|
|
|
runtime_params_.enable_depth = true;
|
|
runtime_params_.enable_fill_mode = config_.depth_fill;
|
|
// The two thresholds discard the depth the SDK is least certain of, which is
|
|
// the depth at object edges: those pixels straddle the object and whatever is
|
|
// behind it, and belong to neither.
|
|
runtime_params_.confidence_threshold = config_.confidence_threshold;
|
|
runtime_params_.texture_confidence_threshold = config_.texture_confidence_threshold;
|
|
|
|
std::cout << "ZED depth: mode=" << sl::toString(init_params_.depth_mode)
|
|
<< ", range=" << config_.depth_min_mm << "-" << config_.depth_max_mm << " mm"
|
|
<< ", confidence=" << config_.confidence_threshold
|
|
<< ", texture_confidence=" << config_.texture_confidence_threshold
|
|
<< ", fill=" << (config_.depth_fill ? "on" : "off") << std::endl;
|
|
}
|
|
|
|
Camera::~Camera() {
|
|
close();
|
|
}
|
|
|
|
void Camera::open() {
|
|
list_available_cameras();
|
|
while (true) {
|
|
sl::ERROR_CODE err = zed_.open(init_params_);
|
|
if (err == sl::ERROR_CODE::SUCCESS) {
|
|
return;
|
|
}
|
|
std::cerr << "Failed to open ZED camera: " << sl::toString(err) << std::endl;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(config_.retry_interval_ms));
|
|
}
|
|
}
|
|
|
|
void Camera::close() {
|
|
zed_.close();
|
|
}
|
|
|
|
void Camera::warmup() {
|
|
for (int i = 0; i < config_.warmup_frames; i++) {
|
|
zed_.grab(runtime_params_);
|
|
}
|
|
}
|
|
|
|
bool Camera::grab() {
|
|
if (zed_.grab(runtime_params_) != sl::ERROR_CODE::SUCCESS) {
|
|
return false;
|
|
}
|
|
zed_.retrieveImage(image_mat_, sl::VIEW::LEFT);
|
|
zed_.retrieveMeasure(depth_mat_, sl::MEASURE::DEPTH);
|
|
zed_.retrieveMeasure(point_cloud_mat_, sl::MEASURE::XYZRGBA);
|
|
return true;
|
|
}
|
|
|
|
Calibration Camera::calibration() const {
|
|
sl::CameraInformation info = zed_.getCameraInformation();
|
|
auto calib = info.camera_configuration.calibration_parameters;
|
|
auto left = calib.left_cam;
|
|
|
|
Calibration result;
|
|
result.fx = left.fx;
|
|
result.fy = left.fy;
|
|
result.cx = left.cx;
|
|
result.cy = left.cy;
|
|
result.distortion.reserve(std::size(left.disto));
|
|
for (double v : left.disto) {
|
|
result.distortion.push_back(v);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int64_t Camera::device_timestamp_ns() {
|
|
return static_cast<int64_t>(
|
|
zed_.getTimestamp(sl::TIME_REFERENCE::IMAGE).getNanoseconds());
|
|
}
|
|
|
|
sl::Mat& Camera::image() {
|
|
return image_mat_;
|
|
}
|
|
|
|
sl::Mat& Camera::depth() {
|
|
return depth_mat_;
|
|
}
|
|
|
|
sl::Mat& Camera::point_cloud() {
|
|
return point_cloud_mat_;
|
|
}
|
|
|
|
sl::Camera& Camera::handle() {
|
|
return zed_;
|
|
}
|
|
|
|
} // namespace zed
|