Skip to content

Commit e292afc

Browse files
committed
merging in main
2 parents fc34c06 + b79face commit e292afc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+5662
-380
lines changed

.github/actions/build-and-test/action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ runs:
1818
python3-pip \
1919
build-essential \
2020
21+
- name: 🔗 Install rosdep dependencies
22+
shell: bash
23+
run: |
24+
sudo rosdep init || true
25+
rosdep update
26+
rosdep install --from-paths . --ignore-src -r -y
27+
2128
- name: 🧱 Build workspace
2229
shell: bash
2330
run: |

DEVELOPING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ This project includes VS Code dev container configurations for easy ROS2 develop
3131
Inside the container:
3232

3333
```bash
34+
# Update apt
35+
sudo apt update
36+
3437
# Update rosdep
3538
rosdep update
3639

deep_conversions/CMakeLists.txt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright (c) 2025-present WATonomous. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
cmake_minimum_required(VERSION 3.22)
15+
project(deep_conversions)
16+
17+
if(NOT CMAKE_CXX_STANDARD)
18+
set(CMAKE_CXX_STANDARD 17)
19+
endif()
20+
21+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
22+
add_compile_options(-Wall -Wextra -Wpedantic)
23+
add_link_options(-Wl,-no-undefined)
24+
endif()
25+
26+
find_package(ament_cmake REQUIRED)
27+
find_package(rclcpp REQUIRED)
28+
find_package(sensor_msgs REQUIRED)
29+
find_package(std_msgs REQUIRED)
30+
find_package(pluginlib REQUIRED)
31+
find_package(deep_core REQUIRED)
32+
33+
set(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include)
34+
35+
# deep_conversions library
36+
set(DEEP_CONVERSIONS_LIB ${PROJECT_NAME}_lib)
37+
add_library(${DEEP_CONVERSIONS_LIB} SHARED
38+
src/image_conversions.cpp
39+
src/imu_conversions.cpp
40+
src/laserscan_conversions.cpp
41+
src/pointcloud_conversions.cpp
42+
)
43+
44+
target_include_directories(${DEEP_CONVERSIONS_LIB} PUBLIC
45+
$<BUILD_INTERFACE:${include_dir}>
46+
$<INSTALL_INTERFACE:include>
47+
)
48+
49+
target_link_libraries(${DEEP_CONVERSIONS_LIB}
50+
PUBLIC
51+
rclcpp::rclcpp
52+
deep_core::deep_core_lib
53+
${sensor_msgs_TARGETS}
54+
${std_msgs_TARGETS}
55+
)
56+
57+
install(TARGETS
58+
${DEEP_CONVERSIONS_LIB}
59+
EXPORT ${PROJECT_NAME}Targets
60+
ARCHIVE DESTINATION lib
61+
LIBRARY DESTINATION lib
62+
RUNTIME DESTINATION bin
63+
)
64+
65+
install(EXPORT ${PROJECT_NAME}Targets
66+
NAMESPACE ${PROJECT_NAME}::
67+
DESTINATION share/${PROJECT_NAME}/cmake
68+
)
69+
70+
install(DIRECTORY include/
71+
DESTINATION include
72+
)
73+
74+
if(BUILD_TESTING)
75+
find_package(deep_test REQUIRED)
76+
77+
add_deep_test(test_conversions test/test_conversions.cpp
78+
LIBRARIES ${DEEP_CONVERSIONS_LIB}
79+
)
80+
endif()
81+
82+
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
83+
ament_package()

deep_conversions/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# deep_conversions
2+
3+
ROS 2 sensor message to tensor conversion library.
4+
5+
## Overview
6+
7+
Provides generic conversions between ROS 2 sensor messages and `deep_core` tensors.
8+
9+
## Supported Conversions
10+
11+
### From ROS to Tensor
12+
- **Images** (`sensor_msgs/msg/Image`) → Tensor with shape `[batch, height, width, channels]` or `[batch, height, width]` for grayscale
13+
- **PointCloud2** (`sensor_msgs/msg/PointCloud2`) → Tensor with shape `[num_points, num_fields]`
14+
- **LaserScan** (`sensor_msgs/msg/LaserScan`) → Tensor with shape `[num_ranges]` or `[num_ranges, 2]` with intensities
15+
- **IMU** (`sensor_msgs/msg/Imu`) → Tensor with shape `[10]` containing quaternion, linear acceleration, and angular velocity
16+
17+
### From Tensor to ROS
18+
- Tensor → **Image** with specified encoding
19+
20+
## Usage
21+
22+
Include the appropriate header for your message type:
23+
- `deep_conversions/image_conversions.hpp`
24+
- `deep_conversions/pointcloud_conversions.hpp`
25+
- `deep_conversions/laserscan_conversions.hpp`
26+
- `deep_conversions/imu_conversions.hpp`
27+
28+
All conversion functions are in the `deep_ros::ros_conversions` namespace and require a `BackendMemoryAllocator` for tensor creation.
29+
30+
## License
31+
32+
Copyright (c) 2025-present WATonomous. All rights reserved.
33+
34+
Licensed under the Apache License, Version 2.0.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) 2025-present WATonomous. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
#include <string>
19+
#include <vector>
20+
21+
#include <sensor_msgs/msg/image.hpp>
22+
#include <std_msgs/msg/header.hpp>
23+
24+
#include "deep_core/plugin_interfaces/backend_memory_allocator.hpp"
25+
#include "deep_core/types/tensor.hpp"
26+
27+
namespace deep_ros
28+
{
29+
namespace ros_conversions
30+
{
31+
32+
/**
33+
* @brief Image encoding information
34+
*/
35+
struct ImageEncoding
36+
{
37+
DataType dtype;
38+
size_t channels;
39+
size_t bytes_per_channel;
40+
};
41+
42+
/**
43+
* @brief Get encoding information from ROS image encoding string
44+
* @param encoding ROS image encoding string (e.g., "rgb8", "bgr8", "mono8")
45+
* @return ImageEncoding struct with dtype, channels, and bytes_per_channel
46+
* @throws std::runtime_error if encoding is unsupported
47+
*/
48+
ImageEncoding get_image_encoding_info(const std::string & encoding);
49+
50+
/**
51+
* @brief Convert sensor_msgs::msg::Image to Tensor
52+
* @param image ROS Image message
53+
* @param allocator Memory allocator to use (uses CPU allocator if nullptr)
54+
* @return Tensor with shape [1, height, width, channels] or [1, height, width]
55+
* @throws std::runtime_error if image dimensions are invalid or data size mismatches
56+
*/
57+
Tensor from_image(const sensor_msgs::msg::Image & image, std::shared_ptr<BackendMemoryAllocator> allocator = nullptr);
58+
59+
/**
60+
* @brief Convert vector of sensor_msgs::msg::Image to batched Tensor
61+
* @param images Vector of ROS Image messages
62+
* @param allocator Memory allocator to use (uses CPU allocator if nullptr)
63+
* @return Tensor with shape [batch_size, height, width, channels] or [batch_size, height, width]
64+
* @throws std::invalid_argument if batch is empty or images have mismatched dimensions/encodings
65+
*/
66+
Tensor from_image(
67+
const std::vector<sensor_msgs::msg::Image> & images, std::shared_ptr<BackendMemoryAllocator> allocator = nullptr);
68+
69+
/**
70+
* @brief Convert Tensor to sensor_msgs::msg::Image (single image from batch)
71+
* @param tensor Tensor with shape [1, height, width] or [1, height, width, channels]
72+
* @param image Output ROS Image message
73+
* @param encoding Image encoding (must match tensor dtype and shape)
74+
* @param header Optional header to set timestamp and frame_id
75+
* @throws std::invalid_argument if tensor shape/dtype doesn't match encoding
76+
*/
77+
void to_image(
78+
const Tensor & tensor,
79+
sensor_msgs::msg::Image & image,
80+
const std::string & encoding,
81+
const std_msgs::msg::Header & header = std_msgs::msg::Header());
82+
83+
/**
84+
* @brief Convert batched Tensor to vector of sensor_msgs::msg::Image
85+
* @param tensor Tensor with shape [batch_size, height, width] or [batch_size, height, width, channels]
86+
* @param images Output vector of ROS Image messages
87+
* @param encoding Image encoding (must match tensor dtype and shape)
88+
* @param header Optional header to set timestamp and frame_id
89+
* @throws std::invalid_argument if tensor shape/dtype doesn't match encoding or batch size is 0
90+
*/
91+
void to_image(
92+
const Tensor & tensor,
93+
std::vector<sensor_msgs::msg::Image> & images,
94+
const std::string & encoding,
95+
const std_msgs::msg::Header & header = std_msgs::msg::Header());
96+
97+
} // namespace ros_conversions
98+
} // namespace deep_ros
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) 2025-present WATonomous. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
19+
#include <sensor_msgs/msg/imu.hpp>
20+
21+
#include "deep_core/plugin_interfaces/backend_memory_allocator.hpp"
22+
#include "deep_core/types/tensor.hpp"
23+
24+
namespace deep_ros
25+
{
26+
namespace ros_conversions
27+
{
28+
29+
/**
30+
* @brief Convert sensor_msgs::msg::Imu to Tensor
31+
*
32+
* Converts a ROS IMU message to a tensor format containing orientation,
33+
* linear acceleration, and angular velocity data. The output tensor has
34+
* shape [10] with the following layout:
35+
* - [0-3]: orientation quaternion (x, y, z, w)
36+
* - [4-6]: linear acceleration (x, y, z)
37+
* - [7-9]: angular velocity (x, y, z)
38+
*
39+
* @param imu ROS IMU message
40+
* @param allocator Memory allocator to use (uses CPU allocator if nullptr)
41+
* @return Tensor with shape [10] containing [qx,qy,qz,qw,ax,ay,az,gx,gy,gz]
42+
*/
43+
Tensor from_imu(const sensor_msgs::msg::Imu & imu, std::shared_ptr<BackendMemoryAllocator> allocator = nullptr);
44+
45+
} // namespace ros_conversions
46+
} // namespace deep_ros
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2025-present WATonomous. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
19+
#include <sensor_msgs/msg/laser_scan.hpp>
20+
21+
#include "deep_core/plugin_interfaces/backend_memory_allocator.hpp"
22+
#include "deep_core/types/tensor.hpp"
23+
24+
namespace deep_ros
25+
{
26+
namespace ros_conversions
27+
{
28+
29+
/**
30+
* @brief Convert sensor_msgs::msg::LaserScan to Tensor
31+
*
32+
* Converts a ROS LaserScan message to a tensor format. If intensity data is present
33+
* and matches the number of range readings, the output tensor will have shape
34+
* [num_ranges, 2] with ranges and intensities. Otherwise, it will have shape
35+
* [num_ranges] with only range data.
36+
*
37+
* @param scan ROS LaserScan message
38+
* @param allocator Memory allocator to use (uses CPU allocator if nullptr)
39+
* @return Tensor with shape [num_ranges] or [num_ranges, 2] if intensities present
40+
*/
41+
Tensor from_laserscan(
42+
const sensor_msgs::msg::LaserScan & scan, std::shared_ptr<BackendMemoryAllocator> allocator = nullptr);
43+
44+
} // namespace ros_conversions
45+
} // namespace deep_ros
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2025-present WATonomous. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
19+
#include <sensor_msgs/msg/point_cloud2.hpp>
20+
21+
#include "deep_core/plugin_interfaces/backend_memory_allocator.hpp"
22+
#include "deep_core/types/tensor.hpp"
23+
24+
namespace deep_ros
25+
{
26+
namespace ros_conversions
27+
{
28+
29+
/**
30+
* @brief Convert sensor_msgs::msg::PointCloud2 to Tensor
31+
*
32+
* Converts a ROS PointCloud2 message to a tensor format suitable for deep learning.
33+
* All field data types are converted to FLOAT32 for consistency.
34+
*
35+
* @param cloud ROS PointCloud2 message
36+
* @param allocator Memory allocator to use (uses CPU allocator if nullptr)
37+
* @return Tensor with shape [num_points, num_fields] containing point data
38+
* @throws std::invalid_argument if pointcloud has no fields
39+
*/
40+
Tensor from_pointcloud2(
41+
const sensor_msgs::msg::PointCloud2 & cloud, std::shared_ptr<BackendMemoryAllocator> allocator = nullptr);
42+
43+
} // namespace ros_conversions
44+
} // namespace deep_ros

0 commit comments

Comments
 (0)