Skip to content

Commit

Permalink
Merge branch 'IntelRealSense:ros2-development' into ros2
Browse files Browse the repository at this point in the history
  • Loading branch information
masf7g authored Jun 24, 2024
2 parents 8d95dd1 + 598756b commit 7b03659
Show file tree
Hide file tree
Showing 24 changed files with 449 additions and 43 deletions.
19 changes: 17 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,32 @@ jobs:
# wget $bag_filename -P "records/"
# sudo apt install ros-${{ matrix.ros_distro}}-launch-pytest

- name: Install Packages For Tests
- name: Install Packages For Foxy Tests
if: ${{ matrix.ros_distro == 'foxy' }}
run: |
# To avoid mixing of 'apt' provided packages and 'pip' provided packages, one way is to create virtual env
# and manage python packages within it. Ref: https://peps.python.org/pep-0668/
python3 -m venv .venv
# Activate the virtual env such that following python related commands run within it.
source .venv/bin/activate
sudo apt-get install python3-pip
pip3 install numpy --upgrade
# numpy-quaternion needs numpy<2.0.0. Chose 1.24.1 as it is the highest version that support foxy.
pip3 install --force-reinstall numpy==1.24.1
pip3 install numpy-quaternion tqdm pyyaml
- name: Install Packages For Humble/Iron/Rolling/Jazzy Tests
if: ${{ matrix.ros_distro != 'foxy' }}
run: |
# To avoid mixing of 'apt' provided packages and 'pip' provided packages, one way is to create virtual env
# and manage python packages within it. Ref: https://peps.python.org/pep-0668/
python3 -m venv .venv
# Activate the virtual env such that following python related commands run within it.
source .venv/bin/activate
sudo apt-get install python3-pip
# numpy-quaternion needs numpy<2.0.0. Chose 1.26.4 as it is the lowest working version for ubuntu 24.04.
pip3 install --force-reinstall numpy==1.26.4
pip3 install numpy-quaternion tqdm pyyaml

- name: Run Tests
run: |
Expand Down
67 changes: 60 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* [Metadata Topic](#metadata-topic)
* [Post-Processing Filters](#post-processing-filters)
* [Available Services](#available-services)
* [Available Actions](#available-actions)
* [Efficient intra-process communication](#efficient-intra-process-communication)
* [Contributing](CONTRIBUTING.md)
* [License](LICENSE)
Expand All @@ -50,9 +51,9 @@

<details>
<summary>
Intel RealSense ROS1 Wrapper
ROS1 Wrapper for Intel® RealSense™ cameras
</summary>
Intel Realsense ROS1 Wrapper is not supported anymore, since our developers team are focusing on ROS2 distro.<br>
ROS1 Wrapper for Intel® RealSense™ cameras is not supported anymore, since our developers team are focusing on ROS2 distro.<br>
For ROS1 wrapper, go to <a href="https://github.com/IntelRealSense/realsense-ros/tree/ros1-legacy">ros1-legacy</a> branch
</details>

Expand Down Expand Up @@ -120,7 +121,7 @@

<details>
<summary>
Step 3: Install Intel&reg; RealSense&trade; ROS2 wrapper
Step 3: Install ROS Wrapper for Intel&reg; RealSense&trade; cameras
</summary>

#### Option 1: Install debian package from ROS servers (Foxy EOL distro is not supported by this option):
Expand All @@ -136,7 +137,7 @@
cd ~/ros2_ws/src/
```

- Clone the latest ROS2 Intel&reg; RealSense&trade; wrapper from [here](https://github.com/IntelRealSense/realsense-ros.git) into '~/ros2_ws/src/'
- Clone the latest ROS Wrapper for Intel&reg; RealSense&trade; cameras from [here](https://github.com/IntelRealSense/realsense-ros.git) into '~/ros2_ws/src/'
```bashrc
git clone https://github.com/IntelRealSense/realsense-ros.git -b ros2-development
cd ~/ros2_ws
Expand Down Expand Up @@ -168,7 +169,7 @@
<hr>

# Installation on Windows
**PLEASE PAY ATTENTION: RealSense ROS2 Wrapper is not meant to be supported on Windows by our team, since ROS2 and its packages are still not fully supported over Windows. We added these installation steps below in order to try and make it easier for users who already started working with ROS2 on Windows and want to take advantage of the capabilities of our RealSense cameras**
**PLEASE PAY ATTENTION: ROS Wrapper for Intel&reg; RealSense&trade; cameras is not meant to be supported on Windows by our team, since ROS2 and its packages are still not fully supported over Windows. We added these installation steps below in order to try and make it easier for users who already started working with ROS2 on Windows and want to take advantage of the capabilities of our RealSense cameras**

<details>
<summary>
Expand All @@ -185,7 +186,7 @@
- [ROS2 Foxy](https://docs.ros.org/en/foxy/Installation/Windows-Install-Binary.html)
- Microsoft IOT binary installation:
- https://ms-iot.github.io/ROSOnWindows/GettingStarted/SetupRos2.html
- Pay attention that the examples of install are for Foxy distro (which is not supported anymore by RealSense ROS2 Wrapper)
- Pay attention that the examples of install are for Foxy distro (which is not supported anymore by ROS Wrapper for Intel&reg; RealSense&trade; cameras)
- Please replace the word "Foxy" with Humble or Iron, depends on the chosen distro.
</details>

Expand All @@ -194,7 +195,7 @@
Step 2: Download RealSense&trade; ROS2 Wrapper and RealSense&trade; SDK 2.0 source code from github:
</summary>

- Download Intel&reg; RealSense&trade; ROS2 Wrapper source code from [Intel&reg; RealSense&trade; ROS2 Wrapper Releases](https://github.com/IntelRealSense/realsense-ros/releases)
- Download ROS Wrapper for Intel&reg; RealSense&trade; cameras source code from [ROS Wrapper for Intel&reg; RealSense&trade; cameras releases](https://github.com/IntelRealSense/realsense-ros/releases)
- Download the corrosponding supported Intel&reg; RealSense&trade; SDK 2.0 source code from the **"Supported RealSense SDK" section** of the specific release you chose fronm the link above
- Place the librealsense folder inside the realsense-ros folder, to make the librealsense package set beside realsense2_camera, realsense2_camera_msgs and realsense2_description packages
</details>
Expand Down Expand Up @@ -637,6 +638,58 @@ Each of the above filters have it's own parameters, following the naming convent
- Type `ros2 interface show realsense2_camera_msgs/srv/DeviceInfo` for the full list.
- Call example: `ros2 service call /camera/camera/device_info realsense2_camera_msgs/srv/DeviceInfo`
### calib_config_read (for specific camera modules):
- Read calibration config.
- Note that reading calibration config is applicable only in Safey Service Mode
- Type `ros2 interface show realsense2_camera_msgs/srv/CalibConfigRead` for the full request/response fields.
- Call example: `ros2 service call /camera/camera/calib_config_read realsense2_camera_msgs/srv/CalibConfigRead`
<details>
<summary>Click to see the full response of the call example</summary>
`response: realsense2_camera_msgs.srv.CalibConfigRead_Response(success=True, error_message='', calib_config='{"calibration_config":{"calib_roi_num_of_segments":0,"camera_position":{"rotation":[[0,0,0],[0,0,0],[0,0,0]],"translation":[0,0,0]},"crypto_signature":[0, 0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],"roi":[[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]]]}}')`
</details>
- [JSON safety interface config example](realsense2_camera/examples/jsons/calib_config_example.json)
### calib_config_write (for specific camera modules):
- Write calibration config.
- Note that writing calibration config is applicable only in Safey Service Mode
- Type `ros2 interface show realsense2_camera_msgs/srv/CalibConfigWrite` for the full request/response fields.
- Only for commnad line usage, user should escape all " with \\". Using ros2 services API from rclcpp/rclpy doesn't need escaping. e.g.,:
<details>
<summary>Click to see full call example</summary>
`ros2 service call /camera/camera/calib_config_write realsense2_camera_msgs/srv/CalibConfigWrite "{calib_config: '{\"calibration_config\":{\"calib_roi_num_of_segments\":0,\"camera_position\":{\"rotation\":[[0,0,0],[0,0,0],[0,0,0]],\"translation\":[0,0,0]},\"crypto_signature\":[0, 0 ,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\"roi\":[[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]],[[0,0],[0,0],[0,0],[0,0]]]}}' }"`
</details>
- [JSON safety interface config example](realsense2_camera/examples/jsons/calib_config_example.json)
- Result example: `realsense2_camera_msgs.srv.CalibConfigWrite_Response(success=True, error_message='')`
<hr>
## Available actions
### triggered_calibration
- Type `ros2 interface show realsense2_camera_msgs/action/TriggeredCalibration` for the full request/result/feedback fields.
```
# request
string json "calib run" # default value
---
# result
string calibration
float32 health
---
# feedback
float32 progress
```
- To use from command line: `ros2 action send_goal /camera/camera/triggered_calibration realsense2_camera_msgs/action/TriggeredCalibration '{json: "{calib run}"}'` or even with an empty request `ros2 action send_goal /camera/camera/triggered_calibration realsense2_camera_msgs/action/TriggeredCalibration ''` because the default behavior is already calib run.
- The action gives an updated feedback about the progress (%) if the client asks for feedback.
- If succeded, the action writes the new calibration table to the flash. It also returns the new calibration table as json string and the health as float32
<hr>
Expand Down
6 changes: 4 additions & 2 deletions realsense2_camera/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ find_package(builtin_interfaces REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(image_transport REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(realsense2_camera_msgs REQUIRED)
find_package(std_msgs REQUIRED)
Expand All @@ -117,9 +118,9 @@ find_package(tf2 REQUIRED)
find_package(diagnostic_updater REQUIRED)
find_package(OpenCV REQUIRED COMPONENTS core)

find_package(realsense2 2.55.1)
find_package(realsense2 2.56.0)
if (BUILD_ACCELERATE_GPU_WITH_GLSL)
find_package(realsense2-gl 2.55.1)
find_package(realsense2-gl 2.56.0)
endif()
if(NOT realsense2_FOUND)
message(FATAL_ERROR "\n\n Intel RealSense SDK 2.0 is missing, please install it from https://github.com/IntelRealSense/librealsense/releases\n\n")
Expand All @@ -145,6 +146,7 @@ set(SOURCES
src/profile_manager.cpp
src/image_publisher.cpp
src/tfs.cpp
src/actions.cpp
)

if (BUILD_ACCELERATE_GPU_WITH_GLSL)
Expand Down
21 changes: 21 additions & 0 deletions realsense2_camera/include/base_realsense_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
#include "realsense2_camera_msgs/msg/metadata.hpp"
#include "realsense2_camera_msgs/msg/rgbd.hpp"
#include "realsense2_camera_msgs/srv/device_info.hpp"
#include "realsense2_camera_msgs/srv/calib_config_read.hpp"
#include "realsense2_camera_msgs/srv/calib_config_write.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "realsense2_camera_msgs/action/triggered_calibration.hpp"
#include <librealsense2/hpp/rs_processing.hpp>
#include <librealsense2/rs_advanced_mode.hpp>

Expand Down Expand Up @@ -118,6 +122,8 @@ namespace realsense2_camera
void publishTopics();

public:
using TriggeredCalibration = realsense2_camera_msgs::action::TriggeredCalibration;
using GoalHandleTriggeredCalibration = rclcpp_action::ServerGoalHandle<TriggeredCalibration>;
enum class imu_sync_method{NONE, COPY, LINEAR_INTERPOLATION};

protected:
Expand Down Expand Up @@ -150,6 +156,10 @@ namespace realsense2_camera
std::vector<rs2_option> _monitor_options;
rclcpp::Logger _logger;
rclcpp::Service<realsense2_camera_msgs::srv::DeviceInfo>::SharedPtr _device_info_srv;
rclcpp::Service<realsense2_camera_msgs::srv::CalibConfigRead>::SharedPtr _calib_config_read_srv;
rclcpp::Service<realsense2_camera_msgs::srv::CalibConfigWrite>::SharedPtr _calib_config_write_srv;
rclcpp_action::Server<TriggeredCalibration>::SharedPtr _triggered_calibration_action_server;

std::shared_ptr<Parameters> _parameters;
std::list<std::string> _parameters_names;

Expand All @@ -158,6 +168,16 @@ namespace realsense2_camera
void calcAndAppendTransformMsgs(const rs2::stream_profile& profile, const rs2::stream_profile& base_profile);
void getDeviceInfo(const realsense2_camera_msgs::srv::DeviceInfo::Request::SharedPtr req,
realsense2_camera_msgs::srv::DeviceInfo::Response::SharedPtr res);
void CalibConfigReadService(const realsense2_camera_msgs::srv::CalibConfigRead::Request::SharedPtr req,
realsense2_camera_msgs::srv::CalibConfigRead::Response::SharedPtr res);
void CalibConfigWriteService(const realsense2_camera_msgs::srv::CalibConfigWrite::Request::SharedPtr req,
realsense2_camera_msgs::srv::CalibConfigWrite::Response::SharedPtr res);

rclcpp_action::GoalResponse TriggeredCalibrationHandleGoal(const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const TriggeredCalibration::Goal> goal);
rclcpp_action::CancelResponse TriggeredCalibrationHandleCancel(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle);
void TriggeredCalibrationHandleAccepted(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle);
void TriggeredCalibrationExecute(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle);

tf2::Quaternion rotationMatrixToQuaternion(const float rotation[9]) const;
void append_static_tf_msg(const rclcpp::Time& t,
const float3& trans,
Expand Down Expand Up @@ -263,6 +283,7 @@ namespace realsense2_camera
void startUpdatedSensors();
void stopRequiredSensors();
void publishServices();
void publishActions();
void startPublishers(const std::vector<rs2::stream_profile>& profiles, const RosSensor& sensor);
void startRGBDPublisherIfNeeded();
void stopPublishers(const std::vector<rs2::stream_profile>& profiles);
Expand Down
2 changes: 1 addition & 1 deletion realsense2_camera/include/ros_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ namespace realsense2_camera
const rmw_qos_profile_t qos_string_to_qos(std::string str);
const std::string list_available_qos_strings();
rs2_format string_to_rs2_format(std::string str);

std::string vectorToJsonString(const std::vector<uint8_t>& vec);
}

121 changes: 121 additions & 0 deletions realsense2_camera/src/actions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2024 Intel Corporation. All Rights Reserved.
//
// 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 "../include/base_realsense_node.h"
using namespace realsense2_camera;
using namespace rs2;

/***
* Implementation of ROS2 Actions based on:
* ROS2 Actions Design: https://design.ros2.org/articles/actions.html
* ROS2 Actions Tutorials/Examples: https://docs.ros.org/en/rolling/Tutorials/Intermediate/Writing-an-Action-Server-Client/Cpp.html
*/

// Triggered Calibration Action Struct (Message)
/*
# request
string json "calib run"
---
# result
string calibration
float32 health
---
# feedback
float32 progress
*/

/***
* A callback for handling new goals (requests) for Triggered Calibration
* This implementation just accepts all goals with no restriction on the json input
*/
rclcpp_action::GoalResponse BaseRealSenseNode::TriggeredCalibrationHandleGoal(const rclcpp_action::GoalUUID & uuid,
std::shared_ptr<const TriggeredCalibration::Goal> goal)
{
(void)uuid; // unused parameter
ROS_INFO_STREAM("TriggeredCalibrationAction: Received request with json " << goal->json);
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
}

/***
* A callback for handling cancel events
* This implementation just tells the client that it accepted the cancellation.
*/
rclcpp_action::CancelResponse BaseRealSenseNode::TriggeredCalibrationHandleCancel(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle)
{
(void)goal_handle; // unused parameter
ROS_INFO("TriggeredCalibrationAction: Received request to cancel");
return rclcpp_action::CancelResponse::ACCEPT;
}

/***
* A callback that accepts a new goal (request) and starts processing it.
* Since the execution is a long-running operation, we spawn off a
* thread to do the actual work and return from handle_accepted quickly.
*/
void BaseRealSenseNode::TriggeredCalibrationHandleAccepted(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle)
{
using namespace std::placeholders;
ROS_INFO("TriggeredCalibrationAction: Request accepted");
// this needs to return quickly to avoid blocking the executor, so spin up a new thread
std::thread{std::bind(&BaseRealSenseNode::TriggeredCalibrationExecute, this, _1), goal_handle}.detach();
}

/***
* All processing and updates of Triggered Calibration operation
* are done in this execute method in the new thread called by the
* TriggeredCalibrationHandleAccepted() above.
*/
void BaseRealSenseNode::TriggeredCalibrationExecute(const std::shared_ptr<GoalHandleTriggeredCalibration> goal_handle)
{
ROS_INFO("TriggeredCalibrationAction: Executing...");
const auto goal = goal_handle->get_goal(); // get the TriggeredCalibration srv struct
auto feedback = std::make_shared<TriggeredCalibration::Feedback>();
float & _progress = feedback->progress;
auto result = std::make_shared<TriggeredCalibration::Result>();

try
{
rs2::auto_calibrated_device ac_dev = _dev.as<auto_calibrated_device>();
float health = 0.f; // output health
int timeout_ms = 120000; // 2 minutes timout
auto ans = ac_dev.run_on_chip_calibration(goal->json,
&health,
[&](const float progress) {_progress = progress; },
timeout_ms);

// the new calibration is the result without the first 3 bytes
rs2::calibration_table new_calib = std::vector<uint8_t>(ans.begin() + 3, ans.end());

if (rclcpp::ok() && _progress == 100.0)
{
result->calibration = vectorToJsonString(new_calib);
result->health = health;
goal_handle->succeed(result);
ROS_DEBUG("TriggeredCalibrationExecute: Succeded");
}
else
{
result->calibration = "{}";
goal_handle->canceled(result);
ROS_WARN("TriggeredCalibrationExecute: Canceled");
}
}
catch(...)
{
// exception must have been thrown from run_on_chip_calibration call
result->calibration = "{}";
goal_handle->abort(result);
ROS_ERROR("TriggeredCalibrationExecute: Aborted");
}
}
13 changes: 13 additions & 0 deletions realsense2_camera/src/ros_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,17 @@ const std::string list_available_qos_strings()
return res.str();
}

std::string vectorToJsonString(const std::vector<uint8_t>& vec) {
std::ostringstream oss;
oss << "[";
for (size_t i = 0; i < vec.size(); ++i) {
oss << static_cast<int>(vec[i]);
if (i < vec.size() - 1) {
oss << ",";
}
}
oss << "]";
return oss.str();
}

}
Loading

0 comments on commit 7b03659

Please sign in to comment.