-
Notifications
You must be signed in to change notification settings - Fork 5
[RSDK-13174] Fixing spammy color and depth timestamps differ logs #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d6a909b
39ec6f6
3139d42
2b53c3a
f993489
6d0f251
4952672
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,10 @@ constexpr char service_name[] = "viam_orbbec"; | |
| const float mmToMeterMultiple = 0.001; | ||
| const uint64_t maxFrameAgeUs = 1e6; // time until a frame is considered stale, in microseconds (equal to 1 sec) | ||
|
|
||
| const uint64_t timestampWarningLogIntervalUs = 300e6; // log at warning level at most every 5 minutes | ||
| const uint64_t maxFrameSetTimeDiffUs = | ||
| 2000; // max time difference between frames in a frameset to be considered simultaneous, in microseconds (equal to 2 ms) | ||
|
|
||
| // Model configurations | ||
| namespace { | ||
| static const OrbbecModelConfig ASTRA2_CONFIG{ | ||
|
|
@@ -213,6 +217,18 @@ std::string formatError(Args&&... args) { | |
| return buffer.str(); | ||
| } | ||
|
|
||
| // Get the best available timestamp for a frame (Global > System) | ||
| uint64_t getBestTimestampUs(std::shared_ptr<ob::Frame> frame) { | ||
| if (frame == nullptr) { | ||
| return 0; | ||
| } | ||
| uint64_t ts = frame->getGlobalTimeStampUs(); | ||
| if (ts > 0) { | ||
| return ts; | ||
| } | ||
| return frame->getSystemTimeStampUs(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. according to your description, it sounds like we shouldn't use system timestamp bc it's unreliable why are we still using it as a fallback when somehow also, how can could it happen when global timestamp is not enabled on the system?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we log a single, not noisy warning that color-depth timestamp comparisons are not supported when global timestamp isn't available? Instead of falling back to the unreliable system timestamp?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm, another option would be to support system timestamp and put a threshold of perhaps 400ms between frames. My thinking is that it may be better to have that rough measurement than nothing, but totally open to discuss it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I researched this further, even with GlobalTimestamps we are still seeing differences between the frames. I know this is a known issue at Orbbec, I just sent them an email to ask about it. In the meantime, I'm going to use GlobalTimestamps, throttle this log to once per minute, and log always on debug level.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I prefer to keep support for System Timestamps for know, to be future proof for cameras we may support in the future that doesn't support GlobalTimestamps.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we log once per five minutes and keep it at the info level? would that be considered "spammy" still?..
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the latest improvements, on this PR, we should not see any more timestamp differ logs, I'll set this to 5 minutes but raise this to warn instead of info. |
||
| } | ||
|
|
||
| // Validate color frame format and timestamp | ||
| void validateColorFrame(std::shared_ptr<ob::Frame> color, | ||
| const std::optional<DeviceFormat>& device_format_opt, | ||
|
|
@@ -234,7 +250,7 @@ void validateColorFrame(std::shared_ptr<ob::Frame> color, | |
|
|
||
| // Timestamp validation | ||
| uint64_t nowUs = getNowUs(); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, color->getSystemTimeStampUs()); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(color)); | ||
| if (diff > maxFrameAgeUs) { | ||
| throw std::runtime_error(formatError("no recent color frame: check connection, diff: ", diff, "us")); | ||
| } | ||
|
|
@@ -271,7 +287,7 @@ void validateDepthFrame(std::shared_ptr<ob::Frame> depth, | |
|
|
||
| // Timestamp validation | ||
| uint64_t nowUs = getNowUs(); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, depth->getSystemTimeStampUs()); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(depth)); | ||
| if (diff > maxFrameAgeUs) { | ||
| throw std::runtime_error(formatError("no recent depth frame: check connection, diff: ", diff, "us")); | ||
| } | ||
|
|
@@ -654,33 +670,33 @@ auto frameCallback(const std::string& serialNumber) { | |
|
|
||
| std::lock_guard<std::mutex> lock(frame_set_by_serial_mu()); | ||
| uint64_t nowUs = getNowUs(); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, color->getSystemTimeStampUs()); | ||
| uint64_t diff = timeSinceFrameUs(nowUs, getBestTimestampUs(color)); | ||
| if (diff > maxFrameAgeUs) { | ||
| std::cerr << "color frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " | ||
| << color->getSystemTimeStampUs() << "\n"; | ||
| std::cerr << "color frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " << getBestTimestampUs(color) | ||
| << "\n"; | ||
| } | ||
| diff = timeSinceFrameUs(nowUs, depth->getSystemTimeStampUs()); | ||
| diff = timeSinceFrameUs(nowUs, getBestTimestampUs(depth)); | ||
| if (diff > maxFrameAgeUs) { | ||
| std::cerr << "depth frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " | ||
| << depth->getSystemTimeStampUs() << "\n"; | ||
| std::cerr << "depth frame is " << diff << "us older than now, nowUs: " << nowUs << " frameTimeUs " << getBestTimestampUs(depth) | ||
| << "\n"; | ||
| } | ||
|
|
||
| auto it = frame_set_by_serial().find(serialNumber); | ||
| if (it != frame_set_by_serial().end()) { | ||
| std::shared_ptr<ob::Frame> prevColor = it->second->getFrame(OB_FRAME_COLOR); | ||
| std::shared_ptr<ob::Frame> prevDepth = it->second->getFrame(OB_FRAME_DEPTH); | ||
| if (prevColor != nullptr && prevDepth != nullptr) { | ||
| diff = timeSinceFrameUs(color->getSystemTimeStampUs(), prevColor->getSystemTimeStampUs()); | ||
| diff = timeSinceFrameUs(getBestTimestampUs(color), getBestTimestampUs(prevColor)); | ||
| if (diff > maxFrameAgeUs) { | ||
| std::cerr << "previous color frame is " << diff | ||
| << "us older than current color frame. previousUs: " << prevColor->getSystemTimeStampUs() | ||
| << " currentUs: " << color->getSystemTimeStampUs() << "\n"; | ||
| << "us older than current color frame. previousUs: " << getBestTimestampUs(prevColor) | ||
| << " currentUs: " << getBestTimestampUs(color) << "\n"; | ||
| } | ||
| diff = timeSinceFrameUs(depth->getSystemTimeStampUs(), prevDepth->getSystemTimeStampUs()); | ||
| diff = timeSinceFrameUs(getBestTimestampUs(depth), getBestTimestampUs(prevDepth)); | ||
| if (diff > maxFrameAgeUs) { | ||
| std::cerr << "previous depth frame is " << diff | ||
| << "us older than current depth frame. previousUs: " << prevDepth->getSystemTimeStampUs() | ||
| << " currentUs: " << depth->getSystemTimeStampUs() << "\n"; | ||
| << "us older than current depth frame. previousUs: " << getBestTimestampUs(prevDepth) | ||
| << " currentUs: " << getBestTimestampUs(depth) << "\n"; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -700,6 +716,14 @@ void configureDevice(std::string serialNumber, OrbbecModelConfig const& modelCon | |
|
|
||
| std::unique_ptr<ViamOBDevice>& my_dev = search->second; | ||
|
|
||
| // Enable global timestamp if supported | ||
| if (my_dev->device->isGlobalTimestampSupported()) { | ||
seanavery marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| my_dev->device->enableGlobalTimestamp(true); | ||
| VIAM_SDK_LOG(info) << "[configureDevice] Global timestamp enabled for device " << serialNumber; | ||
| } else { | ||
| VIAM_SDK_LOG(info) << "[configureDevice] Global timestamp not supported for device " << serialNumber; | ||
| } | ||
|
|
||
| // Initialize fields if not already set | ||
| if (!my_dev->pipe) { | ||
| my_dev->pipe = std::make_shared<ob::Pipeline>(my_dev->device); | ||
|
|
@@ -772,6 +796,33 @@ void configureDevice(std::string serialNumber, OrbbecModelConfig const& modelCon | |
| VIAM_SDK_LOG(info) << "Device " << serialNumber << " supports hardware depth-to-color alignment, using it"; | ||
| } | ||
|
|
||
| // If color and depth are enabled and they operate on the same FPS, we can enable OB_MULTI_DEVICE_SYNC_MODE_STANDALONE to make color and | ||
| // depth frames to be synced. More info here: | ||
| // https://orbbec.github.io/OrbbecSDK/doc/api/English/ObTypes_8h.html#aabad929b67059752029c3374594ac63ba6bd45c53211519f5392eab382e229225 | ||
| if (config != nullptr) { | ||
| std::shared_ptr<ob::VideoStreamProfile> colorProfile = nullptr; | ||
| std::shared_ptr<ob::VideoStreamProfile> depthProfile = nullptr; | ||
| auto profiles = config->getEnabledStreamProfileList(); | ||
| for (uint32_t i = 0; i < profiles->getCount(); i++) { | ||
| auto profile = profiles->getProfile(i); | ||
| if (profile->getType() == OB_STREAM_COLOR) { | ||
| colorProfile = profile->as<ob::VideoStreamProfile>(); | ||
| } else if (profile->getType() == OB_STREAM_DEPTH) { | ||
| depthProfile = profile->as<ob::VideoStreamProfile>(); | ||
| } | ||
| } | ||
|
|
||
| if (colorProfile != nullptr && depthProfile != nullptr && colorProfile->getFps() == depthProfile->getFps()) { | ||
| // Set multi-device sync mode to standalone to avoid depth/color timestamp differences | ||
| auto curConfig = my_dev->device->getMultiDeviceSyncConfig(); | ||
| // Update the configuration items of the configuration file, and keep the original configuration for other items | ||
| VIAM_SDK_LOG(info) << "[configureDevice] Setting multi-device sync mode to standalone for device " << serialNumber | ||
| << " with sync mode " << curConfig.syncMode << " to " << OB_MULTI_DEVICE_SYNC_MODE_STANDALONE; | ||
| curConfig.syncMode = OB_MULTI_DEVICE_SYNC_MODE_STANDALONE; | ||
| my_dev->device->setMultiDeviceSyncConfig(curConfig); | ||
| } | ||
| } | ||
|
|
||
| if (config == nullptr) { | ||
| std::ostringstream buffer; | ||
| buffer << service_name << ": unable to configure device " << serialNumber << " - no valid stream configuration found"; | ||
|
|
@@ -1341,26 +1392,48 @@ vsdk::Camera::image_collection Orbbec::get_images(std::vector<std::string> filte | |
| return response; | ||
| } | ||
|
|
||
| uint64_t colorTS = color ? color->getSystemTimeStampUs() : 0; | ||
| uint64_t depthTS = depth ? depth->getSystemTimeStampUs() : 0; | ||
| uint64_t timestamp = 0; | ||
| uint64_t const colorTSUs = color ? getBestTimestampUs(color) : 0; | ||
| uint64_t const depthTSUs = depth ? getBestTimestampUs(depth) : 0; | ||
| uint64_t const timeDiff = (colorTSUs > depthTSUs) ? colorTSUs - depthTSUs : depthTSUs - colorTSUs; | ||
| uint64_t timestampUs = 0; | ||
|
|
||
| if (colorTSUs > 0 && depthTSUs > 0 && timeDiff > maxFrameSetTimeDiffUs) { | ||
| // Always log at debug level | ||
| VIAM_RESOURCE_LOG(debug) << "color and depth timestamps differ, " | ||
| << "color: " << colorTSUs << " depth: " << depthTSUs << ", diff: " << timeDiff << "us"; | ||
|
|
||
| if (colorTS > 0 && depthTS > 0) { | ||
| if (colorTS != depthTS) { | ||
| VIAM_RESOURCE_LOG(info) << "color and depth timestamps differ, defaulting to " | ||
| "older of the two" | ||
| << "color timestamp was " << colorTS << " depth timestamp was " << depthTS; | ||
| // Throttled info-level logging | ||
| uint64_t lastTimestampLogTime = 0; | ||
| { | ||
| std::lock_guard<std::mutex> lock(devices_by_serial_mu()); | ||
| auto search = devices_by_serial().find(serial_number); | ||
| if (search != devices_by_serial().end()) { | ||
| lastTimestampLogTime = search->second->lastTimestampLogTime; | ||
| } | ||
| } | ||
| uint64_t nowUs = | ||
| std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | ||
| if (nowUs - lastTimestampLogTime > timestampWarningLogIntervalUs) { | ||
| VIAM_RESOURCE_LOG(warn) << "color and depth timestamps differ by " << timeDiff << "us, using older timestamp. " | ||
| << "(This warning throttled to once per 60s; see debug for all occurrences)"; | ||
| { | ||
| std::lock_guard<std::mutex> lock(devices_by_serial_mu()); | ||
| auto search = devices_by_serial().find(serial_number); | ||
| if (search != devices_by_serial().end()) { | ||
| search->second->lastTimestampLogTime = nowUs; | ||
| } | ||
| } | ||
| } | ||
| // use the older of the two timestamps | ||
| timestamp = (colorTS > depthTS) ? depthTS : colorTS; | ||
| } else if (colorTS > 0) { | ||
| timestamp = colorTS; | ||
| timestampUs = (colorTSUs > depthTSUs) ? depthTSUs : colorTSUs; | ||
| } else if (colorTSUs > 0) { | ||
| timestampUs = colorTSUs; | ||
| } else { | ||
| timestamp = depthTS; | ||
| timestampUs = depthTSUs; | ||
| } | ||
|
|
||
| std::chrono::microseconds latestTimestamp(timestamp); | ||
| response.metadata.captured_at = vsdk::time_pt{std::chrono::duration_cast<std::chrono::nanoseconds>(latestTimestamp)}; | ||
| std::chrono::microseconds latestTimestampUs(timestampUs); | ||
| response.metadata.captured_at = vsdk::time_pt{std::chrono::duration_cast<std::chrono::nanoseconds>(latestTimestampUs)}; | ||
| VIAM_RESOURCE_LOG(debug) << "[get_images] end"; | ||
| return response; | ||
| } catch (const std::exception& e) { | ||
|
|
@@ -1785,16 +1858,16 @@ void startOrbbecSDK(ob::Context& ctx) { | |
| } catch (ob::Error& e) { | ||
| VIAM_SDK_LOG(error) << "Failed to query Orbbec devices: " << e.what() << " (function: " << e.getFunction() | ||
| << ", args: " << e.getArgs() << ", name: " << e.getName() << ", type: " << e.getExceptionType() << ")"; | ||
| VIAM_SDK_LOG(warn) | ||
| << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB connection for USB cameras"; | ||
| VIAM_SDK_LOG(warn) << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB " | ||
| "connection for USB cameras"; | ||
| } catch (const std::exception& e) { | ||
| VIAM_SDK_LOG(error) << "Failed to query Orbbec devices: " << e.what(); | ||
| VIAM_SDK_LOG(warn) | ||
| << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB connection for USB cameras"; | ||
| VIAM_SDK_LOG(warn) << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB " | ||
| "connection for USB cameras"; | ||
| } catch (...) { | ||
| VIAM_SDK_LOG(error) << "Failed to query Orbbec devices: unknown error"; | ||
| VIAM_SDK_LOG(warn) | ||
| << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB connection for USB cameras"; | ||
| VIAM_SDK_LOG(warn) << "Continuing without Orbbec devices - check network connectivity for Ethernet cameras or USB " | ||
| "connection for USB cameras"; | ||
| } | ||
| } | ||
| // ORBBEC SDK DEVICE REGISTRY END | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.