Skip to content

Conversation

@SebastianMunozP
Copy link
Contributor

@SebastianMunozP SebastianMunozP commented Oct 24, 2025

Providing support for Gemini 335LE cameras, until now, the Orbbec module was designed to exclusively support Astra2 cameras, so this PR involves a redesign of the Orbbec module to support for different Orbbec model cameras, adding support at this instance to Gemini 335LE.

Per documentation, the camera only supports MJPG color stream format natively, it supports RGB variants through the SDK. I'm disabling that option for now until thoroughly tested as well, so only supporting MJPG at this time.

Testing Performed:

  1. Having a Astra2 and an Gemini335LE resources in the same robot, streamed continuously for a day
  2. Successfully tested the firmware update of this camera to the recommended FW for our SDK version
  3. Hot unplugging/replugging Gemini and Astra2 cameras
  4. Tested the following color->depth resolutions using the supported MJPG, Y16 formats.
- Aspect Ratio 16:10:
	- 1280 X 800
		- 1280 X 1280: OK
		- 848 X 530: OK
		- 640 X 400: OK
		- 424 X 266: OK
		- 320 X 200: OK
	- 848 X 530
		- 848 X 530: OK
		- 640 X 400: OK
		- 424 X 266: OK
		- 320 X 200: OK
	- 640 X 400
		- 424 X 266: OK
		- 320 X 200: OK
- Aspect Ratio 4:3:
	- 640 X 480
		- 640 X 480: OK

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for the Orbbec Gemini 335LE camera model in addition to the existing Astra 2, implementing a model-based configuration system to handle multiple camera types. The changes include refactoring model-specific configurations, adding firmware update support, Windows registry setup utilities, and improving device discovery for both USB and Ethernet-connected cameras.

  • Introduced model configuration system with separate configs for Astra 2 and Gemini 335LE
  • Added firmware update functionality and Windows registry setup as separate modules
  • Enhanced device discovery to support network-connected cameras

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/module/orbbec_windows_registry.hpp Header for Windows registry setup utilities
src/module/orbbec_windows_registry.cpp Implementation of Windows device registry configuration
src/module/orbbec_firmware.hpp Header declaring firmware update functionality
src/module/orbbec_firmware.cpp Implementation of firmware download and update process
src/module/orbbec.hpp Added model configurations, multiple model support, and updated method signatures
src/module/orbbec.cpp Refactored to use model configs, added validation helpers, improved error handling
src/module/main.cpp Registered both Astra2 and Gemini335Le models
src/module/discovery.cpp Enhanced discovery to support network devices and multiple models
CMakeLists.txt Added new firmware and Windows registry source files to build

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

SebastianMunozP and others added 20 commits October 27, 2025 14:48
…, preventing reutilizing a camera by multiple resources by the if my_dev->started in startDevice
…ons are not supported instead of silently picking other ones
…until it has been tested, removing support for RGB color stream on Gemini as it didnt work with a 640X480 resolution -> need to investigate which are actually supported, removed raw pointers in favor of const references, adding supported and default formats to each model config
…n nowUs <= imageTimeUs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@SebastianMunozP SebastianMunozP force-pushed the support-gemini350LE-camera branch from 544917e to 767ce34 Compare October 27, 2025 18:49
@SebastianMunozP
Copy link
Contributor Author

Hello @williamjhyland , @IanWhalen

Please not that for the time being, we are supporting the following set of resolutions, and only MJPG/Y16 as formats for color/depth streams respectively, is this OK for the customer?

- Aspect Ratio 16:10:
	- 1280 X 800
		- 1280 X 1280: OK
		- 848 X 530: OK
		- 640 X 400: OK
		- 424 X 266: OK
		- 320 X 200: OK
	- 848 X 530
		- 848 X 530: OK
		- 640 X 400: OK
		- 424 X 266: OK
		- 320 X 200: OK
	- 640 X 400
		- 424 X 266: OK
		- 320 X 200: OK
- Aspect Ratio 4:3:
	- 640 X 480
		- 640 X 480: OK

@williamjhyland
Copy link

Checking regarding customer commentary.

deviceInfoString << "Device " << (i + 1) << " - Name: " << deviceName << ", Serial: " << serialNumber
<< ", Connection: " << connectionType;
if (!ipAddress.empty()) {
deviceInfoString << ", IP: " << ipAddress;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ethernet cameras does the IP address need to be in the config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, the SDK takes care of that, it has its own "discovery service", it detects all Orbbec cameras reachable therough USB or the network


VIAM_RESOURCE_LOG(info) << "Successfully configured device " << (i + 1) << " with serial: " << serialNumber;
} catch (ob::Error& deviceError) {
VIAM_RESOURCE_LOG(warn) << "Failed to get device info for device " << (i + 1) << ": " << deviceError.what();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be an error log?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, raised to error

VIAM_RESOURCE_LOG(warn) << "Discovery failed - check network connectivity for Ethernet cameras or USB connection for USB cameras";
} catch (...) {
VIAM_RESOURCE_LOG(error) << "Failed to discover Orbbec devices: unknown error";
VIAM_RESOURCE_LOG(warn) << "Discovery failed - check network connectivity for Ethernet cameras or USB connection for USB cameras";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with these, they feel more like errors than warnings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, raised to error

const OrbbecModelConfig& OrbbecModelConfig::forDevice(const std::string& device_name) {
if (device_name.find("Astra2") != std::string::npos || device_name.find("Astra 2") != std::string::npos)
return ASTRA2_CONFIG;
if (device_name.find("Gemini 335Le") != std::string::npos)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible the device name could be "Gemini335Le" like the Astra2 check above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the exact names that I get from the Device for simplicity.

Just bear in mind that for unexplicable reasons, Astra2 can return to different names depending on the way you query, either "Orbbec Astra2" or "Orbbec Astra 2", hence I added support for multiple names :/

throw std::runtime_error("Unsupported Orbbec camera model: " + device_name);
}

bool OrbbecModelConfig::isSupported(const std::string& device_name) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we combine this with the function above since they pretty much do the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

std::string const serial = attrs["serial_number"].get_unchecked<std::string>();
if (serial.empty()) {
throw std::invalid_argument("serial_number must be a non-empty string");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could me make a helper for these above checks since the code is repeated in the validateGemini function ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or is it possible to combine into only one validate function? Would be harder to mantain a validate function for every model if we add more in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done

buffer << "Device firmware already on version " << model_config_->min_firmware_version;
resp.emplace(firmware_key, buffer.str());
break;
VIAM_RESOURCE_LOG(info) << buffer.str();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we log the buffer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no harm in adding this to the logs as well, but we may want it to investigate potential issues, specially with customers. This log is not repetitive, so wont be intrusive.

} else {
VIAM_SDK_LOG(warn) << "VIAM_MODULE_DATA is set to " << viam_module_data
<< " but is not a valid directory, using current working directory to store PCD file";
VIAM_RESOURCE_LOG(warn) << "VIAM_MODULE_DATA is set to " << viam_module_data
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you run into this env var not having a valid directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, the only change I'm doing in this part is to use VIAM_RESOURCE_LOG instead of VIAM_SDK_LOG for better clarity in the logs

VIAM_SDK_LOG(error) << "Current device does not support hardware depth-to-color "
"alignment.";
return;
VIAM_SDK_LOG(warn) << "Device " << serialNumber << " does not support hardware depth-to-color alignment, trying software alignment";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we do this both in registerDevice and startDevice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was present before, which raises a really good point, I restructured this logic as follows:

registerDevice -> Only creates an unconfigured device, containing only its serialNumber and an empty ViamOBDevice.

configureDevice -> New function, takes care of actually configuring the device, i.e. taking the specs from the user and making all adjustments to the device to comply with them

startDevice -> Simply starts the device, nothing more

README.md Outdated
## Model viam:orbbec:gemini_335le

### Attributes
Use [Orbbec cameras](https://www.orbbec.com/gemini-335le/).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong label for link?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try {
const auto& modelConfig = orbbec::OrbbecModelConfig::forDevice(deviceName);
viamModelSuffix = modelConfig.viam_model_suffix;
} catch (const std::runtime_error& e) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're sure all runtime errors = camera unsupported? any more specific error to catch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

int requiredMajor = 0, requiredMinor = 0, requiredPatch = 0;

// ignore any trailing text in the case of a beta or RC version
sscanf(version.c_str(), "%d.%d.%d*s", &major, &minor, &patch);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is *s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means to discard the string after that last integer, in particular

"*" -> means discard
"s" -> means string

put together is to discard the trailing string.


namespace orbbec {

namespace windows_registry {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested on windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disabled this camera for all OSs other than Linux for now, I tested on Mac and the camera could not be reached.

viam::sdk::LogSource& logger);

} // namespace firmware
} // namespace orbbec No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] newline eof missing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@SebastianMunozP SebastianMunozP merged commit 9a29244 into viam-modules:main Oct 30, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants