mirror of
https://github.com/orbbec/OrbbecSDK_ROS2.git
synced 2026-10-05 04:27:46 +08:00
Merge branch 'feat/gige-action-command' into v2/develop
This commit is contained in:
@@ -269,6 +269,22 @@ orbbec_target_dependencies(${PROJECT_NAME}
|
|||||||
rclcpp_components_register_node(
|
rclcpp_components_register_node(
|
||||||
${PROJECT_NAME} PLUGIN "orbbec_camera::OBCameraNodeDriver" EXECUTABLE orbbec_camera_node
|
${PROJECT_NAME} PLUGIN "orbbec_camera::OBCameraNodeDriver" EXECUTABLE orbbec_camera_node
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(gige_action_command_component SHARED src/gige_action_command_node.cpp)
|
||||||
|
target_include_directories(gige_action_command_component PUBLIC ${COMMON_INCLUDE_DIRS})
|
||||||
|
target_link_directories(gige_action_command_component PRIVATE ${ORBBEC_LIBS_DIR})
|
||||||
|
target_link_libraries(gige_action_command_component
|
||||||
|
${orbbec_camera_msgs_TARGETS}
|
||||||
|
OrbbecSDK
|
||||||
|
rclcpp::rclcpp
|
||||||
|
)
|
||||||
|
orbbec_target_dependencies(gige_action_command_component rclcpp_components)
|
||||||
|
rclcpp_components_register_node(
|
||||||
|
gige_action_command_component
|
||||||
|
PLUGIN "orbbec_camera::GigEActionCommandNode"
|
||||||
|
EXECUTABLE gige_action_command_node
|
||||||
|
)
|
||||||
|
|
||||||
# Add nodes using the macro
|
# Add nodes using the macro
|
||||||
add_orbbec_executable(list_devices_node tools/list_devices_node.cpp)
|
add_orbbec_executable(list_devices_node tools/list_devices_node.cpp)
|
||||||
add_orbbec_executable(list_depth_work_mode_node tools/list_depth_work_mode.cpp)
|
add_orbbec_executable(list_depth_work_mode_node tools/list_depth_work_mode.cpp)
|
||||||
@@ -360,7 +376,8 @@ rclcpp_components_register_node(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Install rules
|
# Install rules
|
||||||
install(TARGETS ${PROJECT_NAME} frame_latency start_benchmark multi_save_rgbir ARCHIVE DESTINATION lib
|
install(TARGETS ${PROJECT_NAME} gige_action_command_component frame_latency start_benchmark
|
||||||
|
multi_save_rgbir ARCHIVE DESTINATION lib
|
||||||
LIBRARY DESTINATION lib RUNTIME DESTINATION bin
|
LIBRARY DESTINATION lib RUNTIME DESTINATION bin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ For command-line maintenance and diagnostic utilities, see the official
|
|||||||
| Source | Purpose | Experience Level | Official Guide |
|
| Source | Purpose | Experience Level | Official Guide |
|
||||||
| :---: | --- | :---: | --- |
|
| :---: | --- | :---: | --- |
|
||||||
| [Net camera](./net_camera) | Launch supported Orbbec network cameras. | ⭐️ | [Network camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/configuration/net_camera.html) |
|
| [Net camera](./net_camera) | Launch supported Orbbec network cameras. | ⭐️ | [Network camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/configuration/net_camera.html) |
|
||||||
|
| [GigE Action Command](./gige_action_command) | Configure and trigger a Gemini 335Le camera group over GVCP. | ⭐️⭐️ | [README](./gige_action_command/README.md) |
|
||||||
| [GMSL camera](./gmsl_camera) | Launch single, multiple, or synchronized GMSL cameras. | ⭐️ | [GMSL camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/multi_camera/gmsl_camera.html) |
|
| [GMSL camera](./gmsl_camera) | Launch single, multiple, or synchronized GMSL cameras. | ⭐️ | [GMSL camera guide](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/5_advanced_guide/multi_camera/gmsl_camera.html) |
|
||||||
| [AE/AWB lock test](./ae_awb_lock) | Verify AE/AWB convergence, frame-metadata capture, and manual lock-in through ROS 2. | ⭐️⭐️ | [README](./ae_awb_lock/README.md) |
|
| [AE/AWB lock test](./ae_awb_lock) | Verify AE/AWB convergence, frame-metadata capture, and manual lock-in through ROS 2. | ⭐️⭐️ | [README](./ae_awb_lock/README.md) |
|
||||||
| [Benchmark](./benchmark) | Benchmark the performance of different camera configurations. | ⭐️⭐️ | [Performance benchmark tools](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/benchmark_tools.html) |
|
| [Benchmark](./benchmark) | Benchmark the performance of different camera configurations. | ⭐️⭐️ | [Performance benchmark tools](https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/benchmark_tools.html) |
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# GigE Action Command
|
||||||
|
|
||||||
|
This example starts two Gemini 335Le cameras in Group Actions synchronization mode and one
|
||||||
|
host-side Action Command sender. The sender is intentionally created once at the top level because
|
||||||
|
a GVCP Action Command can trigger multiple cameras.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Gemini 335Le firmware 1.8.24 or later
|
||||||
|
- Orbbec SDK 2.10.2 or later
|
||||||
|
- Both cameras and the host on the same network
|
||||||
|
|
||||||
|
Before running the example, change the two `net_device_ip` values in
|
||||||
|
`multi_gige_action_command.launch.py` to match the cameras.
|
||||||
|
|
||||||
|
## Start the cameras and sender
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ros2 launch orbbec_camera multi_gige_action_command.launch.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The launch file creates these device-scoped configuration services and one network-scoped sender:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/camera_01/get_action_config
|
||||||
|
/camera_01/set_action_config
|
||||||
|
/camera_02/get_action_config
|
||||||
|
/camera_02/set_action_config
|
||||||
|
/gige_action_command_node/send_action_command
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configure the cameras
|
||||||
|
|
||||||
|
Configure Action Signal block 0 on both cameras with matching keys and masks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ros2 service call /camera_01/set_action_config \
|
||||||
|
orbbec_camera_msgs/srv/SetActionConfig \
|
||||||
|
"{device_key: 1, selector: 0, group_key: 1, group_mask: 1}"
|
||||||
|
|
||||||
|
ros2 service call /camera_02/set_action_config \
|
||||||
|
orbbec_camera_msgs/srv/SetActionConfig \
|
||||||
|
"{device_key: 1, selector: 0, group_key: 1, group_mask: 1}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Read the configuration back when needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ros2 service call /camera_01/get_action_config \
|
||||||
|
orbbec_camera_msgs/srv/GetActionConfig \
|
||||||
|
"{selector: 0}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Trigger the group
|
||||||
|
|
||||||
|
Send an immediate broadcast command. Every camera whose device key, group key, and group mask
|
||||||
|
match the request will be triggered:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ros2 service call /gige_action_command_node/send_action_command \
|
||||||
|
orbbec_camera_msgs/srv/SendActionCommand \
|
||||||
|
"{device_key: 1, group_key: 1, group_mask: 1, destination_ip: '255.255.255.255', scheduled_time: 0}"
|
||||||
|
```
|
||||||
|
|
||||||
|
`success: true` means the host dispatched the GVCP command; the protocol does not return a device
|
||||||
|
acknowledgment. A nonzero `scheduled_time` uses a GVCP/PTP timestamp, with seconds in the upper
|
||||||
|
32 bits and nanoseconds in the lower 32 bits. Scheduled triggering requires the cameras and sender
|
||||||
|
host to use synchronized time.
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from launch import LaunchDescription
|
||||||
|
from launch.actions import GroupAction, IncludeLaunchDescription, TimerAction
|
||||||
|
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||||
|
from launch_ros.actions import Node
|
||||||
|
|
||||||
|
|
||||||
|
def generate_launch_description():
|
||||||
|
package_dir = get_package_share_directory("orbbec_camera")
|
||||||
|
camera_launch = os.path.join(
|
||||||
|
package_dir, "launch", "gemini_330_series.launch.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
camera_01 = IncludeLaunchDescription(
|
||||||
|
PythonLaunchDescriptionSource(camera_launch),
|
||||||
|
launch_arguments={
|
||||||
|
"camera_name": "camera_01",
|
||||||
|
"enumerate_net_device": "false",
|
||||||
|
"net_device_ip": "192.168.1.10",
|
||||||
|
"net_device_port": "8090",
|
||||||
|
"sync_mode": "group_actions",
|
||||||
|
"log_file_name": "camera_01.log",
|
||||||
|
}.items(),
|
||||||
|
)
|
||||||
|
|
||||||
|
camera_02 = IncludeLaunchDescription(
|
||||||
|
PythonLaunchDescriptionSource(camera_launch),
|
||||||
|
launch_arguments={
|
||||||
|
"camera_name": "camera_02",
|
||||||
|
"enumerate_net_device": "false",
|
||||||
|
"net_device_ip": "192.168.1.11",
|
||||||
|
"net_device_port": "8090",
|
||||||
|
"sync_mode": "group_actions",
|
||||||
|
"log_file_name": "camera_02.log",
|
||||||
|
}.items(),
|
||||||
|
)
|
||||||
|
|
||||||
|
action_command_node = Node(
|
||||||
|
package="orbbec_camera",
|
||||||
|
executable="gige_action_command_node",
|
||||||
|
name="gige_action_command_node",
|
||||||
|
output="screen",
|
||||||
|
)
|
||||||
|
|
||||||
|
return LaunchDescription(
|
||||||
|
[
|
||||||
|
action_command_node,
|
||||||
|
TimerAction(period=0.0, actions=[GroupAction([camera_01])]),
|
||||||
|
TimerAction(period=2.0, actions=[GroupAction([camera_02])]),
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2026 Orbbec 3D Technology, Inc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <rclcpp/rclcpp.hpp>
|
||||||
|
|
||||||
|
#include "libobsensor/hpp/Context.hpp"
|
||||||
|
#include "orbbec_camera_msgs/srv/send_action_command.hpp"
|
||||||
|
|
||||||
|
namespace orbbec_camera {
|
||||||
|
|
||||||
|
class GigEActionCommandNode : public rclcpp::Node {
|
||||||
|
public:
|
||||||
|
explicit GigEActionCommandNode(const rclcpp::NodeOptions& node_options = rclcpp::NodeOptions());
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendActionCommandCallback(
|
||||||
|
const std::shared_ptr<orbbec_camera_msgs::srv::SendActionCommand::Request> request,
|
||||||
|
std::shared_ptr<orbbec_camera_msgs::srv::SendActionCommand::Response> response);
|
||||||
|
|
||||||
|
std::unique_ptr<ob::Context> context_;
|
||||||
|
rclcpp::Service<orbbec_camera_msgs::srv::SendActionCommand>::SharedPtr
|
||||||
|
send_action_command_service_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace orbbec_camera
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
#include "orbbec_camera_msgs/msg/depth_filter_state.hpp"
|
#include "orbbec_camera_msgs/msg/depth_filter_state.hpp"
|
||||||
#include "orbbec_camera_msgs/msg/depth_filters_status.hpp"
|
#include "orbbec_camera_msgs/msg/depth_filters_status.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/get_device_config.hpp"
|
#include "orbbec_camera_msgs/srv/get_device_config.hpp"
|
||||||
|
#include "orbbec_camera_msgs/srv/get_action_config.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/get_device_info.hpp"
|
#include "orbbec_camera_msgs/srv/get_device_info.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/get_awb_gain.hpp"
|
#include "orbbec_camera_msgs/srv/get_awb_gain.hpp"
|
||||||
#include "orbbec_camera_msgs/msg/extrinsics.hpp"
|
#include "orbbec_camera_msgs/msg/extrinsics.hpp"
|
||||||
@@ -65,6 +66,7 @@
|
|||||||
#include "orbbec_camera_msgs/srv/get_bool.hpp"
|
#include "orbbec_camera_msgs/srv/get_bool.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/set_string.hpp"
|
#include "orbbec_camera_msgs/srv/set_string.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/set_filter.hpp"
|
#include "orbbec_camera_msgs/srv/set_filter.hpp"
|
||||||
|
#include "orbbec_camera_msgs/srv/set_action_config.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/set_arrays.hpp"
|
#include "orbbec_camera_msgs/srv/set_arrays.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/set_stream_profile.hpp"
|
#include "orbbec_camera_msgs/srv/set_stream_profile.hpp"
|
||||||
#include "orbbec_camera_msgs/srv/get_user_calib_params.hpp"
|
#include "orbbec_camera_msgs/srv/get_user_calib_params.hpp"
|
||||||
@@ -135,6 +137,7 @@
|
|||||||
|
|
||||||
namespace orbbec_camera {
|
namespace orbbec_camera {
|
||||||
using GetDeviceConfig = orbbec_camera_msgs::srv::GetDeviceConfig;
|
using GetDeviceConfig = orbbec_camera_msgs::srv::GetDeviceConfig;
|
||||||
|
using GetActionConfig = orbbec_camera_msgs::srv::GetActionConfig;
|
||||||
using GetDeviceInfo = orbbec_camera_msgs::srv::GetDeviceInfo;
|
using GetDeviceInfo = orbbec_camera_msgs::srv::GetDeviceInfo;
|
||||||
using Extrinsics = orbbec_camera_msgs::msg::Extrinsics;
|
using Extrinsics = orbbec_camera_msgs::msg::Extrinsics;
|
||||||
using SetInt32 = orbbec_camera_msgs::srv::SetInt32;
|
using SetInt32 = orbbec_camera_msgs::srv::SetInt32;
|
||||||
@@ -146,6 +149,7 @@ using SetString = orbbec_camera_msgs::srv::SetString;
|
|||||||
using SetBool = std_srvs::srv::SetBool;
|
using SetBool = std_srvs::srv::SetBool;
|
||||||
using GetBool = orbbec_camera_msgs::srv::GetBool;
|
using GetBool = orbbec_camera_msgs::srv::GetBool;
|
||||||
using SetFilter = orbbec_camera_msgs::srv::SetFilter;
|
using SetFilter = orbbec_camera_msgs::srv::SetFilter;
|
||||||
|
using SetActionConfig = orbbec_camera_msgs::srv::SetActionConfig;
|
||||||
using SetArrays = orbbec_camera_msgs::srv::SetArrays;
|
using SetArrays = orbbec_camera_msgs::srv::SetArrays;
|
||||||
using SetStreamProfile = orbbec_camera_msgs::srv::SetStreamProfile;
|
using SetStreamProfile = orbbec_camera_msgs::srv::SetStreamProfile;
|
||||||
using SetUserCalibParams = orbbec_camera_msgs::srv::SetUserCalibParams;
|
using SetUserCalibParams = orbbec_camera_msgs::srv::SetUserCalibParams;
|
||||||
@@ -481,6 +485,12 @@ class OBCameraNode {
|
|||||||
void getDeviceConfigCallback(const std::shared_ptr<GetDeviceConfig::Request>& request,
|
void getDeviceConfigCallback(const std::shared_ptr<GetDeviceConfig::Request>& request,
|
||||||
std::shared_ptr<GetDeviceConfig::Response>& response);
|
std::shared_ptr<GetDeviceConfig::Response>& response);
|
||||||
|
|
||||||
|
void getActionConfigCallback(const std::shared_ptr<GetActionConfig::Request>& request,
|
||||||
|
std::shared_ptr<GetActionConfig::Response>& response);
|
||||||
|
|
||||||
|
void setActionConfigCallback(const std::shared_ptr<SetActionConfig::Request>& request,
|
||||||
|
std::shared_ptr<SetActionConfig::Response>& response);
|
||||||
|
|
||||||
void getSDKVersion(const std::shared_ptr<GetString::Request>& request,
|
void getSDKVersion(const std::shared_ptr<GetString::Request>& request,
|
||||||
std::shared_ptr<GetString::Response>& response);
|
std::shared_ptr<GetString::Response>& response);
|
||||||
|
|
||||||
@@ -778,6 +788,8 @@ class OBCameraNode {
|
|||||||
std::map<stream_index_pair, rclcpp::Service<SetArrays>::SharedPtr> set_ae_roi_srv_;
|
std::map<stream_index_pair, rclcpp::Service<SetArrays>::SharedPtr> set_ae_roi_srv_;
|
||||||
rclcpp::Service<GetDeviceInfo>::SharedPtr get_device_srv_;
|
rclcpp::Service<GetDeviceInfo>::SharedPtr get_device_srv_;
|
||||||
rclcpp::Service<GetDeviceConfig>::SharedPtr get_device_config_srv_;
|
rclcpp::Service<GetDeviceConfig>::SharedPtr get_device_config_srv_;
|
||||||
|
rclcpp::Service<GetActionConfig>::SharedPtr get_action_config_srv_;
|
||||||
|
rclcpp::Service<SetActionConfig>::SharedPtr set_action_config_srv_;
|
||||||
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr set_laser_enable_srv_;
|
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr set_laser_enable_srv_;
|
||||||
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr set_ldp_enable_srv_;
|
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr set_ldp_enable_srv_;
|
||||||
rclcpp::Service<orbbec_camera_msgs::srv::GetBool>::SharedPtr get_ldp_status_srv_;
|
rclcpp::Service<orbbec_camera_msgs::srv::GetBool>::SharedPtr get_ldp_status_srv_;
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2026 Orbbec 3D Technology, Inc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include "orbbec_camera/gige_action_command_node.h"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "rclcpp_components/register_node_macro.hpp"
|
||||||
|
|
||||||
|
namespace orbbec_camera {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string formatObError(const ob::Error& error) {
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << (error.getMessage() ? error.getMessage() : "Unknown OB error")
|
||||||
|
<< " status:" << static_cast<int>(error.getStatus());
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
GigEActionCommandNode::GigEActionCommandNode(const rclcpp::NodeOptions& node_options)
|
||||||
|
: Node("gige_action_command_node", node_options), context_(std::make_unique<ob::Context>()) {
|
||||||
|
context_->enableNetDeviceEnumeration(true);
|
||||||
|
send_action_command_service_ = create_service<orbbec_camera_msgs::srv::SendActionCommand>(
|
||||||
|
"~/send_action_command", std::bind(&GigEActionCommandNode::sendActionCommandCallback, this,
|
||||||
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
|
RCLCPP_INFO(get_logger(), "GigE Action Command service is ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GigEActionCommandNode::sendActionCommandCallback(
|
||||||
|
const std::shared_ptr<orbbec_camera_msgs::srv::SendActionCommand::Request> request,
|
||||||
|
std::shared_ptr<orbbec_camera_msgs::srv::SendActionCommand::Response> response) {
|
||||||
|
if (!request) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "Invalid request";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string destination_ip =
|
||||||
|
request->destination_ip.empty() ? "255.255.255.255" : request->destination_ip;
|
||||||
|
try {
|
||||||
|
response->success =
|
||||||
|
context_->sendActionCommand(request->device_key, request->group_key, request->group_mask,
|
||||||
|
destination_ip.c_str(), request->scheduled_time);
|
||||||
|
response->message =
|
||||||
|
response->success ? "Action Command dispatched" : "SDK failed to send Action Command";
|
||||||
|
} catch (const ob::Error& error) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = formatObError(error);
|
||||||
|
} catch (const std::exception& error) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = error.what();
|
||||||
|
} catch (...) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "Unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace orbbec_camera
|
||||||
|
|
||||||
|
RCLCPP_COMPONENTS_REGISTER_NODE(orbbec_camera::GigEActionCommandNode)
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <rclcpp/rclcpp.hpp>
|
#include <rclcpp/rclcpp.hpp>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "orbbec_camera/utils.h"
|
#include "orbbec_camera/utils.h"
|
||||||
@@ -112,6 +113,8 @@ std::string OBSyncModeToString(const OBMultiDeviceSyncMode& mode) {
|
|||||||
return "SOFTWARE_TRIGGERING";
|
return "SOFTWARE_TRIGGERING";
|
||||||
case OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING:
|
case OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING:
|
||||||
return "HARDWARE_TRIGGERING";
|
return "HARDWARE_TRIGGERING";
|
||||||
|
case OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS:
|
||||||
|
return "GROUP_ACTIONS";
|
||||||
default:
|
default:
|
||||||
return "FREE_RUN";
|
return "FREE_RUN";
|
||||||
}
|
}
|
||||||
@@ -334,6 +337,28 @@ void OBCameraNode::setupCameraCtrlServices() {
|
|||||||
std::shared_ptr<GetDeviceConfig::Response> response) {
|
std::shared_ptr<GetDeviceConfig::Response> response) {
|
||||||
getDeviceConfigCallback(request, response);
|
getDeviceConfigCallback(request, response);
|
||||||
});
|
});
|
||||||
|
if (isPropertyReadable(device_, OB_PROP_ACTION_SIGNAL_COUNT_INT) &&
|
||||||
|
isPropertyReadable(device_, OB_PROP_ACTION_DEVICE_KEY_INT) &&
|
||||||
|
isPropertyWritable(device_, OB_PROP_ACTION_SELECTOR_INT) &&
|
||||||
|
isPropertyReadable(device_, OB_PROP_ACTION_GROUP_KEY_INT) &&
|
||||||
|
isPropertyReadable(device_, OB_PROP_ACTION_GROUP_MASK_INT)) {
|
||||||
|
get_action_config_srv_ = node_->create_service<GetActionConfig>(
|
||||||
|
"get_action_config", [this](const std::shared_ptr<GetActionConfig::Request> request,
|
||||||
|
std::shared_ptr<GetActionConfig::Response> response) {
|
||||||
|
getActionConfigCallback(request, response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isPropertyReadable(device_, OB_PROP_ACTION_SIGNAL_COUNT_INT) &&
|
||||||
|
isPropertyWritable(device_, OB_PROP_ACTION_DEVICE_KEY_INT) &&
|
||||||
|
isPropertyWritable(device_, OB_PROP_ACTION_SELECTOR_INT) &&
|
||||||
|
isPropertyWritable(device_, OB_PROP_ACTION_GROUP_KEY_INT) &&
|
||||||
|
isPropertyWritable(device_, OB_PROP_ACTION_GROUP_MASK_INT)) {
|
||||||
|
set_action_config_srv_ = node_->create_service<SetActionConfig>(
|
||||||
|
"set_action_config", [this](const std::shared_ptr<SetActionConfig::Request> request,
|
||||||
|
std::shared_ptr<SetActionConfig::Response> response) {
|
||||||
|
setActionConfigCallback(request, response);
|
||||||
|
});
|
||||||
|
}
|
||||||
get_sdk_version_srv_ = node_->create_service<GetString>(
|
get_sdk_version_srv_ = node_->create_service<GetString>(
|
||||||
"get_sdk_version",
|
"get_sdk_version",
|
||||||
[this](const std::shared_ptr<GetString::Request> request,
|
[this](const std::shared_ptr<GetString::Request> request,
|
||||||
@@ -1598,6 +1623,100 @@ void OBCameraNode::getDeviceInfoCallback(const std::shared_ptr<GetDeviceInfo::Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OBCameraNode::getActionConfigCallback(const std::shared_ptr<GetActionConfig::Request>& request,
|
||||||
|
std::shared_ptr<GetActionConfig::Response>& response) {
|
||||||
|
if (!request) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "Invalid request";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<decltype(device_lock_)> lock(device_lock_);
|
||||||
|
try {
|
||||||
|
const int action_signal_count = device_->getIntProperty(OB_PROP_ACTION_SIGNAL_COUNT_INT);
|
||||||
|
if (action_signal_count <= 0 ||
|
||||||
|
request->selector >= static_cast<uint32_t>(action_signal_count)) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "selector must be less than action signal count " +
|
||||||
|
std::to_string(std::max(action_signal_count, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_->setIntProperty(OB_PROP_ACTION_SELECTOR_INT, static_cast<int32_t>(request->selector));
|
||||||
|
response->action_signal_count = static_cast<uint32_t>(action_signal_count);
|
||||||
|
response->device_key =
|
||||||
|
static_cast<uint32_t>(device_->getIntProperty(OB_PROP_ACTION_DEVICE_KEY_INT));
|
||||||
|
response->group_key =
|
||||||
|
static_cast<uint32_t>(device_->getIntProperty(OB_PROP_ACTION_GROUP_KEY_INT));
|
||||||
|
response->group_mask =
|
||||||
|
static_cast<uint32_t>(device_->getIntProperty(OB_PROP_ACTION_GROUP_MASK_INT));
|
||||||
|
response->success = true;
|
||||||
|
response->message = "OK";
|
||||||
|
} catch (const ob::Error& e) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = orbbec_camera::formatObErrorWithStatus(e);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = e.what();
|
||||||
|
} catch (...) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OBCameraNode::setActionConfigCallback(const std::shared_ptr<SetActionConfig::Request>& request,
|
||||||
|
std::shared_ptr<SetActionConfig::Response>& response) {
|
||||||
|
if (!request) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "Invalid request";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<decltype(device_lock_)> lock(device_lock_);
|
||||||
|
try {
|
||||||
|
const int action_signal_count = device_->getIntProperty(OB_PROP_ACTION_SIGNAL_COUNT_INT);
|
||||||
|
if (action_signal_count <= 0 ||
|
||||||
|
request->selector >= static_cast<uint32_t>(action_signal_count)) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "selector must be less than action signal count " +
|
||||||
|
std::to_string(std::max(action_signal_count, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto validate_property = [this](OBPropertyID property_id, uint32_t value,
|
||||||
|
const char* property_name) {
|
||||||
|
const auto range = device_->getIntPropertyRange(property_id);
|
||||||
|
const int64_t signed_value = static_cast<int64_t>(value);
|
||||||
|
if (signed_value < range.min || signed_value > range.max) {
|
||||||
|
throw std::out_of_range(std::string(property_name) + " must be in [" +
|
||||||
|
std::to_string(range.min) + ", " + std::to_string(range.max) + "]");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
validate_property(OB_PROP_ACTION_DEVICE_KEY_INT, request->device_key, "device_key");
|
||||||
|
validate_property(OB_PROP_ACTION_SELECTOR_INT, request->selector, "selector");
|
||||||
|
validate_property(OB_PROP_ACTION_GROUP_KEY_INT, request->group_key, "group_key");
|
||||||
|
validate_property(OB_PROP_ACTION_GROUP_MASK_INT, request->group_mask, "group_mask");
|
||||||
|
|
||||||
|
device_->setIntProperty(OB_PROP_ACTION_DEVICE_KEY_INT,
|
||||||
|
static_cast<int32_t>(request->device_key));
|
||||||
|
device_->setIntProperty(OB_PROP_ACTION_SELECTOR_INT, static_cast<int32_t>(request->selector));
|
||||||
|
device_->setIntProperty(OB_PROP_ACTION_GROUP_KEY_INT, static_cast<int32_t>(request->group_key));
|
||||||
|
device_->setIntProperty(OB_PROP_ACTION_GROUP_MASK_INT,
|
||||||
|
static_cast<int32_t>(request->group_mask));
|
||||||
|
response->success = true;
|
||||||
|
response->message = "OK";
|
||||||
|
} catch (const ob::Error& e) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = orbbec_camera::formatObErrorWithStatus(e);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = e.what();
|
||||||
|
} catch (...) {
|
||||||
|
response->success = false;
|
||||||
|
response->message = "unknown error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OBCameraNode::getDeviceConfigCallback(const std::shared_ptr<GetDeviceConfig::Request>& request,
|
void OBCameraNode::getDeviceConfigCallback(const std::shared_ptr<GetDeviceConfig::Request>& request,
|
||||||
std::shared_ptr<GetDeviceConfig::Response>& response) {
|
std::shared_ptr<GetDeviceConfig::Response>& response) {
|
||||||
(void)request;
|
(void)request;
|
||||||
|
|||||||
@@ -679,6 +679,8 @@ OBMultiDeviceSyncMode OBSyncModeFromString(const std::string &mode) {
|
|||||||
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_TRIGGERING;
|
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_SOFTWARE_TRIGGERING;
|
||||||
} else if (mode == "HARDWARE_TRIGGERING") {
|
} else if (mode == "HARDWARE_TRIGGERING") {
|
||||||
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING;
|
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_HARDWARE_TRIGGERING;
|
||||||
|
} else if (mode == "GROUP_ACTIONS") {
|
||||||
|
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_GROUP_ACTIONS;
|
||||||
} else {
|
} else {
|
||||||
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_FREE_RUN;
|
return OBMultiDeviceSyncMode::OB_MULTI_DEVICE_SYNC_MODE_FREE_RUN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ rosidl_generate_interfaces(
|
|||||||
"msg/RGBD.msg"
|
"msg/RGBD.msg"
|
||||||
"msg/StreamProfile.msg"
|
"msg/StreamProfile.msg"
|
||||||
"srv/GetBool.srv"
|
"srv/GetBool.srv"
|
||||||
|
"srv/GetActionConfig.srv"
|
||||||
"srv/GetDeviceConfig.srv"
|
"srv/GetDeviceConfig.srv"
|
||||||
"srv/GetDeviceInfo.srv"
|
"srv/GetDeviceInfo.srv"
|
||||||
"srv/GetCameraInfo.srv"
|
"srv/GetCameraInfo.srv"
|
||||||
@@ -35,6 +36,7 @@ rosidl_generate_interfaces(
|
|||||||
"srv/GetAwbGain.srv"
|
"srv/GetAwbGain.srv"
|
||||||
"srv/GetString.srv"
|
"srv/GetString.srv"
|
||||||
"srv/SetFilter.srv"
|
"srv/SetFilter.srv"
|
||||||
|
"srv/SetActionConfig.srv"
|
||||||
"srv/SetInt32.srv"
|
"srv/SetInt32.srv"
|
||||||
"srv/SetAwbGain.srv"
|
"srv/SetAwbGain.srv"
|
||||||
"srv/SetString.srv"
|
"srv/SetString.srv"
|
||||||
@@ -43,6 +45,7 @@ rosidl_generate_interfaces(
|
|||||||
"srv/SetUserCalibParams.srv"
|
"srv/SetUserCalibParams.srv"
|
||||||
"srv/SetBagRecording.srv"
|
"srv/SetBagRecording.srv"
|
||||||
"srv/SetStreamProfile.srv"
|
"srv/SetStreamProfile.srv"
|
||||||
|
"srv/SendActionCommand.srv"
|
||||||
"action/RunAeAwbLockTest.action"
|
"action/RunAeAwbLockTest.action"
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
action_msgs
|
action_msgs
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
uint32 selector
|
||||||
|
---
|
||||||
|
uint32 action_signal_count
|
||||||
|
uint32 device_key
|
||||||
|
uint32 group_key
|
||||||
|
uint32 group_mask
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
uint32 device_key
|
||||||
|
uint32 group_key
|
||||||
|
uint32 group_mask
|
||||||
|
# IPv4 destination. Leave empty to use the SDK broadcast address (255.255.255.255).
|
||||||
|
string destination_ip
|
||||||
|
# GVCP/PTP timestamp: upper 32 bits are seconds and lower 32 bits are nanoseconds.
|
||||||
|
# Zero sends the command immediately.
|
||||||
|
uint64 scheduled_time
|
||||||
|
---
|
||||||
|
# Success means that the SDK dispatched the command; Action Command has no device acknowledgment.
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
uint32 device_key
|
||||||
|
uint32 selector
|
||||||
|
uint32 group_key
|
||||||
|
uint32 group_mask
|
||||||
|
---
|
||||||
|
bool success
|
||||||
|
string message
|
||||||
Reference in New Issue
Block a user