Delete redundant launch files

This commit is contained in:
jj
2025-06-10 22:50:08 +08:00
parent 900e401149
commit 22d0c137ed
149 changed files with 0 additions and 7875 deletions
-22
View File
@@ -1,22 +0,0 @@
# Sample launch for OrbbecSDK_ROS2
These simple examples demonstrate how to easily use the camera with OrbbecSDK_ROS2
## List of Examples:
| Name | Description | Experience Level |
| :---------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| [Start_camera_node](./start_camera_node) | Hhow to launch the camera node with a colored point cloud feature enabled using OrbbecSDK_ROS2. | ⭐️ |
| [Align_depth_color](./align_depth_color) | Align the depth image with the color image to create an overlay image. | ⭐️ |
| [Point_cloud](./point_cloud) | How to enable point cloud data output from the camera node and visualize it using RViz2 | ⭐️ |
| [Lower_cpu_usage](./lower_cpu_usage) | How to reduce CPU usage for one or more cameras in a ROS 2 environment. | ⭐️ |
| [Net_camera](./net_camera) | How to use Net camera in OrbbecSDK_ROS2(Only Femto Mega nad Gemini 335Le) | ⭐️ |
| [Gmsl_camera](./gmsl_camera) | How to use GMSL camera.(Only Gemini 335Lg) | ⭐️ |
| [Benchmark](./benchmark) | The goal of this tool is to benchmark the performance of various OrbbecSDK_ROS2 camera configurations. | ⭐️⭐️ |
| [Disparity_search_offset](./disparity_search_offset) | Use the disparity_search_offset function in the Gemini330 series cameras (minimum camera firmware version[1.4.60](https://www.orbbec.com/docs/g330-firmware-release/)). | ⭐️⭐️ |
| [Interleave_ae_mode](./interleave_ae_mode) | How to use interleave_ae in Gemini 330 series cameras (minimum camera firmware version[1.4.00](https://www.orbbec.com/docs/g330-firmware-release/)) | ⭐️⭐️ |
| [Multi_camera_synced](./multi_camera_synced) | How to use multi-camera synced with OrbbecSDK_ROS2 | ⭐️⭐️ |
| [Multi_camera_synced_verification_tool](./multi_camera_synced_verification_tool) | How to verify the synchronization accuracy of multi-camera synchronization. | ⭐️⭐️⭐️ |
@@ -1,39 +0,0 @@
## Aligning Depth to Color in ROS 2
This section explains how to align depth images with color images to create an overlay image using ROS 2. This is particularly useful for applications requiring synchronized visual information from different sensor modalities.
### Commands to Align and View Depth and Color Images
1. **Basic Depth to Color Alignment:**
To simply align the depth image to the color image, use the following command:
```bash
ros2 launch orbbec_camera gemini_330_series.launch.py depth_registration:=true
```
This command activates the depth registration feature without opening a viewer.
2. **Viewing Depth to Color Overlay:**
If you wish to view the depth to color overlay, you need to enable the viewer by using the command below:
```bash
ros2 launch orbbec_camera gemini_330_series.launch.py depth_registration:=true enable_d2c_viewer:=true
```
This launches the camera node with depth to color registration and opens a viewer to display the overlay image.
### Selecting Topics in RViz2
To visualize the aligned images in RViz2:
1. Launch RViz2 after running one of the above commands.
2. Select the topic for the depth to color overlay image. An example topic selection is shown here:
![Topic Selection for Depth to Color Overlay](./image/image3.png)
### Example of Depth to Color Overlay
After selecting the appropriate topic in RViz2, you will be able to see the depth to color overlay image. Here's what it might look like:
![Depth to Color Overlay Image](./image/image4.jpg)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

@@ -1,51 +0,0 @@
# Ob_benchmark tool
> The goal of this tool is to benchmark the performance of various OrbbecSDK_ROS2 camera configurations. The benchmark results depend on the camera and settings used.(Currently only works with ROS2 Humble)
## Usage Instructions
### Tool Configuration ([start_benchmark_params.json](../../config/tools/startbenchmark/start_benchmark_params.json))
```json
{
"start_benchmark_params": {
"camera_name": [
"camera_01",
"camera_02",
"camera_03",
"camera_04"
],
"process_name": "component_conta",
"switch_cycle": 300,
"test_cycle": 1,
"skip_number": 30
}
}
```
* `camera_name`: Names of the cameras to be configured. Example: `"camera_01"`, `"camera_02"`, etc.
* `process_name`: The name of the process to be monitored. For example, `"component_conta"` will monitor the data of the container process.
* `switch_cycle`: The cycle time for switching configurations, in seconds. For example, setting it to `300` means the configuration will switch every 300 seconds.
* `test_cycle`: The testing cycle, in seconds. For example, setting it to `1` means the tool will collect data for the monitored process every 1 second.
* `skip_number`: The number of data points to skip. For example, setting it to `30` means that the first 30 data points will be ignored.
### Camera configuration (launch files)
In the launch folder, there are multiple\.launch.py files (`ob_benchmark_0.launch.py`, `ob_benchmark_1.launch.py`, ..., `ob_benchmark_19.launch.py`). Each file corresponds to a different camera configuration.
### Running the ob_benchmark tool
To run the tool, use the following commands:
```bash
source install/setup.bash
ros2 run orbbec_camera ob_benchmark_node
```
### Output Data Files
The output data files will be stored in the ob_benchmark folder with filenames like `0.csv`, `1.csv`, ..., 19.csv. For example:
* `0.csv` contains data from the `ob_benchmark_0.launch.py` configuration.
* `1.csv` contains data from the `ob_benchmark_1.launch.py` configuration.
@@ -1,318 +0,0 @@
import os
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
from launch.conditions import UnlessCondition
from launch_ros.actions import LoadComposableNodes
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
args = [
DeclareLaunchArgument('camera_name', default_value='camera'),
DeclareLaunchArgument('depth_registration', default_value='true'),
DeclareLaunchArgument('serial_number', default_value=''),
DeclareLaunchArgument('usb_port', default_value=''),
DeclareLaunchArgument('device_num', default_value='1'),
DeclareLaunchArgument('upgrade_firmware', default_value=''),
DeclareLaunchArgument('preset_firmware_path', default_value=''),
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('cloud_frame_id', default_value=''),
DeclareLaunchArgument('connection_delay', default_value='10'),
DeclareLaunchArgument('color_width', default_value='0'),
DeclareLaunchArgument('color_height', default_value='0'),
DeclareLaunchArgument('color_fps', default_value='0'),
DeclareLaunchArgument('color_format', default_value='ANY'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('color_qos', default_value='default'),
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('color_flip', default_value='false'),
DeclareLaunchArgument('color_mirror', default_value='false'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('color_exposure', default_value='-1'),
DeclareLaunchArgument('color_gain', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('color_brightness', default_value='-1'),
DeclareLaunchArgument('color_sharpness', default_value='-1'),
DeclareLaunchArgument('color_gamma', default_value='-1'),
DeclareLaunchArgument('color_saturation', default_value='-1'),
DeclareLaunchArgument('color_constrast', default_value='-1'),
DeclareLaunchArgument('color_hue', default_value='-1'),
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='0'),
DeclareLaunchArgument('depth_height', default_value='0'),
DeclareLaunchArgument('depth_fps', default_value='0'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('depth_qos', default_value='default'),
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('depth_flip', default_value='false'),
DeclareLaunchArgument('depth_mirror', default_value='false'),
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('depth_brightness', default_value='-1'),
DeclareLaunchArgument('left_ir_width', default_value='0'),
DeclareLaunchArgument('left_ir_height', default_value='0'),
DeclareLaunchArgument('left_ir_fps', default_value='0'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='false'),
DeclareLaunchArgument('left_ir_qos', default_value='default'),
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('left_ir_flip', default_value='false'),
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('right_ir_width', default_value='0'),
DeclareLaunchArgument('right_ir_height', default_value='0'),
DeclareLaunchArgument('right_ir_fps', default_value='0'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='false'),
DeclareLaunchArgument('right_ir_qos', default_value='default'),
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('right_ir_flip', default_value='false'),
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
DeclareLaunchArgument('ir_exposure', default_value='-1'),
DeclareLaunchArgument('ir_gain', default_value='-1'),
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('ir_brightness', default_value='-1'),
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
DeclareLaunchArgument('enable_accel', default_value='false'),
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
DeclareLaunchArgument('accel_rate', default_value='200hz'),
DeclareLaunchArgument('accel_range', default_value='4g'),
DeclareLaunchArgument('enable_gyro', default_value='false'),
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
DeclareLaunchArgument('publish_tf', default_value='true'),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
DeclareLaunchArgument('ir_info_url', default_value=''),
DeclareLaunchArgument('color_info_url', default_value=''),
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
# If you do not want to automatically enumerate network devices,
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
DeclareLaunchArgument('net_device_ip', default_value=''),
DeclareLaunchArgument('net_device_port', default_value='0'),
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
DeclareLaunchArgument('log_level', default_value='none'),
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
DeclareLaunchArgument('enable_ldp', default_value='true'),
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
DeclareLaunchArgument('sync_mode', default_value='standalone'),
DeclareLaunchArgument('depth_delay_us', default_value='0'),
DeclareLaunchArgument('color_delay_us', default_value='0'),
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
DeclareLaunchArgument('ordered_pc', default_value='false'),
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
DeclareLaunchArgument('align_mode', default_value='SW'),
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
DeclareLaunchArgument('config_file_path', default_value=''),
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
DeclareLaunchArgument('offset_index0', default_value='-1'),
DeclareLaunchArgument('offset_index1', default_value='-1'),
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
]
def get_params(context, args):
return [load_parameters(context, args)]
def create_node_action(context, args):
params = get_params(context, args)
ros_distro = os.environ.get("ROS_DISTRO", "humble")
if ros_distro == "foxy":
return [
Node(
package="orbbec_camera",
executable="orbbec_camera_node",
name="ob_camera_node",
namespace=LaunchConfiguration("camera_name"),
parameters=params,
output="screen",
)
]
else:
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
orbbec_container = Node(
name=component_container_name_arg,
package='rclcpp_components',
executable='component_container_mt',
output='screen',
condition=UnlessCondition(attach_to_shared_component_container_arg)
)
return [
orbbec_container,
LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace=LaunchConfiguration("camera_name"),
name=LaunchConfiguration("camera_name"),
package='orbbec_camera',
plugin='orbbec_camera::OBCameraNodeDriver',
parameters=params,
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
)
]
)
]
return LaunchDescription(
args + [
OpaqueFunction(function=lambda context: create_node_action(context, args))
]
)
@@ -1,197 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node,LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(package_dir, "examples/benchmark")
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
start_benchmark = LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace="start_benchmark",
name="start_benchmark",
package='orbbec_camera',
plugin='orbbec_camera::tools::StartBenchmark',
)
]
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
args = [
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),
DeclareLaunchArgument('depth_registration', default_value='false'),
DeclareLaunchArgument('color_width', default_value='848'),
DeclareLaunchArgument('color_height', default_value='480'),
DeclareLaunchArgument('color_fps', default_value='30'),
DeclareLaunchArgument('color_format', default_value='MJPG'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('depth_width', default_value='848'),
DeclareLaunchArgument('depth_height', default_value='480'),
DeclareLaunchArgument('depth_fps', default_value='30'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('left_ir_width', default_value='848'),
DeclareLaunchArgument('left_ir_height', default_value='480'),
DeclareLaunchArgument('left_ir_fps', default_value='30'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='true'),
DeclareLaunchArgument('right_ir_width', default_value='848'),
DeclareLaunchArgument('right_ir_height', default_value='480'),
DeclareLaunchArgument('right_ir_fps', default_value='30'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='true'),
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
]
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "2-7",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "2-2",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch3_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_03",
"usb_port": "2-1",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch4_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_04",
"usb_port": "2-3",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
delayed_left_camera = TimerAction(
period=2.0,
actions=args+[launch1_include],
)
delayed_right_camera = TimerAction(
period=4.0,
actions=args+[launch2_include],
)
delayed_rear_camera = TimerAction(
period=6.0,
actions=args+[launch3_include],
)
delayed_front_camera = TimerAction(
period=8.0,
actions=args+[launch4_include],
)
ld = LaunchDescription(
[
shared_orbbec_container,
start_benchmark,
delayed_left_camera,
delayed_right_camera,
delayed_rear_camera,
delayed_front_camera,
]
)
return ld
@@ -1,197 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node,LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(package_dir, "examples/benchmark")
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
start_benchmark = LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace="start_benchmark",
name="start_benchmark",
package='orbbec_camera',
plugin='orbbec_camera::tools::StartBenchmark',
)
]
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
args = [
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),
DeclareLaunchArgument('depth_registration', default_value='false'),
DeclareLaunchArgument('color_width', default_value='848'),
DeclareLaunchArgument('color_height', default_value='480'),
DeclareLaunchArgument('color_fps', default_value='30'),
DeclareLaunchArgument('color_format', default_value='MJPG'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('depth_width', default_value='848'),
DeclareLaunchArgument('depth_height', default_value='480'),
DeclareLaunchArgument('depth_fps', default_value='30'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('left_ir_width', default_value='848'),
DeclareLaunchArgument('left_ir_height', default_value='480'),
DeclareLaunchArgument('left_ir_fps', default_value='30'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='true'),
DeclareLaunchArgument('right_ir_width', default_value='848'),
DeclareLaunchArgument('right_ir_height', default_value='480'),
DeclareLaunchArgument('right_ir_fps', default_value='30'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='true'),
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='true'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
]
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "2-7",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "2-2",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch3_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_03",
"usb_port": "2-1",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch4_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
),
launch_arguments={
"camera_name": "camera_04",
"usb_port": "2-3",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
delayed_left_camera = TimerAction(
period=2.0,
actions=args+[launch1_include],
)
delayed_right_camera = TimerAction(
period=4.0,
actions=args+[launch2_include],
)
delayed_rear_camera = TimerAction(
period=6.0,
actions=args+[launch3_include],
)
delayed_front_camera = TimerAction(
period=8.0,
actions=args+[launch4_include],
)
ld = LaunchDescription(
[
shared_orbbec_container,
start_benchmark,
delayed_left_camera,
delayed_right_camera,
delayed_rear_camera,
delayed_front_camera,
]
)
return ld
@@ -1,47 +0,0 @@
# Disparity_search_offset
> This section describes how to use the disparity_search_offset function in the Gemini330 series cameras (minimum camera firmware version [1.4.60](https://www.orbbec.com/docs/g330-firmware-release/)).Disparity_search_offset is effective only for1280×720, 1280×800 and 640×400 resolutions of depth stream.
## Function Introduction
The definition of disparity search range: For any pixel *(u_l, v)* in the left image, by default, the corresponding disparity search range in the right image is *[ (u_l - 255, v)*, *(u_l, v) ]*, where the disparity search length is 256 and the maximum integer disparity is 255. If the starting point of the search is adjusted to *[ (u_l - 255 - offset, v)*, *(u_l - offset, v) ]*, the offset is defined as the disparity shift. Therefore, our disparity search range configuration includes both the disparity search length and the search position offset (which can also be referred to as the disparity shift).
![Depth Point Cloud Visualization](image/search_offset0.png)
## Parameter Introduction
The disparity_search_offset related parameters are set in [gemini_330_series.launch.py](../../launch/gemini_330_series.launch.py)
* `disparity_range_mode` : Disparity search length,can only be set to 64, 128 and 256.
* `disparity_search_offset` : Disparity search offset value,Disparity search offset value, can be set from 0 to 127.
* `disparity_offset_config` : Disparity search offset interleave frames.
* `offset_index0` : Frame 0 disparity search offset value.
* `offset_index1` : Frame 1 disparity search offset value.
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
| :------------------: | :---------------------: | :---------------------------------: |
| 64 | 85 | Gemini 335L  388-406 |
| 64 | 127 | Gemini 335L  302-317 |
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
| :------------------: | :---------------------: | :---------------------------------------------: |
| 128 | 0 | Gemini 335  233-249<br />Gemini 335L  453-475 |
| 128 | 45 | Gemini 335  172-184<br />Gemini 335L  334-349 |
| 128 | 127 | Gemini 335  117-125<br />Gemini 335L  226-236 |
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
| :------------------: | :---------------------: | :---------------------------------: |
| 256 | 85 | Gemini 335L  169-178 |
| 256 | 127 | Gemini 335L  151-158 |
## Run the launch
Setting the disparity_search_offset parameter,`colcon build` again and run launch
```bash
ros2 launch orbbec_camera gemini_330_series.launch.py
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

@@ -1,50 +0,0 @@
# GMSL_camera
> This section describes how to use GMSL camera in OrbbecSDK_ROS2.Currently, only Gemini 335Lg GMSL devices are supported, and other GMSL devices will be supported in the near future.
## Single GMSL camera
The usage of GMSL camera in OrbbecSDK_ROS2 is the same as that of Gemini 330 series camera via USB.
```bash
ros2 launch orbbec_camera gemini_330_gmsl.launch.py
```
## Multi GMSL camera
To get the `usb_port` of the GMSL camera, plug in the camera and run the following command in the terminal:
```bash
ros2 run orbbec_camera list_devices_node
```
For example, the obtained gmsl camera `usb_port`: `gmsl2-1`
Go to the [multi_gmsl_camera.launch.py](./multi_gmsl_camera.launch.py) file and change the `usb_port`.
```bash
ros2 launch orbbec_camera multi_gmsl_camera.launch.py
```
> Note: By default, multi_gmsl_camera.launch.py only starts color and left_ir. If you want to start other sensors, please go to [camera_secondary_params.yaml](../../config/camera_secondary_params.yaml) to modify them.
## Multi GMSL camera synced
First, please see how to use [multi_camera_synced](../multi_camera_synced/README.MD).
In addition, GMSL multi-camera synced does not require Multi-Camera Sync Hub Pro, so there is no need to set the `primary` mode. Each GMSL camera is `secondary`.
### Additional Parameter Settings
* `gmsl_trigger_fps` : set hardware soc trigger source frame rate.
* `enable_gmsl_trigger` : enable hardware soc trigger.
### Run the launch
Please refer to the configuration in [multi_gmsl_camera_synced.launch.py.](multi_gmsl_camera_synced.launch.py)
```bash
ros2 launch orbbec_camera multi_gmsl_camera_synced.launch.py
```
> Note: By default, multi_gmsl_camera_synced.launch.py only starts color and left_ir. If you want to start other sensors, please go to [camera_secondary_params.yaml](../../config/camera_secondary_params.yaml) and [camera_params.yaml](../../config/camera_params.yaml) to modify them.
@@ -1,318 +0,0 @@
import os
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
from launch.conditions import UnlessCondition
from launch_ros.actions import LoadComposableNodes
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
args = [
DeclareLaunchArgument('camera_name', default_value='camera'),
DeclareLaunchArgument('depth_registration', default_value='true'),
DeclareLaunchArgument('serial_number', default_value=''),
DeclareLaunchArgument('usb_port', default_value=''),
DeclareLaunchArgument('device_num', default_value='1'),
DeclareLaunchArgument('upgrade_firmware', default_value=''),
DeclareLaunchArgument('preset_firmware_path', default_value=''),
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('cloud_frame_id', default_value=''),
DeclareLaunchArgument('connection_delay', default_value='10'),
DeclareLaunchArgument('color_width', default_value='0'),
DeclareLaunchArgument('color_height', default_value='0'),
DeclareLaunchArgument('color_fps', default_value='0'),
DeclareLaunchArgument('color_format', default_value='ANY'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('color_qos', default_value='default'),
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('color_flip', default_value='false'),
DeclareLaunchArgument('color_mirror', default_value='false'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('color_exposure', default_value='-1'),
DeclareLaunchArgument('color_gain', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('color_brightness', default_value='-1'),
DeclareLaunchArgument('color_sharpness', default_value='-1'),
DeclareLaunchArgument('color_gamma', default_value='-1'),
DeclareLaunchArgument('color_saturation', default_value='-1'),
DeclareLaunchArgument('color_constrast', default_value='-1'),
DeclareLaunchArgument('color_hue', default_value='-1'),
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='0'),
DeclareLaunchArgument('depth_height', default_value='0'),
DeclareLaunchArgument('depth_fps', default_value='0'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('depth_qos', default_value='default'),
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('depth_flip', default_value='false'),
DeclareLaunchArgument('depth_mirror', default_value='false'),
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('depth_brightness', default_value='-1'),
DeclareLaunchArgument('left_ir_width', default_value='0'),
DeclareLaunchArgument('left_ir_height', default_value='0'),
DeclareLaunchArgument('left_ir_fps', default_value='0'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='false'),
DeclareLaunchArgument('left_ir_qos', default_value='default'),
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('left_ir_flip', default_value='false'),
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('right_ir_width', default_value='0'),
DeclareLaunchArgument('right_ir_height', default_value='0'),
DeclareLaunchArgument('right_ir_fps', default_value='0'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='false'),
DeclareLaunchArgument('right_ir_qos', default_value='default'),
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('right_ir_flip', default_value='false'),
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
DeclareLaunchArgument('ir_exposure', default_value='-1'),
DeclareLaunchArgument('ir_gain', default_value='-1'),
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('ir_brightness', default_value='-1'),
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
DeclareLaunchArgument('enable_accel', default_value='false'),
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
DeclareLaunchArgument('accel_rate', default_value='200hz'),
DeclareLaunchArgument('accel_range', default_value='4g'),
DeclareLaunchArgument('enable_gyro', default_value='false'),
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
DeclareLaunchArgument('publish_tf', default_value='true'),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
DeclareLaunchArgument('ir_info_url', default_value=''),
DeclareLaunchArgument('color_info_url', default_value=''),
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
# If you do not want to automatically enumerate network devices,
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
DeclareLaunchArgument('net_device_ip', default_value=''),
DeclareLaunchArgument('net_device_port', default_value='0'),
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
DeclareLaunchArgument('log_level', default_value='none'),
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
DeclareLaunchArgument('enable_ldp', default_value='true'),
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
DeclareLaunchArgument('sync_mode', default_value='standalone'),
DeclareLaunchArgument('depth_delay_us', default_value='0'),
DeclareLaunchArgument('color_delay_us', default_value='0'),
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
DeclareLaunchArgument('ordered_pc', default_value='false'),
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
DeclareLaunchArgument('align_mode', default_value='SW'),
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
DeclareLaunchArgument('config_file_path', default_value=''),
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
DeclareLaunchArgument('offset_index0', default_value='-1'),
DeclareLaunchArgument('offset_index1', default_value='-1'),
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
]
def get_params(context, args):
return [load_parameters(context, args)]
def create_node_action(context, args):
params = get_params(context, args)
ros_distro = os.environ.get("ROS_DISTRO", "humble")
if ros_distro == "foxy":
return [
Node(
package="orbbec_camera",
executable="orbbec_camera_node",
name="ob_camera_node",
namespace=LaunchConfiguration("camera_name"),
parameters=params,
output="screen",
)
]
else:
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
orbbec_container = Node(
name=component_container_name_arg,
package='rclcpp_components',
executable='component_container_mt',
output='screen',
condition=UnlessCondition(attach_to_shared_component_container_arg)
)
return [
orbbec_container,
LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace=LaunchConfiguration("camera_name"),
name=LaunchConfiguration("camera_name"),
package='orbbec_camera',
plugin='orbbec_camera::OBCameraNodeDriver',
parameters=params,
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
)
]
)
]
return LaunchDescription(
args + [
OpaqueFunction(function=lambda context: create_node_action(context, args))
]
)
@@ -1,108 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node, LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, "r") as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == "true":
return True
elif value.lower() == "false":
return False
return value
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(
package_dir, "examples/gmsl_camera"
)
config_file_dir = os.path.join(package_dir, "config")
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
attach_to_shared_component_container_arg = LaunchConfiguration(
"attach_to_shared_component_container", default=False
)
component_container_name_arg = LaunchConfiguration(
"component_container_name", default="shared_orbbec_container"
)
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "gmsl2-1",
"device_num": "2",
"sync_mode": "standalone",
"config_file_path": secondary_config_file_path,
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "gmsl2-3",
"device_num": "2",
"sync_mode": "standalone",
"config_file_path": secondary_config_file_path,
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
# Launch description
ld = LaunchDescription(
[
shared_orbbec_container,
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
]
)
return ld
@@ -1,110 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node, LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, "r") as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == "true":
return True
elif value.lower() == "false":
return False
return value
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(
package_dir, "examples/gmsl_camera"
)
config_file_dir = os.path.join(package_dir, "config")
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
attach_to_shared_component_container_arg = LaunchConfiguration(
"attach_to_shared_component_container", default=False
)
component_container_name_arg = LaunchConfiguration(
"component_container_name", default="shared_orbbec_container"
)
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "gmsl2-1",
"device_num": "2",
"sync_mode": "secondary_synced",
"gmsl_trigger_fps": "3000",
"enable_gmsl_trigger": "true",
"config_file_path": secondary_config_file_path,
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "gmsl2-3",
"device_num": "2",
"sync_mode": "secondary_synced",
"config_file_path": secondary_config_file_path,
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
# Launch description
ld = LaunchDescription(
[
shared_orbbec_container,
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
]
)
return ld
@@ -1,81 +0,0 @@
# Using interleave_ae with Gemini330 series cameras
> This section describes how to use interleave_ae in Gemini 330 series cameras (minimum camera firmware version [1.4.00](https://www.orbbec.com/docs/g330-firmware-release/))
## Parameter Introduction
The interleave_ae related parameters are set in [gemini_330_series.launch.py](../../launch/gemini_330_series.launch.py)
* `interleave_ae_mode` : Set laser or hdr interleave.
* `interleave_frame_enable` : enable interleave frame mode.
* `interleave_skip_enable` : enable skip frame mode.
* `interleave_skip_index` : Set 0 for skip pattern ir, set 1 for skip flood ir.
### interleave hdr
When the `interleave_ae_mode` parameter is set to `hdr` and `interleave_frame_enable `is set to `true`, interleave hdr will be enabled
* `hdr_index1_laser_control` : Frame 1 laser switch settings.
* `hdr_index1_depth_exposure` : Frame 1 depth exposure value setting, not in AE mode.
* `hdr_index1_depth_gain` : Frame 1 depth gain value setting, not in AE mode.
* `hdr_index1_ir_brightness` : Frame 1 ir gain value setting.
* `hdr_index1_ir_ae_max_exposure` : Frame 1 ir maximum exposure value setting in AE (auto exposure).
* `hdr_index0_laser_control`: Frame 0 laser switch settings.
* `hdr_index0_depth_exposure`: Frame 0 depth exposure value setting, not in AE mode.
* `hdr_index0_depth_gain` : Frame 0 depth gain value setting, not in AE mode.
* `hdr_index0_ir_brightness` : Frame 0 ir gain value setting.
* `hdr_index0_ir_ae_max_exposure` : Frame 0 ir maximum exposure value setting in AE (auto exposure).
### interleave laser
When the `interleave_ae_mode` parameter is set to `laser` and `interleave_frame_enable `is set to `true`, interleave laser will be enabled
* `laser_index1_laser_control` : Frame 1 laser switch settings.
* `laser_index1_depth_exposure` : Frame 1 depth exposure value setting, not in AE mode.
* `laser_index1_depth_gain` : Frame 1 depth gain value setting, not in AE mode.
* `laser_index1_ir_brightness` : Frame 1 ir gain value setting.
* `laser_index1_ir_ae_max_exposure` : Frame 1 ir maximum exposure value setting in AE (auto exposure).
* `laser_index0_laser_control` : Frame 0 laser switch settings.
* `laser_index0_depth_exposure` : Frame 0 depth exposure value setting, not in AE mode.
* `laser_index0_depth_gain` : Frame 0 depth gain value setting, not in AE mode.
* `laser_index0_ir_brightness` : Frame 0 ir gain value setting.
* `laser_index0_ir_ae_max_exposure` : Frame 0 ir maximum exposure value setting in AE (auto exposure).
## Run the launch
Setting the interleave_ae parameter,`colcon build` again and run launch
```bash
ros2 launch orbbec_camera gemini_330_series.launch.py
```
#### Example Visualization
![Depth Point Cloud Visualization](image/interleave_ae0.jpeg)
![Depth Point Cloud Visualization](image/interleave_ae1.jpeg)
## Multi_camera_synced + Interleave_ae
Please refer to [multi_camera_synced](../multi_camera_synced/README.MD) and [Parameter Introduction](#parameter-introduction)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

@@ -1,86 +0,0 @@
## Reducing CPU Usage with Orbbec ROS Package
This document outlines strategies for minimizing CPU usage in the **OrbbecSDK_ROS2 v2** environment when using **Gemini 330 series cameras**. The firmware version must be **no lower than 1.4.10**, and `device` should be set to **Default**.
### Recommended Settings for Lower CPU Usage
To achieve the lowest possible CPU usage in OrbbecSDK_ROS2, it is recommended to configure the following parameters.
| Parameter | Recommendation | Note |
| :--------------: | :------------------------------------: | :--------------------------------------------: |
| `uvc_backend` | `v4l2` | Lower CPU usage compared to `libuvc` |
| `color_format` | `RGB` | Lower CPU usage than `MJPG` |
| `filter` | Only `hardware_noise_removal_filter` | Other filters significantly increase CPU usage |
### Launch Files Used for Testing
* `gemini_330_series_lower_cpu_usage.launch.py`
* `multi_camera_lower_cpu_usage.launch.py`
### Test environment
#### Hardware Configuration
* **CPU**: Intel i7-8700 @ 3.20GHz
* **Memory**: 24 GB
* **Storage**: Micron 2200S NVMe 256GB
* **GPU**: NVIDIA GeForce GTX 1660Ti
* **OS**: Ubuntu22.04
#### ROS Configuration
* **ROS Version**: ROS2 Humble
* **SDK Version**: OrbbecSDK_ROS2 v2.2.1
#### Camera Setup
* Devices: 2x Gemini 335, 1x Gemini 336, 1x Gemini 336L
* Firmware Version: 1.4.10
### Test Setup
* **Stream Settings:**
* Depth / IR Left / IR Right: 848×480 @ 30fps
* Color: 848×480 @ 30fps
Note: The following CPU usage data focuses on `uvc_backend`, `color_format` and various filter combinations.
### Test Results
### 1. `uvc_backend` Comparison (RGB format)
| libuvc CPU Usage | v4l2 CPU Usage | Absolute Change |
| :--------------: | :------------: | :-------------: |
| 182.8% | 118.8% | -64.0% |
The CPU usage can be significantly reduced with v4l2 backend. In our implementation, v4l2 works without requiring any patches to the Linux kernel, allowing users to easily switch between v4l2 and libuvc and maintaining full compatibility with standard Linux distributions.
### 2. `color_format` Comparison (MJPG vs RGB)
| Backend | MJPG CPU Usage | RGB CPU Usage | Absolute Change |
| :-----: | :------------: | :-----------: | :-------------: |
| libuvc | 347.7% | 182.8% | -164.9% |
| v4l2 | 170.0% | 118.8% | -51.2% |
The CPU usage can be reduced if the RGB format is selected instead of MJPG, since the decoding of MJPG image will consume the host CPU resource.
### 3. Filter Configuration Impact
| Filters Applied | libuvc CPU Usage | CPU Usage Increase | v4l2 CPU Usage | CPU Usage Increase |
| ----------------------------------------------------- | ---------------- | ------------------ | -------------- | ------------------ |
| No Filter (benchmark) | 182.8% | 0.0%(benchmark) | 118.8% | 0.0%(benchmark) |
| `(software)noise_removal_filter` | 218.0% | +35.2% | 128.5% | +9.7% |
| `(software)noise_removal_filter + spatial_filter` | 469.6% | +286.8% | 336.7% | +217.9% |
| `hardware_noise_removal_filter` | 186.3% | +3.5% | 115.4% | -3.4% |
| `hardware_noise_removal_filter + spatial_filter` | 251.3% | +68.5% | 152.5% | +33.7% |
Based on the test results, using only the `hardware_noise_removal_filter` results in a negligible change in CPU usage for both `libuvc` (+3.5%) and `v4l2` (-3.4%) compared to the no-filter benchmark, as this filter runs internally on the camera hardware. In contrast, other filters execute on the host system. Adding the `spatial_filter` to the hardware filter leads to a moderate increase in CPU usage, while applying the software-based `noise_removal_filter` —either alone or combined with `spatial_filter` —significantly increases CPU load. To maintain low CPU usage, it is recommended to avoid software-based filters and rely solely on the `hardware_noise_removal_filter`.
## Further Optimization
| Parameter | Recommendation | Note |
| :----------------------------: | :----------------------------------------------: | :---------------------------------------------: |
| `depth_registration` | `false` or `true` with `align_mode=HW` | Software alignment consumes more CPU |
| `enable_point_cloud` | `false` | Disabling point cloud reduces CPU usage |
| `enable_colored_point_cloud` | `false` | Disabling colored point cloud reduces CPU usage |
@@ -1,262 +0,0 @@
import os
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
from launch.conditions import UnlessCondition
from launch_ros.actions import LoadComposableNodes
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
args = [
DeclareLaunchArgument('camera_name', default_value='camera'),
DeclareLaunchArgument('depth_registration', default_value='false'),
DeclareLaunchArgument('serial_number', default_value=''),
DeclareLaunchArgument('usb_port', default_value=''),
DeclareLaunchArgument('device_num', default_value='1'),
DeclareLaunchArgument('uvc_backend', default_value='v4l2'),#libuvc or v4l2
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('cloud_frame_id', default_value=''),
DeclareLaunchArgument('connection_delay', default_value='10'),
DeclareLaunchArgument('color_width', default_value='0'),
DeclareLaunchArgument('color_height', default_value='0'),
DeclareLaunchArgument('color_fps', default_value='0'),
DeclareLaunchArgument('color_format', default_value='RGB'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('color_qos', default_value='default'),
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
DeclareLaunchArgument('color_exposure', default_value='-1'),
DeclareLaunchArgument('color_gain', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('color_brightness', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='0'),
DeclareLaunchArgument('depth_height', default_value='0'),
DeclareLaunchArgument('depth_fps', default_value='0'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('depth_qos', default_value='default'),
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
DeclareLaunchArgument('left_ir_width', default_value='0'),
DeclareLaunchArgument('left_ir_height', default_value='0'),
DeclareLaunchArgument('left_ir_fps', default_value='0'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='false'),
DeclareLaunchArgument('left_ir_qos', default_value='default'),
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('right_ir_width', default_value='0'),
DeclareLaunchArgument('right_ir_height', default_value='0'),
DeclareLaunchArgument('right_ir_fps', default_value='0'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='false'),
DeclareLaunchArgument('right_ir_qos', default_value='default'),
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
DeclareLaunchArgument('ir_exposure', default_value='-1'),
DeclareLaunchArgument('ir_gain', default_value='-1'),
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('ir_brightness', default_value='-1'),
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
DeclareLaunchArgument('enable_accel', default_value='false'),
DeclareLaunchArgument('accel_rate', default_value='200hz'),
DeclareLaunchArgument('accel_range', default_value='4g'),
DeclareLaunchArgument('enable_gyro', default_value='false'),
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
DeclareLaunchArgument('publish_tf', default_value='true'),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
DeclareLaunchArgument('ir_info_url', default_value=''),
DeclareLaunchArgument('color_info_url', default_value=''),
DeclareLaunchArgument('log_level', default_value='none'),
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
DeclareLaunchArgument('enable_ldp', default_value='true'),
DeclareLaunchArgument('sync_mode', default_value='standalone'),
DeclareLaunchArgument('depth_delay_us', default_value='0'),
DeclareLaunchArgument('color_delay_us', default_value='0'),
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
DeclareLaunchArgument('ordered_pc', default_value='false'),
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='true'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
DeclareLaunchArgument('align_mode', default_value='SW'),
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
DeclareLaunchArgument('config_file_path', default_value=''),
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
DeclareLaunchArgument('offset_index0', default_value='-1'),
DeclareLaunchArgument('offset_index1', default_value='-1'),
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
]
def get_params(context, args):
return [load_parameters(context, args)]
def create_node_action(context, args):
params = get_params(context, args)
ros_distro = os.environ.get("ROS_DISTRO", "humble")
if ros_distro == "foxy":
return [
Node(
package="orbbec_camera",
executable="orbbec_camera_node",
name="ob_camera_node",
namespace=LaunchConfiguration("camera_name"),
parameters=params,
output="screen",
)
]
else:
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
orbbec_container = Node(
name=component_container_name_arg,
package='rclcpp_components',
executable='component_container_mt',
output='screen',
condition=UnlessCondition(attach_to_shared_component_container_arg)
)
return [
orbbec_container,
LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace=LaunchConfiguration("camera_name"),
name=LaunchConfiguration("camera_name"),
package='orbbec_camera',
plugin='orbbec_camera::OBCameraNodeDriver',
parameters=params,
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
)
]
)
]
return LaunchDescription(
args + [
OpaqueFunction(function=lambda context: create_node_action(context, args))
]
)
@@ -1,150 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node,LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(package_dir, "examples/lower_cpu_usage")
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "2-7",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "2-2",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch3_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
),
launch_arguments={
"camera_name": "camera_03",
"usb_port": "2-6",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch4_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
),
launch_arguments={
"camera_name": "camera_04",
"usb_port": "2-3",
"device_num": "4",
"sync_mode": "standalone",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
delayed_left_camera = TimerAction(
period=2.0,
actions=[launch1_include],
)
delayed_right_camera = TimerAction(
period=4.0,
actions=[launch2_include],
)
delayed_rear_camera = TimerAction(
period=6.0,
actions=[launch3_include],
)
delayed_front_camera = TimerAction(
period=8.0,
actions=[launch4_include],
)
ld = LaunchDescription(
[
shared_orbbec_container,
TimerAction(period=0.0, actions=[GroupAction([delayed_left_camera])]),
TimerAction(period=2.0, actions=[GroupAction([delayed_right_camera])]),
TimerAction(period=4.0, actions=[GroupAction([delayed_rear_camera])]),
TimerAction(period=6.0, actions=[GroupAction([delayed_front_camera])]),
]
)
return ld
@@ -1,91 +0,0 @@
# Multi-Camera
- To get the `usb_port` of the camera, plug in the camera and run the following command in the terminal:
```bash
ros2 run orbbec_camera list_devices_node
```
- Set the `device_num` parameter to the number of cameras you have.
- Go to the `OrbbecSDK_ROS2/launch/multi_xxx.launch.py` file and change the `usb_port`.
- Don't forget to put the `include` tag inside the `group` tag.
Otherwise, the parameter values of different cameras may become contaminated.
```python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, GroupAction, ExecuteProcess
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory('orbbec_camera')
launch_file_dir = os.path.join(package_dir, 'launch')
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, 'gemini2L.launch.py')
),
launch_arguments={
'camera_name': 'camera_01',
'usb_port': '6-2.4.4.2', # replace your usb port here
'device_num': '2'
}.items()
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, 'gemini2L.launch.py')
),
launch_arguments={
'camera_name': 'camera_02',
'usb_port': '6-2.4.1', # replace your usb port here
'device_num': '2'
}.items()
)
# If you need more cameras, just add more launch_include here, and change the usb_port and device_num
# Launch description
ld = LaunchDescription([
GroupAction([launch1_include]),
GroupAction([launch2_include]),
])
return ld
```
- To launch the cameras, run the following command:
```bash
ros2 launch orbbec_camera multi_camera.launch.py
```
## No Data Stream from Multiple Cameras
**Insufficient Power Supply**:
- Ensure that each camera is connected to a separate hub.
- Use a powered hub to provide sufficient power to each camera.
**High Resolution**:
- Try lowering the resolution to resolve data stream issues.
**Increase usbfs_memory_mb Value**:
- Increase the `usbfs_memory_mb` value to 128MB (this is a reference value and can be adjusted based on your systems needs)
by running the following command:
```bash
echo 128 | sudo tee /sys/module/usbcore/parameters/usbfs_memory_mb
```
- To make this change permanent, check [this link](https://github.com/OpenKinect/libfreenect2/issues/807).
## Image topic frame rate too low from Multiple Cameras
Refer to the [Fast DDS Configuration](./docs/fastdds_tuning.md) file.
@@ -1,55 +0,0 @@
# Multi_camera synced Instructions
> The purpose of this document is to explain how to use multi-camera synced with OrbbecSDK_ROS2
## Setup instructions
* Please read the Multi-Camera Synchronization Setup Guide:[Multi-Camera Synchronization Setup](https://www.orbbec.com/docs/set-up-cameras-for-external-synchronization_v1-2/)
* Make sure the camera is correctly connected to the multi-camera synchronizer.
![Depth Point Cloud Visualization](image/multi_camera_synced1.png)
### Checking camera port with OrbbecSDK_ROS2
```bash
ros2 run orbbec_camera list_devices_node
```
### OrbbecSDK_ROS2 multi-camera synced configuration
Open multi_camera_synced.launch.py, and configure the camera settings as shown below:
![Depth Point Cloud Visualization](image/multi_camera_synced2.png)
1. `gemini_330_series.launch.py` is the launch file for starting the camera.
2. Set `camera_name` to `G330_0`. For example, the published color image topic will be `/G330_0/color/image_raw`.
3. Set `usb_port` to `2-2`, indicating that the camera device on port `2-2` is being used. This value can be found in the output of the `ros2 run orbbec_camera list_devices_node` command.
4. Set `device_num` to `2`, meaning two cameras will be used.
5. Set `sync_mode` to `primary` to indicate that the `2-7` camera device is in primary mode. The multi-camera sync mode options can be found in the figure below.
6. Parameters from the `config_file_path` can override the parameters set in `gemini_330_series.launch.py` (optional).
7. For slave cameras, set `trigger_out_enabled` to false.
| **Pattern Nam**e | **Setting effect description** |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| free_run | -Support different frame rate settings<br />The -8-pin synchronization interface does not support external output of synchronization-related signals |
| standalone(default) | ● Same as Primary by default<br />● Built-in RGBD frame synchronization<br />● 8-pin synchronous interface does not output signals to the outside by default |
| primary | ● Set as primary camera<br />● 8-pin synchronous interface output signal to external device |
| secondary | ● Set as secondary (passive synchronization; When there is a hardware continuous trigger signal input from the outside and the continuous trigger signal matches the currently set frame rate, the image is collected according to the external trigger signal; When there is no external trigger signal, the flow is stopped)<br />● 8-pin synchronous interface output signal to external device |
| secondary_synced | ● Set to secondary synchronization (passive synchronization; When there is a hardware continuous trigger signal input from the outside and the continuous trigger signal matches the currently set frame rate, the image is collected according to the external trigger signal; When there is no external trigger signal, the image is collected according to the internal trigger signal at the set frame rate)<br />● 8-pin synchronous interface output signal to external device |
| hardware_triggering | ● Set as hardware trigger (passive trigger; When there is a hardware trigger signal input from the outside and the trigger signal time interval is not less than the current upper limit, the image is collected according to the external trigger signal; When there is no external trigger signal, the image is not collected)<br />● 8-pin synchronous interface output signal to external device |
| software_triggering | ● Set as software trigger (passive trigger; When there is a trigger command input from the host computer and the trigger command time interval is not less than the current upper limit, the image is collected according to the trigger command; When there is no trigger command, the image is not collected)<br />● 8-pin synchronous interface output signal to external device |
* The master camera should be launched last.
* Ideally, there should be a 2-second delay between starting each camera.
### Run the following command to start the multi-camera synced
```bash
ros2 launch orbbec_camera multi_camera_synced.launch.py
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

@@ -1,159 +0,0 @@
# Multi_camera_synced_verification_tool
> This article will introduce how to verify the synchronization accuracy of multi-camera synchronization.
>
> First, please see how to use [multi_camera_synced](../multi_camera_synced/README.MD).
## Directory Structure
```plaintext
.
├── multicamera_sync/
│ ├── output/
│ ├──20250218102900/
│ ├──DevicesInfo.txt
│ ├──StreamProfileInfo.txt
│ ├── Python/
│ ├──Config.ini
├── gemini_330_series_synced_verify.launch.py
├── multi_camera_synced_verify.launch.py
```
### multicamera_sync
* `gemini_330_series_synced_verify.launch.py` : Single camera runs launch, which provides the camera running node for multi_camera_synced_verify.launch.py.
* `multi_camera_synced_verify.launch.py` : Multi camer synced + launch of save_rgbir tool.
#### output
> The `ouput` folder is the folder where the camera pictures are output
In the output example provided
* `20250218102900` : Represents the camera image information collected at 10:29:00 on February 18, 2025.
* `TotalModeFrames` : Camera image information storage directory.
* `results-2025-02-25_163648` : The result after analyzing and matching the image information of TotalModeFrames.
* `DevicesInfo.txt` : Camera equipment basic information (need to be modified).
* `StreamProfileInfo.txt` : Camera video stream information (no need to modify).
#### Python
* `Config.ini` : Configuration file for Python analysis script (need to be modified).
## Preparation for operation
### save_rgbir node
Edit multi_camera_synced_verify.launch.py and fill in the activated camera device,we can find that save_rgbir is started at the end.
> IMPORTANT
>
> If you are using ROS 2 Foxy, you need to launch the save_rgbir node first.
```python
# Launch description
ld = LaunchDescription(
[
shared_orbbec_container,
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
# The primary camera should be launched at last
TimerAction(period=6.0, actions=[GroupAction([save_rgbir])]),
# save_rgbir is synced verification tool
]
)
return ld
```
save_rgbir is a tool for saving images. The configuration file of this tool is in [multi_save_rgbir_params.json](../../config/tools/multisavergbir/multi_save_rgbir_params.json).
```json
{
"save_rgbir_params": {
"time_domain": "global",
"usb_ports": [
"2-1",
"2-3"
],
"camera_name": [
"camera_01",
"camera_02"
]
}
}
```
* `time_domain` : Timestamp Type
* `usb_ports` : "primary", "secondary 1", "secondary 2", "secondary 3", fill in as many usb_ports as there are cameras
* `camera_name` : The name of the camera setting, for example: camera_01
### DevicesInfo.txt
Edit DevicesInfo.txt. Only the `primarySerialNumber`, `index` and `serialNumber` parameters need to be changed. Other parameters do not need to be changed.Refer to the example of [20250218102900](./multicamera_sync/output/20250218102900).
* `primarySerialNumber` : The SN serial number of the primary camera
* `index` : Camera index
* `serialNumber` : The SN serial number of the camera
### Config.ini
Edit Config.ini.Modify `frameRate` and `tspRangeThreshold`.
## Run this example
### Run launch and save camera pictures
* First terminal
```bash
ros2 launch orbbec_camera multi_camera_synced_verify.launch.py
```
* Second terminal
```bash
ros2 service call /save_rgbir/start_capture orbbec_camera_msgs/srv/SetInt32 '{data: 100}'
```
When the terminal displays "over", the image is saved.A new multicamera_sync folder will be generated under the workspace.
#### Camera pictures naming format
Take [color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg](./multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg) as an example
* `color` : This image is a snapshot of the color stream.
* `SNCP1E5420006D` : The camera's SN serial number is CP1E5420006D.
* `Index0` : Camera 0 (usually referred to primary camera).
* `g1739874543227` : “g” represents the global timestamp, which means the global timestamp of this frame is 1739874543227.
* `f0` : The 0th picture of the color stream acquisition of this camera.
* `s1739874543327` : "s" represents the timestamp of the current system, which means the current system timestamp of this frame is 1739874543327.
* `e50` : The exposure of this frame is 50.
* `d16` : The gain of this frame is 16.
### Analyzing camera image data
You need to copy the modified [Python folder](./multicamera_sync/Python) to the new multi_camera_synced subdirectory, and copy the modified [DevicesInfo.txt ](./multicamera_sync/output/20250218102900/DevicesInfo.txt)and [StreamProfileInfo.txt](./multicamera_sync/output/20250218102900/StreamProfileInfo.txt) to the same level directory as the TotalModeFrames folder.
* Finally everything is ready, run the python script
```bash
cd multicamera_sync/Python
python3 SyncFramesMain.py
```
After the operation is successful, you can view the synchronization effect in the `results folder`
## Files that need to be changed
### Analysis tools
* [multi_save_rgbir_params.json](../../config/tools/multisavergbir/multi_save_rgbir_params.json)
* [DevicesInfo.txt ](./multicamera_sync/output/20250218102900/DevicesInfo.txt)
* [Config.ini](./multicamera_sync/Python/Config.ini)
### Camera Configuration
[camera_params.yaml](../../config/camera_params.yaml)(Camera startup parameter settings)
[multi_camera_synced_verify.launch.py](./multi_camera_synced_verify.launch.py)
@@ -1,318 +0,0 @@
import os
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
from launch.conditions import UnlessCondition
from launch_ros.actions import LoadComposableNodes
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
args = [
DeclareLaunchArgument('camera_name', default_value='camera'),
DeclareLaunchArgument('depth_registration', default_value='true'),
DeclareLaunchArgument('serial_number', default_value=''),
DeclareLaunchArgument('usb_port', default_value=''),
DeclareLaunchArgument('device_num', default_value='1'),
DeclareLaunchArgument('upgrade_firmware', default_value=''),
DeclareLaunchArgument('preset_firmware_path', default_value=''),
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('cloud_frame_id', default_value=''),
DeclareLaunchArgument('connection_delay', default_value='10'),
DeclareLaunchArgument('color_width', default_value='0'),
DeclareLaunchArgument('color_height', default_value='0'),
DeclareLaunchArgument('color_fps', default_value='0'),
DeclareLaunchArgument('color_format', default_value='ANY'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('color_qos', default_value='default'),
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('color_flip', default_value='false'),
DeclareLaunchArgument('color_mirror', default_value='false'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('color_exposure', default_value='-1'),
DeclareLaunchArgument('color_gain', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('color_brightness', default_value='-1'),
DeclareLaunchArgument('color_sharpness', default_value='-1'),
DeclareLaunchArgument('color_gamma', default_value='-1'),
DeclareLaunchArgument('color_saturation', default_value='-1'),
DeclareLaunchArgument('color_constrast', default_value='-1'),
DeclareLaunchArgument('color_hue', default_value='-1'),
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='0'),
DeclareLaunchArgument('depth_height', default_value='0'),
DeclareLaunchArgument('depth_fps', default_value='0'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('depth_qos', default_value='default'),
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('depth_flip', default_value='false'),
DeclareLaunchArgument('depth_mirror', default_value='false'),
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('depth_brightness', default_value='-1'),
DeclareLaunchArgument('left_ir_width', default_value='0'),
DeclareLaunchArgument('left_ir_height', default_value='0'),
DeclareLaunchArgument('left_ir_fps', default_value='0'),
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_left_ir', default_value='false'),
DeclareLaunchArgument('left_ir_qos', default_value='default'),
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('left_ir_flip', default_value='false'),
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('right_ir_width', default_value='0'),
DeclareLaunchArgument('right_ir_height', default_value='0'),
DeclareLaunchArgument('right_ir_fps', default_value='0'),
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_right_ir', default_value='false'),
DeclareLaunchArgument('right_ir_qos', default_value='default'),
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
DeclareLaunchArgument('right_ir_flip', default_value='false'),
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
DeclareLaunchArgument('ir_exposure', default_value='-1'),
DeclareLaunchArgument('ir_gain', default_value='-1'),
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('ir_brightness', default_value='-1'),
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
DeclareLaunchArgument('enable_accel', default_value='false'),
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
DeclareLaunchArgument('accel_rate', default_value='200hz'),
DeclareLaunchArgument('accel_range', default_value='4g'),
DeclareLaunchArgument('enable_gyro', default_value='false'),
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
DeclareLaunchArgument('publish_tf', default_value='true'),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
DeclareLaunchArgument('ir_info_url', default_value=''),
DeclareLaunchArgument('color_info_url', default_value=''),
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
# If you do not want to automatically enumerate network devices,
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
DeclareLaunchArgument('net_device_ip', default_value=''),
DeclareLaunchArgument('net_device_port', default_value='0'),
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
DeclareLaunchArgument('log_level', default_value='none'),
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
DeclareLaunchArgument('enable_ldp', default_value='true'),
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
DeclareLaunchArgument('sync_mode', default_value='standalone'),
DeclareLaunchArgument('depth_delay_us', default_value='0'),
DeclareLaunchArgument('color_delay_us', default_value='0'),
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
DeclareLaunchArgument('ordered_pc', default_value='false'),
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
DeclareLaunchArgument('align_mode', default_value='SW'),
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
DeclareLaunchArgument('enable_laser', default_value='true'),
DeclareLaunchArgument('depth_precision', default_value=''),
DeclareLaunchArgument('device_preset', default_value='Default'),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
DeclareLaunchArgument('config_file_path', default_value=''),
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
DeclareLaunchArgument('offset_index0', default_value='-1'),
DeclareLaunchArgument('offset_index1', default_value='-1'),
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
]
def get_params(context, args):
return [load_parameters(context, args)]
def create_node_action(context, args):
params = get_params(context, args)
ros_distro = os.environ.get("ROS_DISTRO", "humble")
if ros_distro == "foxy":
return [
Node(
package="orbbec_camera",
executable="orbbec_camera_node",
name="ob_camera_node",
namespace=LaunchConfiguration("camera_name"),
parameters=params,
output="screen",
)
]
else:
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
orbbec_container = Node(
name=component_container_name_arg,
package='rclcpp_components',
executable='component_container_mt',
output='screen',
condition=UnlessCondition(attach_to_shared_component_container_arg)
)
return [
orbbec_container,
LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace=LaunchConfiguration("camera_name"),
name=LaunchConfiguration("camera_name"),
package='orbbec_camera',
plugin='orbbec_camera::OBCameraNodeDriver',
parameters=params,
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
)
]
)
]
return LaunchDescription(
args + [
OpaqueFunction(function=lambda context: create_node_action(context, args))
]
)
@@ -1,207 +0,0 @@
import os
import yaml
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
from launch.conditions import UnlessCondition
from launch_ros.actions import LoadComposableNodes
def load_yaml(file_path):
with open(file_path, 'r') as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == 'true':
return True
elif value.lower() == 'false':
return False
return value
def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
config_file_path = LaunchConfiguration('config_file_path').perform(context)
if config_file_path:
yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
return {
key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items()
}
def generate_launch_description():
args = [
DeclareLaunchArgument('camera_name', default_value='camera'),
DeclareLaunchArgument('depth_registration', default_value='true'),
DeclareLaunchArgument('serial_number', default_value=''),
DeclareLaunchArgument('usb_port', default_value=''),
DeclareLaunchArgument('device_num', default_value='1'),
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
DeclareLaunchArgument('product_id', default_value=''),
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
DeclareLaunchArgument('cloud_frame_id', default_value=''),
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
DeclareLaunchArgument('connection_delay', default_value='100'),
DeclareLaunchArgument('color_width', default_value='0'),
DeclareLaunchArgument('color_height', default_value='0'),
DeclareLaunchArgument('color_fps', default_value='0'),
DeclareLaunchArgument('color_format', default_value='ANY'),
DeclareLaunchArgument('enable_color', default_value='true'),
DeclareLaunchArgument('color_flip', default_value='false'),
DeclareLaunchArgument('color_qos', default_value='default'),
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('color_exposure', default_value='-1'),
DeclareLaunchArgument('color_gain', default_value='-1'),
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
DeclareLaunchArgument('color_white_balance', default_value='-1'),
DeclareLaunchArgument('color_brightness', default_value='-1'),
DeclareLaunchArgument('depth_width', default_value='0'),
DeclareLaunchArgument('depth_height', default_value='0'),
DeclareLaunchArgument('depth_fps', default_value='0'),
DeclareLaunchArgument('depth_format', default_value='ANY'),
DeclareLaunchArgument('enable_depth', default_value='true'),
DeclareLaunchArgument('depth_flip', default_value='false'),
DeclareLaunchArgument('depth_qos', default_value='default'),
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
DeclareLaunchArgument('ir_width', default_value='0'),
DeclareLaunchArgument('ir_height', default_value='0'),
DeclareLaunchArgument('ir_fps', default_value='0'),
DeclareLaunchArgument('ir_format', default_value='ANY'),
DeclareLaunchArgument('enable_ir', default_value='true'),
DeclareLaunchArgument('flip_ir', default_value='false'),
DeclareLaunchArgument('ir_qos', default_value='default'),
DeclareLaunchArgument('ir_camera_info_qos', default_value='default'),
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
DeclareLaunchArgument('ir_exposure', default_value='-1'),
DeclareLaunchArgument('ir_gain', default_value='-1'),
DeclareLaunchArgument('ir_brightness', default_value='-1'),
DeclareLaunchArgument('config_file_path', default_value=''),
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='true'),
DeclareLaunchArgument('enable_accel', default_value='false'),
DeclareLaunchArgument('accel_rate', default_value='100hz'),
DeclareLaunchArgument('accel_range', default_value='4g'),
DeclareLaunchArgument('enable_gyro', default_value='false'),
DeclareLaunchArgument('gyro_rate', default_value='100hz'),
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
DeclareLaunchArgument('publish_tf', default_value='true'),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
DeclareLaunchArgument('ir_info_url', default_value=''),
DeclareLaunchArgument('color_info_url', default_value=''),
DeclareLaunchArgument('log_level', default_value='none'),
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
DeclareLaunchArgument('enable_ldp', default_value='true'),
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
# Configure the path for depth filter file, for example: /config/depthfilter/Gemini2_v1.7.json
DeclareLaunchArgument('depth_filter_config', default_value=''),
# Depth work mode support is as follows:
# Unbinned Dense Default
# Unbinned Sparse Default
# Binned Sparse Default
# Obstacle Avoidance
DeclareLaunchArgument('depth_work_mode', default_value=''),
DeclareLaunchArgument('sync_mode', default_value='standalone'),
DeclareLaunchArgument('depth_delay_us', default_value='0'),
DeclareLaunchArgument('color_delay_us', default_value='0'),
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
DeclareLaunchArgument('trigger_out_enabled', default_value='false'),
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
DeclareLaunchArgument('ordered_pc', default_value='false'),
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
DeclareLaunchArgument('align_mode', default_value='SW'),
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
DeclareLaunchArgument('time_domain', default_value='global'),
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
]
def get_params(context, args):
return [load_parameters(context, args)]
def create_node_action(context, args):
params = get_params(context, args)
ros_distro = os.environ.get("ROS_DISTRO", "humble")
if ros_distro == "foxy":
return [
Node(
package="orbbec_camera",
executable="orbbec_camera_node",
name="ob_camera_node",
namespace=LaunchConfiguration("camera_name"),
parameters=params,
output="screen",
)
]
else:
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
orbbec_container = Node(
name=component_container_name_arg,
package='rclcpp_components',
executable='component_container_mt',
output='screen',
condition=UnlessCondition(attach_to_shared_component_container_arg)
)
return [
orbbec_container,
LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace=LaunchConfiguration("camera_name"),
name=LaunchConfiguration("camera_name"),
package='orbbec_camera',
plugin='orbbec_camera::OBCameraNodeDriver',
parameters=params,
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
)
]
)
]
return LaunchDescription(
args + [
OpaqueFunction(function=lambda context: create_node_action(context, args))
]
)
@@ -1,126 +0,0 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node, LoadComposableNodes
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.actions import DeclareLaunchArgument
from launch.conditions import UnlessCondition, IfCondition
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.descriptions import ComposableNode
def load_yaml(file_path):
with open(file_path, "r") as f:
return yaml.safe_load(f)
def merge_params(default_params, yaml_params):
for key, value in yaml_params.items():
if key in default_params:
default_params[key] = value
return default_params
def convert_value(value):
if isinstance(value, str):
try:
return int(value)
except ValueError:
pass
try:
return float(value)
except ValueError:
pass
if value.lower() == "true":
return True
elif value.lower() == "false":
return False
return value
def generate_launch_description():
# Include launch files
package_dir = get_package_share_directory("orbbec_camera")
launch_file_dir = os.path.join(
package_dir, "examples/multi_camera_synced_verification_tool"
)
config_file_dir = os.path.join(package_dir, "config")
config_file_path = os.path.join(config_file_dir, "camera_params.yaml")
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
attach_to_shared_component_container_arg = LaunchConfiguration(
"attach_to_shared_component_container", default=False
)
component_container_name_arg = LaunchConfiguration(
"component_container_name", default="shared_orbbec_container"
)
shared_orbbec_container = Node(
name=component_container_name_arg,
package="rclcpp_components",
executable="component_container_mt",
output="screen",
condition=UnlessCondition(attach_to_shared_component_container_arg),
)
save_rgbir = LoadComposableNodes(
target_container=component_container_name_arg,
composable_node_descriptions=[
ComposableNode(
namespace="save_rgbir",
name="save_rgbir",
package="orbbec_camera",
plugin="orbbec_camera::tools::MultiCameraSubscriber",
)
],
)
attach_to_shared_component_container_arg = TextSubstitution(text="true")
launch1_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_synced_verify.launch.py")
),
launch_arguments={
"camera_name": "camera_01",
"usb_port": "2-1",
"device_num": "2",
"sync_mode": "primary",
"config_file_path": config_file_path,
"trigger_out_enabled": "true",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
launch2_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, "gemini_330_series_synced_verify.launch.py")
),
launch_arguments={
"camera_name": "camera_02",
"usb_port": "2-3",
"device_num": "2",
"sync_mode": "secondary_synced",
"config_file_path": secondary_config_file_path,
"trigger_out_enabled": "false",
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
"component_container_name": component_container_name_arg,
}.items(),
)
# Launch description
ld = LaunchDescription(
[
shared_orbbec_container,
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
# The primary camera should be launched at last
TimerAction(period=6.0, actions=[GroupAction([save_rgbir])]),
# save_rgbir is synced verification tool
]
)
return ld
@@ -1,6 +0,0 @@
## config_frameMatch.py
Definition of 3 folders:
matchFrames: data with card control within half frame.
notMatchFrames: data with card control not within half frame, not matched.
abnormal: data within the group that exceeds tspRangeThreshold setting
@@ -1,5 +0,0 @@
[Parameter]
# Unit: FPS, please fill in according to the actual frame rate of the dataset, otherwise it will lead to inaccurate matching
frameRate=30
# Unit: ms. When the timestamp range of a group of data frames after matching is greater than or equal to tspRangeThreshold, the file name will be marked
tspRangeThreshold=4
@@ -1,97 +0,0 @@
import os
import subprocess
import json
import platform
def list_subdirectories(path):
# 遍历指定目录
for f in os.listdir(path):
# 判断是否是子目录
if os.path.isdir(os.path.join(path, f)):
yield f
def read_pid_from_device_json(frames_dir):
file_path = frames_dir + "/DevicesInfo.txt"
if not os.path.exists(file_path):
print(file_path + " not exists. Please check it")
return ""
with open(file_path, 'r') as f:
data = json.load(f)
if not 'devicePid' in data:
print("Not found 'devicePid' in " + file_path)
return ""
return data['devicePid']
def execute_sync_width_pid(frames_dir, pid):
sync_script_file=""
if pid.lower() == "0x0675":
sync_script_file="script/config_frameMatch_Gemini2VL.py"
elif pid.lower() == "0x0670" or pid.lower() == "0x0701":
sync_script_file="script/config_frameMatch-systemTimestamp.py"
elif pid.lower() == "0x0660":
sync_script_file="script/config_frameMatch-Astra2_new.py"
elif pid.lower() == "0x0803" or pid.lower() == "0x0807" or pid.lower() == "0x0801" or pid.lower() == "0x0805" or pid.lower() == "0x080b" or pid.lower() == "0x080e":
sync_script_file="script/config_frameMatch_g330.py"
else:
sync_script_file="script/config_frameMatch.py"
if len(sync_script_file) > 0:
print(f"execute synchronize frames. script_file={sync_script_file}, frames_dir={frames_dir}")
if platform.system() == "Windows":
output = subprocess.check_output(["python", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
print(output.decode("gbk"))
else:
output = subprocess.check_output(["python3", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
print(output.decode("utf-8"))
else:
print("Invalid pid=" + pid + ", not match sync frame script")
def sync_frames(frames_dir):
pid = read_pid_from_device_json(frames_dir)
if len(pid) <= 0:
print("Get pid failed.")
return
execute_sync_width_pid(frames_dir, pid)
def main():
# 指定目录路径
frames_output_path = os.path.abspath("../output")
# 获取所有子目录
subdirectories = list(list_subdirectories(frames_output_path))
if not subdirectories:
print("No subdirectories found in the specified directory.")
# 打印子目录列表并让用户选择
for i, subdir in enumerate(subdirectories):
print(f"{i + 1}. {subdir}")
# 获取用户输入
choice = input("Please select a subdirectory by number (or 'q' to quit): ")
if choice.lower() == 'q':
return
# 验证用户输入
is_index_valid = False
try:
index = int(choice) - 1
if 0 <= index < len(subdirectories):
is_index_valid = True
else:
print(f"Invalid choice. index={index}. Please enter a number between 1 and the total number of subdirectories.")
except ValueError:
print("Invalid choice(ValueException). Please enter a number between 1 and the total number of subdirectories")
if is_index_valid:
selected_subdir = subdirectories[index]
print(f"You selected the subdirectory: {selected_subdir}")
sync_frames(f"{frames_output_path }/{selected_subdir}")
if __name__ == "__main__":
main()
@@ -1,385 +0,0 @@
import os.path
import argparse
import sys
import json
import shutil
import configparser
from pathlib import Path
from datetime import datetime
## Config.ini中配置
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
frameRate = -1
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
tspRangeThreshold = -1
## Python脚本自动解析, 不要修改
primaryId = '-1' #主机ID
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
sourceRootPath = "" # 存放TotalMode的根目录
resultPath = "" # 保存sync分析结果
streamProfileDict = dict() # 保存Python结果
class PictureInfo(object):
class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId
self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath
self.deviceIdFull = deviceIdFull
self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType
self.width = width
self.height = height
self.format = format
self.fps = fps
def initPrimaryId():
global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False
for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index'])
return True
return False
def initSyncConfigParams():
global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False
# 创建ConfigParser对象
config = configparser.ConfigParser()
# 读取ini文件
config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False
if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False
frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False
return True
def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists")
return 0
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0
return len(data['devices'])
def initProfileInfoDict():
global streamProfileDict
streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False
with open(profilePath, 'r') as f:
data = json.load(f)
if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False
for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo)
else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0
def initResultDir():
global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath):
os.makedirs(resultPath)
def isFrameFile(fileName):
fileExt = os.path.splitext(fileName)
if len(fileExt) < 2:
return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames:
filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName):
continue
frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:])
# 创建结构体
pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# 格式化字典一个key对应一个数组
pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict):
global primaryId, frameRate
global streamProfileDict
# 获取比较图像数组,取第一个设备的Color数组来进行比较
compareList = []
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info)
sorted_dict = dict(sorted(pictureInfoDict.items(), key=lambda x: x[1], reverse=False))
pictureInfoDict = sorted_dict
# 根据开流情况动态分析,减少循环次数
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
# 匹配相邻帧
resultDict = {}
consumedList = []
dictIndex = 0
frameTspGap = int(1000.0/frameRate+0.5)
for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
# 对比彩色
if hasColorProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'color' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对Depth
if hasDepthProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'depth' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# IR
if hasIRProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 计算偏差
for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - comparePic.syncTimeStamp
dictIndex += 1
return resultDict
def safe_make_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def handleSyncFrames():
global resultPath
global sourceFramesPath
global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit")
return
print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return
# # Dump pictureInfoDict
# for key in pictureInfoDict:
# print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key])
# for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount)
resultDict = {}
resultDict = matchFrame(pictureInfoDict)
for key in resultDict:
fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key)
listTmp = list(resultDict[key])
for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict:
listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
haveAbnormalData = True
else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
if haveAbnormalData:
safe_make_dir(abnormalPath)
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
else:
safe_make_dir(notMatchPath)
for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath)
def main():
if not initPrimaryId():
print("Initialize primaryId failed. exit.")
return
if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.")
return
initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames()
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Astra2'")
parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args()
sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Astra2 synchronize frame. sourceRootPath={sourceRootPath}")
main()
print("Finish of Astra2 synchronize frame.")
@@ -1,408 +0,0 @@
import os.path
import argparse
import sys
import json
import shutil
import configparser
from pathlib import Path
from datetime import datetime
## Config.ini中配置
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
frameRate = -1
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
tspRangeThreshold = -1
## Python脚本自动解析, 不要修改
primaryId = '-1' #主机ID
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
sourceRootPath = "" # 存放TotalMode的根目录
resultPath = "" # 保存sync分析结果
streamProfileDict = dict() # 保存Python结果
class PictureInfo(object):
class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId
self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath
self.deviceIdFull = deviceIdFull
self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType
self.width = width
self.height = height
self.format = format
self.fps = fps
def initPrimaryId():
global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False
for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index'])
return True
return False
def initSyncConfigParams():
global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False
# 创建ConfigParser对象
config = configparser.ConfigParser()
# 读取ini文件
config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False
if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False
frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False
return True
def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists")
return 0
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0
return len(data['devices'])
def initProfileInfoDict():
global streamProfileDict
streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False
with open(profilePath, 'r') as f:
data = json.load(f)
if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False
for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo)
else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0
def initResultDir():
global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath):
os.makedirs(resultPath)
def isFrameFile(fileName):
fileExt = os.path.splitext(fileName)
if len(fileExt) < 2:
return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames:
filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName):
continue
frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[5][1:])
# 创建结构体
pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# 格式化字典一个key对应一个数组
pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict):
global primaryId, frameRate
global streamProfileDict
# 获取比较图像数组,取第一个设备的Color数组来进行比较
compareList = []
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info)
# 根据开流情况动态分析,减少循环次数
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# 匹配相邻帧
resultDict = {}
consumedList = []
dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
# 对比彩色
if hasColorProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对Depth
if hasDepthProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# IR
if hasIRProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 左IR
if hasIRLeftProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 右IR
if hasIRRightProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 计算偏差
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1
return resultDict
def safe_make_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def handleSyncFrames():
global resultPath
global sourceFramesPath
global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit")
return
print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return
# # Dump pictureInfoDict
# for key in pictureInfoDict:
# print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key])
# for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount)
resultDict = {}
resultDict = matchFrame(pictureInfoDict)
for key in resultDict:
fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key)
listTmp = list(resultDict[key])
for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict:
listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
haveAbnormalData = True
else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
if haveAbnormalData:
safe_make_dir(abnormalPath)
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
else:
safe_make_dir(notMatchPath)
for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath)
def main():
if not initPrimaryId():
print("Initialize primaryId failed. exit.")
return
if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.")
return
initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames()
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2'")
parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args()
sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Gemini2 synchronize frame. sourceRootPath={sourceRootPath}")
main()
print("Finish of Gemini2 synchronize frame.")
@@ -1,430 +0,0 @@
import os.path
import argparse
import sys
import json
import shutil
import configparser
from pathlib import Path
from datetime import datetime
import os
import re
## Config.ini中配置
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
frameRate = -1
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
tspRangeThreshold = -1
## Python脚本自动解析, 不要修改
primaryId = '-1' #主机ID
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
sourceRootPath = "" # 存放TotalMode的根目录
resultPath = "" # 保存sync分析结果
streamProfileDict = dict() # 保存Python结果
class PictureInfo(object):
class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId
self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath
self.deviceIdFull = deviceIdFull
self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType
self.width = width
self.height = height
self.format = format
self.fps = fps
def initPrimaryId():
global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False
for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index'])
return True
return False
def initSyncConfigParams():
global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False
# 创建ConfigParser对象
config = configparser.ConfigParser()
# 读取ini文件
config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False
if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False
frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False
return True
def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists")
return 0
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0
return len(data['devices'])
def initProfileInfoDict():
global streamProfileDict
streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False
with open(profilePath, 'r') as f:
data = json.load(f)
if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False
for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo)
else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0
def initResultDir():
global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath):
os.makedirs(resultPath)
def isFrameFile(fileName):
fileExt = os.path.splitext(fileName)
if len(fileExt) < 2:
return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts
def extract_d_number(filename):
"""提取文件名中以'd'开头的数字部分"""
match_d = re.search(r'd(\d+)', filename)
if match_d:
return int(match_d.group(1))
match_g = re.search(r'g(\d+)', filename)
if match_g:
return int(match_g.group(1))
return float('inf')
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath):
filenames.sort(key=extract_d_number) # 排序,按文件名中的'd'后面的数字从小到大排序
for fileName in filenames:
filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName):
continue
frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:])
# 创建结构体
pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# 格式化字典一个key对应一个数组
pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict):
global primaryId, frameRate
global streamProfileDict
# 获取比较图像数组,取第一个设备的Color数组来进行比较
compareList = []
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'ir_left':
compareList.append(info)
# 根据开流情况动态分析,减少循环次数
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# 匹配相邻帧
resultDict = {}
consumedList = []
dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
# 对比彩色
if hasColorProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对Depth
if hasDepthProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
print("=====================result value : jjjjjjjjjjjjjjj:%s" % (info.syncTimeStamp))
break
# IR
if hasIRProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对左IR
if hasIRLeftProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对右IR
if hasIRRightProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 计算偏差
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1
return resultDict
def safe_make_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def handleSyncFrames():
global resultPath
global sourceFramesPath
global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit")
return
print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return
# # Dump pictureInfoDict
# for key in pictureInfoDict:
# print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key])
# for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount)
resultDict = {}
resultDict = matchFrame(pictureInfoDict)
for key in resultDict:
fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key)
listTmp = list(resultDict[key])
for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict:
listTmp = list(resultDict[key])
# print("***********")
# print(listTmp)
if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
haveAbnormalData = True
else:
# print("***********")
# print(info.picturePath)
# print("###########")
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
# print(path)
if haveAbnormalData:
safe_make_dir(abnormalPath)
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
else:
safe_make_dir(notMatchPath)
for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath)
def main():
if not initPrimaryId():
print("Initialize primaryId failed. exit.")
return
if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.")
return
initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames()
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args()
sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
main()
print("Finish of CommonFrameMatch synchronize frame.")
@@ -1,408 +0,0 @@
import os.path
import argparse
import sys
import json
import shutil
import configparser
from pathlib import Path
from datetime import datetime
## Config.ini中配置
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
frameRate = -1
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
tspRangeThreshold = -1
## Python脚本自动解析, 不要修改
primaryId = '-1' #主机ID
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
sourceRootPath = "" # 存放TotalMode的根目录
resultPath = "" # 保存sync分析结果
streamProfileDict = dict() # 保存Python结果
class PictureInfo(object):
class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
self.deviceId = deviceId
self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath
self.deviceIdFull = deviceIdFull
self.fileExt = fileExt
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType
self.width = width
self.height = height
self.format = format
self.fps = fps
def initPrimaryId():
global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False
for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index'])
return True
return False
def initSyncConfigParams():
global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False
# 创建ConfigParser对象
config = configparser.ConfigParser()
# 读取ini文件
config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False
if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False
frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False
return True
def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists")
return 0
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0
return len(data['devices'])
def initProfileInfoDict():
global streamProfileDict
streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False
with open(profilePath, 'r') as f:
data = json.load(f)
if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False
for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo)
else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0
def initResultDir():
global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath):
os.makedirs(resultPath)
def isFrameFile(fileName):
fileExt = os.path.splitext(fileName)
if len(fileExt) < 2:
return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames:
filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName):
continue
frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:])
# 创建结构体
pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
# 格式化字典一个key对应一个数组
pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict):
global primaryId, frameRate
global streamProfileDict
# 获取比较图像数组,取第一个设备的Color数组来进行比较
compareList = []
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info)
# 根据开流情况动态分析,减少循环次数
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# 匹配相邻帧
resultDict = {}
consumedList = []
dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
# 对比彩色
if hasColorProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对Depth
if hasDepthProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# IR
if hasIRProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对左IR
if hasIRLeftProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对右IR
if hasIRRightProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 计算偏差
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1
return resultDict
def safe_make_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def handleSyncFrames():
global resultPath
global sourceFramesPath
global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit")
return
print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return
# # Dump pictureInfoDict
# for key in pictureInfoDict:
# print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key])
# for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount)
resultDict = {}
resultDict = matchFrame(pictureInfoDict)
for key in resultDict:
fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key)
listTmp = list(resultDict[key])
for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict:
listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
haveAbnormalData = True
else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
if haveAbnormalData:
safe_make_dir(abnormalPath)
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
shutil.copy(info.picturePath, path)
else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, path)
else:
safe_make_dir(notMatchPath)
for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
shutil.copy(info.picturePath, newFilePath)
def main():
if not initPrimaryId():
print("Initialize primaryId failed. exit.")
return
if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.")
return
initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames()
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2 VL'")
parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args()
sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir
print(f"Start of Gemini2VL synchronize frame. sourceRootPath={sourceRootPath}")
main()
print("Finish of Gemini2VL synchronize frame.")
@@ -1,412 +0,0 @@
import os.path
import argparse
import sys
import json
import shutil
import configparser
from pathlib import Path
from datetime import datetime
## Config.ini中配置
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
frameRate = -1
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
tspRangeThreshold = -1
## Python脚本自动解析, 不要修改
primaryId = '-1' #主机ID
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
sourceRootPath = "" # 存放TotalMode的根目录
resultPath = "" # 保存sync分析结果
streamProfileDict = dict() # 保存Python结果
class PictureInfo(object):
class Struct(object):
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
self.deviceId = deviceId
self.sensorType = sensorType
self.syncTimeStamp = syncTimeStamp
self.picturePath = picturePath
self.deviceIdFull = deviceIdFull
self.fileExt = fileExt
self.exp = exp
self.gain = gain
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain)
class StreamProfileInfo:
def __init__(self, sensorType, width, height, format, fps):
self.sensorType = sensorType
self.width = width
self.height = height
self.format = format
self.fps = fps
def initPrimaryId():
global primaryId
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
return False
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
return False
for item in data['devices']:
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
primaryId = str(item['index'])
return True
return False
def initSyncConfigParams():
global frameRate, tspRangeThreshold
configPath = f"{mainPythonWorkPath}/Config.ini"
if not os.path.exists(configPath):
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
return False
# 创建ConfigParser对象
config = configparser.ConfigParser()
# 读取ini文件
config.read(configPath, 'utf-8')
if not config.has_option('Parameter', 'frameRate'):
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
return False
if not config.has_option('Parameter', 'tspRangeThreshold'):
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
return False
frameRate = config.getfloat('Parameter', 'frameRate')
if frameRate <= 0 or frameRate >= 1000:
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
return False
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
if tspRangeThreshold <= 0:
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
return False
return True
def getDeviceCount():
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
if not os.path.exists(deviceInfoPath):
print(f"Get device count failed. {deviceInfoPath} not exists")
return 0
with open(deviceInfoPath, 'r') as f:
data = json.load(f)
if not 'devices' in data:
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
return 0
return len(data['devices'])
def initProfileInfoDict():
global streamProfileDict
streamProfileDict = dict()
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
if not os.path.exists(profilePath):
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
return False
with open(profilePath, 'r') as f:
data = json.load(f)
if not 'streamProfiles' in data:
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
return False
for item in data['streamProfiles']:
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
streamProfileDict.setdefault(item['sensorType'], profileInfo)
else:
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
return len(streamProfileDict.items()) > 0
def initResultDir():
global resultPath
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
resultPath = f"{sourceRootPath}/results-{timeText}"
if not os.path.exists(resultPath):
os.makedirs(resultPath)
def isFrameFile(fileName):
fileExt = os.path.splitext(fileName)
if len(fileExt) < 2:
return False
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
return fileExt[1] in frameExts
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
frameFileCount = 0
for dirpath, dirnames, filenames in os.walk(rootFilePath):
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
for fileName in filenames:
filePath = os.path.join(dirpath, fileName)
if not isFrameFile(fileName):
continue
frameFileCount += 1
fileNameNoExt = os.path.splitext(fileName)[0]
pictureNameList = fileNameNoExt.split('_')
deviceId = pictureNameList[2][5:]
sensorType = pictureNameList[0].replace('#', '_')
syncTimeStamp = int(pictureNameList[3][1:])
exp = str(pictureNameList[6][0:])
gain = str(pictureNameList[7][0:])
# 创建结构体
pictureInfo = PictureInfo()
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1],exp,gain)
# 格式化字典一个key对应一个数组
pictureInfoDict.setdefault(deviceId, []).append(info)
if 0 == frameFileCount:
print("initialize pictureInfoDict failed. Not found frame file")
def matchFrame(pictureInfoDict):
global primaryId, frameRate
global streamProfileDict
# 获取比较图像数组,取第一个设备的Color数组来进行比较
compareList = []
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == 'color':
compareList.append(info)
# 根据开流情况动态分析,减少循环次数
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
# 匹配相邻帧
resultDict = {}
consumedList = []
dictIndex = 0
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
for comparePic in compareList:
resultDict.setdefault(dictIndex, []).append(comparePic)
for key in pictureInfoDict:
listTmp = list(pictureInfoDict[key])
# 对比彩色
if hasColorProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对Depth
if hasDepthProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# IR
if hasIRProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对左IR
if hasIRLeftProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 对右IR
if hasIRRightProfile:
for info in listTmp:
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
continue
if info in consumedList:
continue
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
resultDict.setdefault(dictIndex, []).append(info)
consumedList.append(info)
break
# 计算偏差
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
for info in resultDict[dictIndex]:
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
dictIndex += 1
return resultDict
def safe_make_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def handleSyncFrames():
global resultPath
global sourceFramesPath
global streamProfileDict
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
if not initProfileInfoDict():
print("Initialize stream profile dictionary failed. exit")
return
print(f"Stream profiles: {streamProfileDict}")
streamProfileCount = len(streamProfileDict.items())
pictureInfoDict = {}
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
if len(pictureInfoDict) <= 0:
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
return
# # Dump pictureInfoDict
# for key in pictureInfoDict:
# print("===================key:%s" % key)
# listTmp = list(pictureInfoDict[key])
# for info in listTmp:
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
deviceCount = getDeviceCount()
print("=================device count:%d" % deviceCount)
resultDict = {}
resultDict = matchFrame(pictureInfoDict)
for key in resultDict:
fp.write("===================result key:%s\n" % key)
print("===================result key:%s" % key)
listTmp = list(resultDict[key])
for info in listTmp:
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
matchPath = f"{resultPath}/matchFrames/"
safe_make_dir(matchPath)
notMatchPath = f"{resultPath}/notMatchFrames/"
abnormalPath = f"{resultPath}/abnormal/"
for key in resultDict:
listTmp = list(resultDict[key])
if (len(listTmp) == (deviceCount * streamProfileCount)):
haveAbnormalData = False
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path)
haveAbnormalData = True
else:
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path)
if haveAbnormalData:
safe_make_dir(abnormalPath)
for info in listTmp:
if info.tspDiff >= tspRangeThreshold:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path)
else:
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, path)
else:
safe_make_dir(notMatchPath)
for info in listTmp:
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
shutil.copy(info.picturePath, newFilePath)
def main():
if not initPrimaryId():
print("Initialize primaryId failed. exit.")
return
if not initSyncConfigParams():
print("Initialized Sync config parameter failed. exit.")
return
initResultDir()
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
handleSyncFrames()
return
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
parser.add_argument('frames_dir', type=str, help="Frames directory")
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
args = parser.parse_args()
sourceRootPath = args.frames_dir
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
mainPythonWorkPath = args.main_script_root_dir
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
main()
print("Finish of CommonFrameMatch synchronize frame.")
@@ -1,42 +0,0 @@
{
"deviceVid": "0x2BC5",
"devicePid": "0x0800",
"primarySerialNumber": "CP1E5420006D",
"devices": [{
"index": 0,
"isPrimaryDevice": true,
"vid": "0x2BC5",
"pid": "0x0800",
"name": "Orbbec Gemini 335",
"firmwareVersion": "1.3.64",
"serialNumber": "CP1E5420006D",
"hardwareVersion": "0.1",
"ipAddress": "0.0.0.0",
"extensionInfo": {
"ExtensionInfo": {
"IspFwVer": "20240820",
"IspNeedVer": "20240820",
"HwType": "R1",
"HwVer": "V1.1"
}
}
}, {
"index": 1,
"isPrimaryDevice": false,
"vid": "0x2BC5",
"pid": "0x0800",
"name": "Orbbec Gemini 335",
"firmwareVersion": "1.3.64",
"serialNumber": "CP1L44P00051",
"hardwareVersion": "0.1",
"ipAddress": "0.0.0.0",
"extensionInfo": {
"ExtensionInfo": {
"IspFwVer": "20240820",
"IspNeedVer": "20240820",
"HwType": "R1",
"HwVer": "V1.0"
}
}
}]
}
@@ -1,16 +0,0 @@
{
"D2CAlignMode": "ALIGN_D2C_SW_MODE",
"streamProfiles": [{
"sensorType": "OB_SENSOR_COLOR",
"width": 1280,
"height": 720,
"fps": 30,
"format": "MJPEG"
}, {
"sensorType": "OB_SENSOR_IR_LEFT",
"width": 848,
"height": 480,
"fps": 30,
"format": "Y8"
}]
}
@@ -1,55 +0,0 @@
===================result key:0
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543527_f9_s1739874543627_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543527_f9_s1739874543616_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543527_f10_s1739874543687_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543527_f9_s1739874543603_e50_d16_.jpg
===================result key:1
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543494_f8_s1739874543528_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543494_f8_s1739874543540_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543494_f8_s1739874543687_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543494_f8_s1739874543548_e50_d16_.jpg
===================result key:2
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543461 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543461_f7_s1739874543493_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543460 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543460_f7_s1739874543505_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543460 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543460_f7_s1739874543685_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543461 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543461_f7_s1739874543520_e50_d16_.jpg
===================result key:3
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543260_f1_s1739874543310_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543260_f1_s1739874543345_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543260_f1_s1739874543294_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543260_f1_s1739874543351_e50_d16_.jpg
===================result key:4
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543361 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543361_f4_s1739874543405_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543360_f4_s1739874543468_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543360_f4_s1739874543460_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543360_f4_s1739874543442_e50_d16_.jpg
===================result key:5
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543327_f3_s1739874543393_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543327_f3_s1739874543458_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543327_f3_s1739874543460_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543327_f3_s1739874543441_e50_d16_.jpg
===================result key:6
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543561 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543561_f10_s1739874543628_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543560 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543560_f10_s1739874543618_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543560 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543560_f11_s1739874543687_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543561 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543561_f10_s1739874543648_e50_d16_.jpg
===================result key:7
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543427_f6_s1739874543474_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543427_f6_s1739874543485_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543427_f6_s1739874543684_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543427_f6_s1739874543518_e50_d16_.jpg
===================result key:8
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543294_f2_s1739874543329_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543293 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543293_f2_s1739874543446_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543294_f2_s1739874543319_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543294_f2_s1739874543359_e50_d16_.jpg
===================result key:9
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543394_f5_s1739874543473_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543393 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543393_f5_s1739874543470_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543394_f5_s1739874543661_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543394_f5_s1739874543506_e50_d16_.jpg
===================result key:10
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543250_e5000_d16_.jpg
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543227_f0_s1739874543304_e50_d16_.jpg
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543227_f0_s1739874543250_e5000_d16_.jpg
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg

Some files were not shown because too many files have changed in this diff Show More