Working ros2 version
This commit is contained in:
parent
b0b5d24c76
commit
634224960d
@ -1,18 +1,30 @@
|
||||
cmake_minimum_required(VERSION 2.8.5)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Detect if it is called by catkin
|
||||
SET(CATKIN_BUILD FALSE)
|
||||
SET(COLCON_BUILD FALSE)
|
||||
|
||||
# Detect if it is called by catkin (ros1)
|
||||
IF(CATKIN_TOPLEVEL OR CATKIN_BUILD_BINARY_PACKAGE OR CATKIN_SKIP_TESTING OR CATKIN_ENABLE_TESTING OR CATKIN_DEVEL_PREFIX)
|
||||
SET(CATKIN_BUILD TRUE)
|
||||
ENDIF(CATKIN_TOPLEVEL OR CATKIN_BUILD_BINARY_PACKAGE OR CATKIN_SKIP_TESTING OR CATKIN_ENABLE_TESTING OR CATKIN_DEVEL_PREFIX)
|
||||
ELSE()
|
||||
# Detect if it is called by colcon (ros2)
|
||||
string(FIND ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR} POS)
|
||||
IF(${POS} EQUAL -1)
|
||||
SET(COLCON_BUILD TRUE)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
MESSAGE(STATUS "CATKIN_BUILD=${CATKIN_BUILD}")
|
||||
MESSAGE(STATUS "COLCON_BUILD=${COLCON_BUILD}")
|
||||
SET(ROS_BUILD CATKIN_BUILD OR COLCON_BUILD)
|
||||
|
||||
IF(NOT CATKIN_BUILD)
|
||||
IF(NOT ROS_BUILD)
|
||||
#Standalone build
|
||||
PROJECT( Find-Object )
|
||||
ELSE()
|
||||
#ROS catkin build
|
||||
#ROS build
|
||||
PROJECT( find_object_2d )
|
||||
MESSAGE(STATUS "ROS_DISTRO=$ENV{ROS_DISTRO}")
|
||||
ENDIF()
|
||||
|
||||
# Catkin doesn't support multiarch library path,
|
||||
@ -130,7 +142,7 @@ ENDIF()
|
||||
|
||||
CONFIGURE_FILE(Version.h.in ${PROJECT_SOURCE_DIR}/include/${PROJECT_PREFIX}/Version.h)
|
||||
|
||||
IF(NOT CATKIN_BUILD)
|
||||
IF(NOT ROS_BUILD)
|
||||
#Standalone build
|
||||
IF(WIN32 AND NOT MINGW)
|
||||
ADD_DEFINITIONS("-wd4251")
|
||||
@ -358,7 +370,7 @@ IF(NOT CATKIN_BUILD)
|
||||
ENDIF(APPLE)
|
||||
MESSAGE(STATUS "--------------------------------------------")
|
||||
|
||||
ELSE()
|
||||
ELSEIF(CATKIN_BUILD)
|
||||
#ROS Catkin build
|
||||
|
||||
## Find catkin macros and libraries
|
||||
@ -405,12 +417,48 @@ ELSE()
|
||||
## Install ##
|
||||
#############
|
||||
## Mark other files for installation (e.g. launch and bag files, etc.)
|
||||
install(FILES
|
||||
launch/find_object_3d_kinect2.launch
|
||||
launch/find_object_3d_zed.launch
|
||||
launch/find_object_2d.launch
|
||||
launch/find_object_3d.launch
|
||||
install(DIRECTORY
|
||||
launch/ros1
|
||||
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/launch
|
||||
)
|
||||
ELSE() # COLCON_BUILD
|
||||
#ROS Colcon build
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(builtin_interfaces REQUIRED)
|
||||
find_package(rosidl_default_generators REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rclcpp_components REQUIRED)
|
||||
find_package(cv_bridge REQUIRED)
|
||||
find_package(sensor_msgs REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
find_package(image_transport REQUIRED)
|
||||
find_package(message_filters REQUIRED)
|
||||
find_package(tf2 REQUIRED)
|
||||
find_package(tf2_ros REQUIRED)
|
||||
find_package(geometry_msgs REQUIRED)
|
||||
|
||||
## Generate messages and services
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}
|
||||
msg/ObjectsStamped.msg
|
||||
msg/DetectionInfo.msg
|
||||
DEPENDENCIES std_msgs sensor_msgs
|
||||
)
|
||||
ament_export_dependencies(rosidl_default_runtime)
|
||||
|
||||
###########
|
||||
## Build ##
|
||||
###########
|
||||
ADD_SUBDIRECTORY( src )
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
#############
|
||||
install(DIRECTORY
|
||||
launch/ros2/.
|
||||
DESTINATION share/${PROJECT_NAME}/launch
|
||||
)
|
||||
|
||||
ament_package()
|
||||
ENDIF()
|
||||
|
||||
|
||||
48
README.md
48
README.md
@ -3,7 +3,7 @@ Linux: [ for more information.
|
||||
|
||||
## find_object_2d (ROS package)
|
||||
## find_object_2d (ROS1 package)
|
||||
|
||||
### Install
|
||||
|
||||
@ -46,3 +46,49 @@ Source:
|
||||
$ rosrun find_object_2d find_object_2d image:=image_raw
|
||||
```
|
||||
See [find_object_2d](http://wiki.ros.org/find_object_2d) for more information.
|
||||
|
||||
## find_object_2d (ROS2 package)
|
||||
|
||||
### Install
|
||||
|
||||
Binaries:
|
||||
```bash
|
||||
To come...
|
||||
```
|
||||
|
||||
Source:
|
||||
|
||||
```bash
|
||||
# Install ROS Foxy/Galactic/Humble (colcon build):
|
||||
$ cd ~/ros2_ws
|
||||
$ git clone https://github.com/introlab/find-object.git src/find_object_2d
|
||||
$ colcon build
|
||||
```
|
||||
|
||||
### Run
|
||||
```bash
|
||||
# Launch your preferred usb camera driver
|
||||
[...]
|
||||
|
||||
# Launch find_object_2d node:
|
||||
$ ros2 launch find_object_2d find_object_2d.launch.py image:=/camera/color/image_raw
|
||||
|
||||
# Draw objects detected on an image:
|
||||
$ ros2 run find_object_2d print_objects_detected --ros-args -r image:=/camera/color/image_raw
|
||||
```
|
||||
#### 3D Pose (TF)
|
||||
A RGB-D camera is required. Example with Realsense D400 camera:
|
||||
```bash
|
||||
# Launch your preferred usb camera driver
|
||||
ros2 launch realsense2_camera rs_launch.py align_depth.enable:=true
|
||||
|
||||
# Launch find_object_2d node:
|
||||
$ ros2 launch find_object_2d find_object_3d.launch.py \
|
||||
rgb_topic:=/camera/color/image_raw \
|
||||
depth_topic:=/camera/aligned_depth_to_color/image_raw \
|
||||
camera_info_topic:=/camera/color/camera_info
|
||||
|
||||
# Show 3D pose in camera frame:
|
||||
$ ros2 run find_object_2d tf_example
|
||||
```
|
||||
|
||||
|
||||
30
launch/ros2/find_object_2d.launch.py
Normal file
30
launch/ros2/find_object_2d.launch.py
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
def generate_launch_description():
|
||||
|
||||
return LaunchDescription([
|
||||
|
||||
SetEnvironmentVariable('RCUTILS_LOGGING_USE_STDOUT', '1'),
|
||||
SetEnvironmentVariable('RCUTILS_LOGGING_BUFFERED_STREAM', '0'),
|
||||
|
||||
# Launch arguments
|
||||
DeclareLaunchArgument('gui', default_value='true', description='Launch GUI.'),
|
||||
DeclareLaunchArgument('image_topic', default_value='image', description='Image topic to subscribe to.'),
|
||||
DeclareLaunchArgument('objects_path', default_value='~/objects', description='Directory containing objects to load on initialization.'),
|
||||
DeclareLaunchArgument('settings_path', default_value='~/.ros/find_object_2d.ini', description='Config file.'),
|
||||
|
||||
# Nodes to launch
|
||||
Node(
|
||||
package='find_object_2d', executable='find_object_2d', output='screen',
|
||||
parameters=[{
|
||||
'gui':LaunchConfiguration('gui'),
|
||||
'objects_path':LaunchConfiguration('objects_path'),
|
||||
'settings_path':LaunchConfiguration('settings_path')
|
||||
}],
|
||||
remappings=[
|
||||
('image', LaunchConfiguration('image_topic'))]),
|
||||
])
|
||||
42
launch/ros2/find_object_3d.launch.py
Normal file
42
launch/ros2/find_object_3d.launch.py
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, SetEnvironmentVariable
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import Node
|
||||
|
||||
def generate_launch_description():
|
||||
|
||||
return LaunchDescription([
|
||||
|
||||
SetEnvironmentVariable('RCUTILS_LOGGING_USE_STDOUT', '1'),
|
||||
SetEnvironmentVariable('RCUTILS_LOGGING_BUFFERED_STREAM', '0'),
|
||||
|
||||
# Launch arguments
|
||||
DeclareLaunchArgument('gui', default_value='true', description='Launch GUI.'),
|
||||
DeclareLaunchArgument('approx_sync', default_value='true', description=''),
|
||||
DeclareLaunchArgument('pnp', default_value='true', description=''),
|
||||
DeclareLaunchArgument('object_prefix', default_value='object', description='TF prefix of objects.'),
|
||||
DeclareLaunchArgument('objects_path', default_value='~/objects', description='Directory containing objects to load on initialization.'),
|
||||
DeclareLaunchArgument('settings_path', default_value='~/.ros/find_object_2d.ini', description='Config file.'),
|
||||
|
||||
DeclareLaunchArgument('rgb_topic', default_value='camera/rgb/image_rect_color', description='Image topic.'),
|
||||
DeclareLaunchArgument('depth_topic', default_value='camera/depth_registered/image_raw', description='Registered depth topic.'),
|
||||
DeclareLaunchArgument('camera_info_topic', default_value='camera/rgb/camera_info', description='Camera info topic.'),
|
||||
|
||||
# Nodes to launch
|
||||
Node(
|
||||
package='find_object_2d', executable='find_object_2d', output='screen',
|
||||
parameters=[{
|
||||
'subscribe_depth':True,
|
||||
'gui':LaunchConfiguration('gui'),
|
||||
'approx_sync':LaunchConfiguration('approx_sync'),
|
||||
'pnp':LaunchConfiguration('pnp'),
|
||||
'object_prefix':LaunchConfiguration('object_prefix'),
|
||||
'objects_path':LaunchConfiguration('objects_path'),
|
||||
'settings_path':LaunchConfiguration('settings_path')
|
||||
}],
|
||||
remappings=[
|
||||
('rgb/image_rect_color', LaunchConfiguration('rgb_topic')),
|
||||
('depth_registered/image_raw', LaunchConfiguration('depth_topic')),
|
||||
('depth_registered/camera_info', LaunchConfiguration('camera_info_topic'))]),
|
||||
])
|
||||
@ -1,11 +1,11 @@
|
||||
|
||||
Header header
|
||||
std_msgs/Header header
|
||||
|
||||
# All arrays should have the same size
|
||||
std_msgs/Int32[] ids
|
||||
std_msgs/Int32[] widths
|
||||
std_msgs/Int32[] heights
|
||||
std_msgs/String[] filePaths
|
||||
std_msgs/String[] file_paths
|
||||
std_msgs/Int32[] inliers
|
||||
std_msgs/Int32[] outliers
|
||||
# 3x3 homography matrix: [h11, h12, h13, h21, h22, h23, h31, h32, h33] (h31 = dx and h32 = dy, see QTransform)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# objects format:
|
||||
# [ObjectId1, objectWidth, objectHeight, h11, h12, h13, h21, h22, h23, h31, h32, h33, ObjectId2...]
|
||||
# where h## is a 3x3 homography matrix (h31 = dx and h32 = dy, see QTransform)
|
||||
Header header
|
||||
std_msgs/Header header
|
||||
std_msgs/Float32MultiArray objects
|
||||
53
package.xml
53
package.xml
@ -1,38 +1,51 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="3">
|
||||
<name>find_object_2d</name>
|
||||
<version>0.6.4</version>
|
||||
<description>The find_object_2d package</description>
|
||||
|
||||
<maintainer email="matlabbe@gmail.com">Mathieu Labbe</maintainer>
|
||||
<author>Mathieu Labbe</author>
|
||||
<license>BSD</license>
|
||||
<url type="website">http://find-object.googlecode.com</url>
|
||||
<author email="matlabbe@gmail.com">Mathieu Labbe</author>
|
||||
<url type="bugtracker">https://github.com/introlab/find-object/issues</url>
|
||||
<url type="repository">https://github.com/introlab/find-object</url>
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
<buildtool_depend condition="$ROS_VERSION == 1">catkin</buildtool_depend>
|
||||
<buildtool_depend condition="$ROS_VERSION == 2">ament_cmake</buildtool_depend>
|
||||
|
||||
<depend condition="$ROS_VERSION == 1">roscpp</depend>
|
||||
<depend condition="$ROS_VERSION == 1">tf</depend>
|
||||
<depend condition="$ROS_VERSION == 2">rclcpp</depend>
|
||||
<depend condition="$ROS_VERSION == 2">tf2</depend>
|
||||
<depend condition="$ROS_VERSION == 2">tf2_ros</depend>
|
||||
|
||||
<build_depend condition="$ROS_VERSION == 2">ros_environment</build_depend>
|
||||
<depend condition="$ROS_VERSION == 2">builtin_interfaces</depend>
|
||||
<build_depend condition="$ROS_VERSION == 2">rosidl_default_generators</build_depend>
|
||||
<exec_depend condition="$ROS_VERSION == 2">rosidl_default_runtime</exec_depend>
|
||||
<member_of_group condition="$ROS_VERSION == 2">rosidl_interface_packages</member_of_group>
|
||||
|
||||
<build_depend>message_generation</build_depend>
|
||||
<build_depend condition="$ROS_VERSION == 1">message_generation</build_depend>
|
||||
<build_depend>qtbase5-dev</build_depend>
|
||||
<build_depend>cv_bridge</build_depend>
|
||||
<build_depend>roscpp</build_depend>
|
||||
<build_depend>rospy</build_depend>
|
||||
<build_depend>sensor_msgs</build_depend>
|
||||
<build_depend>std_msgs</build_depend>
|
||||
<build_depend>std_srvs</build_depend>
|
||||
<build_depend>geometry_msgs</build_depend>
|
||||
<build_depend>image_transport</build_depend>
|
||||
<build_depend>message_filters</build_depend>
|
||||
<build_depend>tf</build_depend>
|
||||
|
||||
<run_depend>message_runtime</run_depend>
|
||||
<run_depend>qtbase5-dev</run_depend>
|
||||
<run_depend>cv_bridge</run_depend>
|
||||
<run_depend>roscpp</run_depend>
|
||||
<run_depend>rospy</run_depend>
|
||||
<run_depend>sensor_msgs</run_depend>
|
||||
<run_depend>std_msgs</run_depend>
|
||||
<run_depend>std_srvs</run_depend>
|
||||
<run_depend>image_transport</run_depend>
|
||||
<run_depend>message_filters</run_depend>
|
||||
<run_depend>tf</run_depend>
|
||||
<exec_depend condition="$ROS_VERSION == 1">message_runtime</exec_depend>
|
||||
<exec_depend>qtbase5-dev</exec_depend>
|
||||
<exec_depend>cv_bridge</exec_depend>
|
||||
<exec_depend>sensor_msgs</exec_depend>
|
||||
<exec_depend>std_msgs</exec_depend>
|
||||
<exec_depend>std_srvs</exec_depend>
|
||||
<exec_depend>geometry_msgs</exec_depend>
|
||||
<exec_depend>image_transport</exec_depend>
|
||||
<exec_depend>message_filters</exec_depend>
|
||||
|
||||
<export>
|
||||
<build_type condition="$ROS_VERSION == 2">ament_cmake</build_type>
|
||||
</export>
|
||||
</package>
|
||||
|
||||
@ -19,10 +19,17 @@ SET(headers_ui
|
||||
IF(CATKIN_BUILD)
|
||||
SET(headers_ui
|
||||
${headers_ui}
|
||||
./ros/CameraROS.h
|
||||
./ros/FindObjectROS.h
|
||||
./ros1/CameraROS.h
|
||||
./ros1/FindObjectROS.h
|
||||
)
|
||||
ENDIF(CATKIN_BUILD)
|
||||
IF(COLCON_BUILD)
|
||||
SET(headers_ui
|
||||
${headers_ui}
|
||||
./ros2/CameraROS.h
|
||||
./ros2/FindObjectROS.h
|
||||
)
|
||||
ENDIF(COLCON_BUILD)
|
||||
|
||||
SET(uis
|
||||
./ui/mainWindow.ui
|
||||
@ -82,12 +89,17 @@ SET(SRC_FILES
|
||||
IF(CATKIN_BUILD)
|
||||
SET(SRC_FILES
|
||||
${SRC_FILES}
|
||||
./ros/CameraROS.cpp
|
||||
./ros/FindObjectROS.cpp
|
||||
./ros1/CameraROS.cpp
|
||||
./ros1/FindObjectROS.cpp
|
||||
)
|
||||
ENDIF(CATKIN_BUILD)
|
||||
|
||||
|
||||
IF(COLCON_BUILD)
|
||||
SET(SRC_FILES
|
||||
${SRC_FILES}
|
||||
./ros2/CameraROS.cpp
|
||||
./ros2/FindObjectROS.cpp
|
||||
)
|
||||
ENDIF(COLCON_BUILD)
|
||||
|
||||
SET(INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
||||
@ -112,6 +124,21 @@ IF(CATKIN_BUILD)
|
||||
${catkin_LIBRARIES}
|
||||
)
|
||||
ENDIF(CATKIN_BUILD)
|
||||
IF(COLCON_BUILD)
|
||||
SET(AMENT_LIBRARIES
|
||||
rclcpp
|
||||
rclcpp_components
|
||||
cv_bridge
|
||||
sensor_msgs
|
||||
std_msgs
|
||||
image_transport
|
||||
message_filters
|
||||
tf2
|
||||
tf2_ros
|
||||
geometry_msgs
|
||||
)
|
||||
ENDIF(COLCON_BUILD)
|
||||
|
||||
|
||||
IF(TORCH_FOUND)
|
||||
SET(LIBRARIES
|
||||
@ -141,31 +168,51 @@ ENDIF(CATKIN_BUILD)
|
||||
# create a library from the source files
|
||||
ADD_LIBRARY(find_object ${SRC_FILES})
|
||||
# Linking with Qt libraries
|
||||
TARGET_LINK_LIBRARIES(find_object ${LIBRARIES})
|
||||
IF(Qt5_FOUND)
|
||||
QT5_USE_MODULES(find_object Widgets Core Gui Network PrintSupport)
|
||||
ENDIF(Qt5_FOUND)
|
||||
|
||||
IF(NOT COLCON_BUILD)
|
||||
TARGET_LINK_LIBRARIES(find_object ${LIBRARIES})
|
||||
ENDIF()
|
||||
IF(CATKIN_BUILD)
|
||||
set_target_properties(find_object PROPERTIES OUTPUT_NAME find_object_2d)
|
||||
add_dependencies(find_object ${${PROJECT_NAME}_EXPORTED_TARGETS})
|
||||
ENDIF(CATKIN_BUILD)
|
||||
IF(COLCON_BUILD)
|
||||
ament_target_dependencies(find_object ${AMENT_LIBRARIES})
|
||||
|
||||
if("$ENV{ROS_DISTRO}" STRGREATER_EQUAL "humble")
|
||||
rosidl_get_typesupport_target(cpp_typesupport_target ${PROJECT_NAME} "rosidl_typesupport_cpp")
|
||||
target_link_libraries(find_object ${cpp_typesupport_target} ${LIBRARIES})
|
||||
else()
|
||||
# foxy, galatic
|
||||
target_link_libraries(find_object ${LIBRARIES})
|
||||
rosidl_target_interfaces(find_object ${PROJECT_NAME} "rosidl_typesupport_cpp")
|
||||
|
||||
IF(NOT CATKIN_BUILD)
|
||||
function(rosidl_get_typesupport_target var generate_interfaces_target typesupport_name)
|
||||
rosidl_target_interfaces(${var} ${generate_interfaces_target} ${typesupport_name})
|
||||
endfunction()
|
||||
add_definitions(-DPRE_ROS_HUMBLE)
|
||||
endif()
|
||||
ENDIF(COLCON_BUILD)
|
||||
|
||||
IF(NOT ROS_BUILD)
|
||||
INSTALL(TARGETS find_object
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/ DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT devel FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE)
|
||||
ELSE()
|
||||
add_executable(find_object_2d ros/find_object_2d_node.cpp)
|
||||
ELSEIF(CATKIN_BUILD)
|
||||
add_executable(find_object_2d ros1/find_object_2d_node.cpp)
|
||||
target_link_libraries(find_object_2d find_object ${LIBRARIES})
|
||||
|
||||
add_executable(print_objects_detected ros/print_objects_detected_node.cpp)
|
||||
add_executable(print_objects_detected ros1/print_objects_detected_node.cpp)
|
||||
target_link_libraries(print_objects_detected ${LIBRARIES})
|
||||
add_dependencies(print_objects_detected ${${PROJECT_NAME}_EXPORTED_TARGETS})
|
||||
|
||||
add_executable(tf_example ros/tf_example_node.cpp)
|
||||
add_executable(tf_example ros1/tf_example_node.cpp)
|
||||
target_link_libraries(tf_example ${LIBRARIES})
|
||||
add_dependencies(tf_example ${${PROJECT_NAME}_EXPORTED_TARGETS})
|
||||
|
||||
@ -185,4 +232,49 @@ ELSE()
|
||||
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
|
||||
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
ELSE() # COLCON_BUILD
|
||||
add_executable(find_object_2d_node ros2/find_object_2d_node.cpp)
|
||||
set_target_properties(find_object_2d_node PROPERTIES OUTPUT_NAME find_object_2d)
|
||||
ament_target_dependencies(find_object_2d_node ${AMENT_LIBRARIES})
|
||||
target_link_libraries(find_object_2d_node find_object ${LIBRARIES})
|
||||
|
||||
add_executable(print_objects_detected ros2/print_objects_detected_node.cpp)
|
||||
ament_target_dependencies(print_objects_detected ${AMENT_LIBRARIES})
|
||||
target_link_libraries(print_objects_detected find_object ${LIBRARIES})
|
||||
|
||||
add_executable(tf_example ros2/tf_example_node.cpp)
|
||||
ament_target_dependencies(tf_example ${AMENT_LIBRARIES})
|
||||
target_link_libraries(tf_example find_object ${LIBRARIES})
|
||||
|
||||
# Only required when using messages built from the same package
|
||||
# https://index.ros.org/doc/ros2/Tutorials/Rosidl-Tutorial/
|
||||
get_default_rmw_implementation(rmw_implementation)
|
||||
find_package("${rmw_implementation}" REQUIRED)
|
||||
get_rmw_typesupport(typesupport_impls "${rmw_implementation}" LANGUAGE "cpp")
|
||||
|
||||
foreach(typesupport_impl ${typesupport_impls})
|
||||
rosidl_get_typesupport_target(find_object_2d_node
|
||||
${PROJECT_NAME} ${typesupport_impl}
|
||||
)
|
||||
rosidl_get_typesupport_target(print_objects_detected
|
||||
${PROJECT_NAME} ${typesupport_impl}
|
||||
)
|
||||
rosidl_get_typesupport_target(tf_example
|
||||
${PROJECT_NAME} ${typesupport_impl}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
## Mark executables and/or libraries for installation
|
||||
install(TARGETS
|
||||
find_object
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
install(TARGETS
|
||||
find_object_2d_node
|
||||
print_objects_detected
|
||||
tf_example
|
||||
DESTINATION lib/${PROJECT_NAME}
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
@ -25,10 +25,10 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "CameraROS.h"
|
||||
#include "find_object/Settings.h"
|
||||
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include "CameraROS.h"
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
|
||||
using namespace find_object;
|
||||
@ -26,7 +26,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FindObjectROS.h"
|
||||
|
||||
#include <std_msgs/Float32MultiArray.h>
|
||||
#include "find_object_2d/ObjectsStamped.h"
|
||||
#include "find_object_2d/DetectionInfo.h"
|
||||
@ -25,9 +25,8 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "CameraROS.h"
|
||||
#include "FindObjectROS.h"
|
||||
|
||||
#include <ros1/CameraROS.h>
|
||||
#include <ros1/FindObjectROS.h>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include "find_object/MainWindow.h"
|
||||
188
src/ros2/CameraROS.cpp
Normal file
188
src/ros2/CameraROS.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "CameraROS.h"
|
||||
#include "find_object/Settings.h"
|
||||
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <sensor_msgs/image_encodings.hpp>
|
||||
|
||||
using namespace find_object;
|
||||
|
||||
CameraROS::CameraROS(bool subscribeDepth, rclcpp::Node * node) :
|
||||
node_(node),
|
||||
subscribeDepth_(subscribeDepth),
|
||||
approxSync_(0),
|
||||
exactSync_(0)
|
||||
{
|
||||
qRegisterMetaType<rclcpp::Time>("ros::Time");
|
||||
qRegisterMetaType<cv::Mat>("cv::Mat");
|
||||
|
||||
if(!subscribeDepth_)
|
||||
{
|
||||
image_transport::TransportHints hints(node);
|
||||
imageSub_ = image_transport::create_subscription(node, "image", std::bind(&CameraROS::imgReceivedCallback, this, std::placeholders::_1), hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
}
|
||||
else
|
||||
{
|
||||
int queueSize = 10;
|
||||
bool approxSync = true;
|
||||
queueSize = node->declare_parameter("queue_size", queueSize);
|
||||
approxSync = node->declare_parameter("approx_sync", approxSync);
|
||||
RCLCPP_INFO(node->get_logger(), "find_object_ros: queue_size = %d", queueSize);
|
||||
RCLCPP_INFO(node->get_logger(), "find_object_ros: approx_sync = %s", approxSync?"true":"false");
|
||||
|
||||
|
||||
image_transport::TransportHints hints(node);
|
||||
rgbSub_.subscribe(node, "rgb/image_rect_color", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
depthSub_.subscribe(node, "depth_registered/image_raw", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
cameraInfoSub_.subscribe(node, "depth_registered/camera_info", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
|
||||
if(approxSync)
|
||||
{
|
||||
approxSync_ = new message_filters::Synchronizer<MyApproxSyncPolicy>(MyApproxSyncPolicy(queueSize), rgbSub_, depthSub_, cameraInfoSub_);
|
||||
approxSync_->registerCallback(std::bind(&CameraROS::imgDepthReceivedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
else
|
||||
{
|
||||
exactSync_ = new message_filters::Synchronizer<MyExactSyncPolicy>(MyExactSyncPolicy(queueSize), rgbSub_, depthSub_, cameraInfoSub_);
|
||||
exactSync_->registerCallback(std::bind(&CameraROS::imgDepthReceivedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
}
|
||||
}
|
||||
CameraROS::~CameraROS()
|
||||
{
|
||||
delete approxSync_;
|
||||
delete exactSync_;
|
||||
}
|
||||
|
||||
void CameraROS::setupExecutor(std::shared_ptr<rclcpp::Node> node)
|
||||
{
|
||||
executor_.add_node(node);
|
||||
}
|
||||
|
||||
QStringList CameraROS::subscribedTopics() const
|
||||
{
|
||||
QStringList topics;
|
||||
if(subscribeDepth_)
|
||||
{
|
||||
topics.append(rgbSub_.getTopic().c_str());
|
||||
topics.append(depthSub_.getTopic().c_str());
|
||||
topics.append(cameraInfoSub_.getSubscriber()->get_topic_name());
|
||||
}
|
||||
else
|
||||
{
|
||||
topics.append(imageSub_.getTopic().c_str());
|
||||
}
|
||||
return topics;
|
||||
}
|
||||
|
||||
bool CameraROS::start()
|
||||
{
|
||||
this->startTimer();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CameraROS::stop()
|
||||
{
|
||||
this->stopTimer();
|
||||
}
|
||||
|
||||
void CameraROS::takeImage()
|
||||
{
|
||||
executor_.spin_some();
|
||||
}
|
||||
|
||||
void CameraROS::imgReceivedCallback(const sensor_msgs::msg::Image::ConstSharedPtr msg)
|
||||
{
|
||||
if(msg->data.size())
|
||||
{
|
||||
cv::Mat image;
|
||||
cv_bridge::CvImageConstPtr imgPtr = cv_bridge::toCvShare(msg);
|
||||
try
|
||||
{
|
||||
if(msg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
msg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
image = cv_bridge::cvtColor(imgPtr, "mono8")->image;
|
||||
}
|
||||
else
|
||||
{
|
||||
image = cv_bridge::cvtColor(imgPtr, "bgr8")->image;
|
||||
}
|
||||
|
||||
Q_EMIT imageReceived(image, Header(msg->header.frame_id.c_str(), msg->header.stamp.sec, msg->header.stamp.nanosec), cv::Mat(), 0.0f);
|
||||
}
|
||||
catch(const cv_bridge::Exception & e)
|
||||
{
|
||||
RCLCPP_ERROR(node_->get_logger(), "find_object_ros: Could not convert input image to mono8 or bgr8 format, encoding detected is %s... cv_bridge exception: %s", msg->encoding.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CameraROS::imgDepthReceivedCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr rgbMsg,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr depthMsg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoMsg)
|
||||
{
|
||||
if(depthMsg->encoding.compare(sensor_msgs::image_encodings::TYPE_16UC1)!=0 &&
|
||||
depthMsg->encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1)!=0)
|
||||
{
|
||||
RCLCPP_ERROR(node_->get_logger(), "find_object_ros: Depth image type must be 32FC1 or 16UC1");
|
||||
return;
|
||||
}
|
||||
|
||||
if(rgbMsg->data.size())
|
||||
{
|
||||
cv_bridge::CvImageConstPtr ptr = cv_bridge::toCvShare(rgbMsg);
|
||||
cv_bridge::CvImageConstPtr ptrDepth = cv_bridge::toCvShare(depthMsg);
|
||||
float depthConstant = 1.0f/cameraInfoMsg->k[4];
|
||||
|
||||
cv::Mat image;
|
||||
cv_bridge::CvImageConstPtr imgPtr = cv_bridge::toCvShare(rgbMsg);
|
||||
try
|
||||
{
|
||||
if(rgbMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
rgbMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
image = cv_bridge::cvtColor(imgPtr, "mono8")->image;
|
||||
}
|
||||
else
|
||||
{
|
||||
image = cv_bridge::cvtColor(imgPtr, "bgr8")->image;
|
||||
}
|
||||
|
||||
Q_EMIT imageReceived(image, Header(rgbMsg->header.frame_id.c_str(), rgbMsg->header.stamp.sec, rgbMsg->header.stamp.nanosec), ptrDepth->image, depthConstant);
|
||||
}
|
||||
catch(const cv_bridge::Exception & e)
|
||||
{
|
||||
RCLCPP_ERROR(node_->get_logger(), "find_object_ros: Could not convert input image to mono8 or bgr8 format, encoding detected is %s... cv_bridge exception: %s", rgbMsg->encoding.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
93
src/ros2/CameraROS.h
Normal file
93
src/ros2/CameraROS.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef CAMERAROS_H_
|
||||
#define CAMERAROS_H_
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
|
||||
#include <message_filters/subscriber.h>
|
||||
#include <message_filters/synchronizer.h>
|
||||
#include <message_filters/sync_policies/approximate_time.h>
|
||||
#include <message_filters/sync_policies/exact_time.h>
|
||||
|
||||
#include <image_transport/image_transport.hpp>
|
||||
#include <image_transport/subscriber_filter.hpp>
|
||||
|
||||
#include <sensor_msgs/msg/camera_info.hpp>
|
||||
#include <sensor_msgs/msg/image.hpp>
|
||||
|
||||
#include "find_object/Camera.h"
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
class CameraROS : public find_object::Camera {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CameraROS(bool subscribeDepth, rclcpp::Node * node);
|
||||
virtual ~CameraROS();
|
||||
void setupExecutor(std::shared_ptr<rclcpp::Node> node);
|
||||
|
||||
virtual bool start();
|
||||
virtual void stop();
|
||||
|
||||
QStringList subscribedTopics() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
virtual void takeImage();
|
||||
|
||||
private:
|
||||
void imgReceivedCallback(const sensor_msgs::msg::Image::ConstSharedPtr msg);
|
||||
void imgDepthReceivedCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr rgbMsg,
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr depthMsg,
|
||||
const sensor_msgs::msg::CameraInfo::ConstSharedPtr cameraInfoMsg);
|
||||
|
||||
private:
|
||||
rclcpp::Node * node_;
|
||||
rclcpp::executors::SingleThreadedExecutor executor_;
|
||||
bool subscribeDepth_;
|
||||
image_transport::Subscriber imageSub_;
|
||||
|
||||
image_transport::SubscriberFilter rgbSub_;
|
||||
image_transport::SubscriberFilter depthSub_;
|
||||
message_filters::Subscriber<sensor_msgs::msg::CameraInfo> cameraInfoSub_;
|
||||
|
||||
typedef message_filters::sync_policies::ApproximateTime<
|
||||
sensor_msgs::msg::Image,
|
||||
sensor_msgs::msg::Image,
|
||||
sensor_msgs::msg::CameraInfo> MyApproxSyncPolicy;
|
||||
message_filters::Synchronizer<MyApproxSyncPolicy> * approxSync_;
|
||||
|
||||
typedef message_filters::sync_policies::ExactTime<
|
||||
sensor_msgs::msg::Image,
|
||||
sensor_msgs::msg::Image,
|
||||
sensor_msgs::msg::CameraInfo> MyExactSyncPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncPolicy> * exactSync_;
|
||||
};
|
||||
|
||||
#endif /* CAMERAROS_H_ */
|
||||
362
src/ros2/FindObjectROS.cpp
Normal file
362
src/ros2/FindObjectROS.cpp
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FindObjectROS.h"
|
||||
|
||||
#include <geometry_msgs/msg/transform_stamped.hpp>
|
||||
#include <tf2/LinearMath/Quaternion.h>
|
||||
#include <tf2/LinearMath/Vector3.h>
|
||||
#include <tf2/LinearMath/Matrix3x3.h>
|
||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace find_object;
|
||||
|
||||
FindObjectROS::FindObjectROS(rclcpp::Node * node) :
|
||||
FindObject(true),
|
||||
node_(node),
|
||||
objFramePrefix_("object"),
|
||||
usePnP_(true)
|
||||
{
|
||||
tfBroadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(node);
|
||||
|
||||
objFramePrefix_ = node->declare_parameter("object_prefix", objFramePrefix_);
|
||||
usePnP_ = node->declare_parameter("pnp", usePnP_);
|
||||
RCLCPP_INFO(node->get_logger(), "object_prefix = %s", objFramePrefix_.c_str());
|
||||
RCLCPP_INFO(node->get_logger(), "pnp = %s", usePnP_?"true":"false");
|
||||
|
||||
pub_ = node->create_publisher<std_msgs::msg::Float32MultiArray>("objects", rclcpp::QoS(1).reliability(rclcpp::ReliabilityPolicy::Reliable));
|
||||
pubStamped_ = node->create_publisher<find_object_2d::msg::ObjectsStamped>("objectsStamped", rclcpp::QoS(1).reliability(rclcpp::ReliabilityPolicy::Reliable));
|
||||
pubInfo_ = node->create_publisher<find_object_2d::msg::DetectionInfo>("info", rclcpp::QoS(1).reliability(rclcpp::ReliabilityPolicy::Reliable));
|
||||
|
||||
this->connect(this, SIGNAL(objectsFound(const find_object::DetectionInfo &, const find_object::Header &, const cv::Mat &, float)), this, SLOT(publish(const find_object::DetectionInfo &, const find_object::Header &, const cv::Mat &, float)));
|
||||
}
|
||||
|
||||
void FindObjectROS::publish(const find_object::DetectionInfo & info, const Header & header, const cv::Mat & depth, float depthConstant)
|
||||
{
|
||||
// send tf before the message
|
||||
if(info.objDetected_.size() && !depth.empty() && depthConstant != 0.0f)
|
||||
{
|
||||
std::vector<geometry_msgs::msg::TransformStamped> transforms;
|
||||
char multiSubId = 'b';
|
||||
int previousId = -1;
|
||||
QMultiMap<int, QSize>::const_iterator iterSizes=info.objDetectedSizes_.constBegin();
|
||||
for(QMultiMap<int, QTransform>::const_iterator iter=info.objDetected_.constBegin();
|
||||
iter!=info.objDetected_.constEnd();
|
||||
++iter, ++iterSizes)
|
||||
{
|
||||
// get data
|
||||
int id = iter.key();
|
||||
float objectWidth = iterSizes->width();
|
||||
float objectHeight = iterSizes->height();
|
||||
|
||||
QString multiSuffix;
|
||||
if(id == previousId)
|
||||
{
|
||||
multiSuffix = QString("_") + multiSubId++;
|
||||
}
|
||||
else
|
||||
{
|
||||
multiSubId = 'b';
|
||||
}
|
||||
previousId = id;
|
||||
|
||||
// Find center of the object
|
||||
QPointF center = iter->map(QPointF(objectWidth/2, objectHeight/2));
|
||||
cv::Vec3f center3D = this->getDepth(depth,
|
||||
center.x()+0.5f, center.y()+0.5f,
|
||||
float(depth.cols/2)-0.5f, float(depth.rows/2)-0.5f,
|
||||
1.0f/depthConstant, 1.0f/depthConstant);
|
||||
|
||||
cv::Vec3f axisEndX;
|
||||
cv::Vec3f axisEndY;
|
||||
if(!usePnP_)
|
||||
{
|
||||
QPointF xAxis = iter->map(QPointF(3*objectWidth/4, objectHeight/2));
|
||||
axisEndX = this->getDepth(depth,
|
||||
xAxis.x()+0.5f, xAxis.y()+0.5f,
|
||||
float(depth.cols/2)-0.5f, float(depth.rows/2)-0.5f,
|
||||
1.0f/depthConstant, 1.0f/depthConstant);
|
||||
|
||||
QPointF yAxis = iter->map(QPointF(objectWidth/2, 3*objectHeight/4));
|
||||
axisEndY = this->getDepth(depth,
|
||||
yAxis.x()+0.5f, yAxis.y()+0.5f,
|
||||
float(depth.cols/2)-0.5f, float(depth.rows/2)-0.5f,
|
||||
1.0f/depthConstant, 1.0f/depthConstant);
|
||||
}
|
||||
|
||||
if((std::isfinite(center3D.val[2]) && usePnP_) ||
|
||||
(std::isfinite(center3D.val[0]) && std::isfinite(center3D.val[1]) && std::isfinite(center3D.val[2]) &&
|
||||
std::isfinite(axisEndX.val[0]) && std::isfinite(axisEndX.val[1]) && std::isfinite(axisEndX.val[2]) &&
|
||||
std::isfinite(axisEndY.val[0]) && std::isfinite(axisEndY.val[1]) && std::isfinite(axisEndY.val[2])))
|
||||
{
|
||||
geometry_msgs::msg::TransformStamped transform;
|
||||
transform.transform.rotation.x=0;
|
||||
transform.transform.rotation.y=0;
|
||||
transform.transform.rotation.z=0;
|
||||
transform.transform.rotation.w=1;
|
||||
transform.transform.translation.x=0;
|
||||
transform.transform.translation.y=0;
|
||||
transform.transform.translation.z=0;
|
||||
transform.child_frame_id = QString("%1_%2%3").arg(objFramePrefix_.c_str()).arg(id).arg(multiSuffix).toStdString();
|
||||
transform.header.frame_id = header.frameId_.toStdString();
|
||||
transform.header.stamp.sec = header.sec_;
|
||||
transform.header.stamp.nanosec = header.nsec_;
|
||||
|
||||
tf2::Quaternion q;
|
||||
if(usePnP_)
|
||||
{
|
||||
std::vector<cv::Point3f> objectPoints(4);
|
||||
std::vector<cv::Point2f> imagePoints(4);
|
||||
|
||||
objectPoints[0] = cv::Point3f(-0.5, -(objectHeight/objectWidth)/2.0f,0);
|
||||
objectPoints[1] = cv::Point3f(0.5,-(objectHeight/objectWidth)/2.0f,0);
|
||||
objectPoints[2] = cv::Point3f(0.5,(objectHeight/objectWidth)/2.0f,0);
|
||||
objectPoints[3] = cv::Point3f(-0.5,(objectHeight/objectWidth)/2.0f,0);
|
||||
|
||||
QPointF pt = iter->map(QPointF(0, 0));
|
||||
imagePoints[0] = cv::Point2f(pt.x(), pt.y());
|
||||
pt = iter->map(QPointF(objectWidth, 0));
|
||||
imagePoints[1] = cv::Point2f(pt.x(), pt.y());
|
||||
pt = iter->map(QPointF(objectWidth, objectHeight));
|
||||
imagePoints[2] = cv::Point2f(pt.x(), pt.y());
|
||||
pt = iter->map(QPointF(0, objectHeight));
|
||||
imagePoints[3] = cv::Point2f(pt.x(), pt.y());
|
||||
|
||||
cv::Mat cameraMatrix = cv::Mat::eye(3,3,CV_64FC1);
|
||||
cameraMatrix.at<double>(0,0) = 1.0f/depthConstant;
|
||||
cameraMatrix.at<double>(1,1) = 1.0f/depthConstant;
|
||||
cameraMatrix.at<double>(0,2) = float(depth.cols/2)-0.5f;
|
||||
cameraMatrix.at<double>(1,2) = float(depth.rows/2)-0.5f;
|
||||
cv::Mat distCoeffs;
|
||||
|
||||
cv::Mat rvec(1,3, CV_64FC1);
|
||||
cv::Mat tvec(1,3, CV_64FC1);
|
||||
cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
|
||||
|
||||
cv::Mat R;
|
||||
cv::Rodrigues(rvec, R);
|
||||
tf2::Matrix3x3 rotationMatrix(
|
||||
R.at<double>(0,0), R.at<double>(0,1), R.at<double>(0,2),
|
||||
R.at<double>(1,0), R.at<double>(1,1), R.at<double>(1,2),
|
||||
R.at<double>(2,0), R.at<double>(2,1), R.at<double>(2,2));
|
||||
rotationMatrix.getRotation(q);
|
||||
transform.transform.translation.x = tvec.at<double>(0)*(center3D.val[2]/tvec.at<double>(2));
|
||||
transform.transform.translation.y = tvec.at<double>(1)*(center3D.val[2]/tvec.at<double>(2));
|
||||
transform.transform.translation.z = tvec.at<double>(2)*(center3D.val[2]/tvec.at<double>(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
tf2::Vector3 xAxis(axisEndX.val[0] - center3D.val[0], axisEndX.val[1] - center3D.val[1], axisEndX.val[2] - center3D.val[2]);
|
||||
xAxis.normalize();
|
||||
tf2::Vector3 yAxis(axisEndY.val[0] - center3D.val[0], axisEndY.val[1] - center3D.val[1], axisEndY.val[2] - center3D.val[2]);
|
||||
yAxis.normalize();
|
||||
tf2::Vector3 zAxis = xAxis.cross(yAxis);
|
||||
zAxis.normalize();
|
||||
tf2::Matrix3x3 rotationMatrix(
|
||||
xAxis.x(), yAxis.x() ,zAxis.x(),
|
||||
xAxis.y(), yAxis.y(), zAxis.y(),
|
||||
xAxis.z(), yAxis.z(), zAxis.z());
|
||||
rotationMatrix.getRotation(q);
|
||||
transform.transform.translation.x = center3D.val[0];
|
||||
transform.transform.translation.y = center3D.val[1];
|
||||
transform.transform.translation.z = center3D.val[2];
|
||||
}
|
||||
|
||||
// set x axis going front of the object, with z up and y left
|
||||
tf2::Quaternion q2;
|
||||
q2.setRPY(CV_PI/2.0, CV_PI/2.0, 0);
|
||||
q *= q2;
|
||||
transform.transform.rotation = tf2::toMsg(q.normalized());
|
||||
transforms.push_back(transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
RCLCPP_WARN(node_->get_logger(), "Object %d detected, center 2D at (%f,%f), but invalid depth, cannot set frame \"%s\"! "
|
||||
"(maybe object is too near of the camera or bad depth image)\n",
|
||||
id,
|
||||
center.x(), center.y(),
|
||||
QString("%1_%2").arg(objFramePrefix_.c_str()).arg(id).toStdString().c_str());
|
||||
}
|
||||
}
|
||||
if(transforms.size())
|
||||
{
|
||||
tfBroadcaster_->sendTransform(transforms);
|
||||
}
|
||||
}
|
||||
|
||||
if(pub_->get_subscription_count() || pubStamped_->get_subscription_count() || pubInfo_->get_subscription_count())
|
||||
{
|
||||
std_msgs::msg::Float32MultiArray msg;
|
||||
find_object_2d::msg::ObjectsStamped msgStamped;
|
||||
find_object_2d::msg::DetectionInfo infoMsg;
|
||||
if(pubInfo_->get_subscription_count())
|
||||
{
|
||||
infoMsg.ids.resize(info.objDetected_.size());
|
||||
infoMsg.widths.resize(info.objDetected_.size());
|
||||
infoMsg.heights.resize(info.objDetected_.size());
|
||||
infoMsg.file_paths.resize(info.objDetected_.size());
|
||||
infoMsg.inliers.resize(info.objDetected_.size());
|
||||
infoMsg.outliers.resize(info.objDetected_.size());
|
||||
infoMsg.homographies.resize(info.objDetected_.size());
|
||||
}
|
||||
msg.data = std::vector<float>(info.objDetected_.size()*12);
|
||||
msgStamped.objects.data = std::vector<float>(info.objDetected_.size()*12);
|
||||
|
||||
Q_ASSERT(info.objDetected_.size() == info.objDetectedSizes_.size() &&
|
||||
info.objDetected_.size() == info.objDetectedFilePaths_.size() &&
|
||||
info.objDetected_.size() == info.objDetectedInliersCount_.size() &&
|
||||
info.objDetected_.size() == info.objDetectedOutliersCount_.size());
|
||||
|
||||
int infoIndex=0;
|
||||
int i=0;
|
||||
QMultiMap<int, QSize>::const_iterator iterSizes=info.objDetectedSizes_.constBegin();
|
||||
QMultiMap<int, QString>::const_iterator iterFilePaths=info.objDetectedFilePaths_.constBegin();
|
||||
QMultiMap<int, int>::const_iterator iterInliers=info.objDetectedInliersCount_.constBegin();
|
||||
QMultiMap<int, int>::const_iterator iterOutliers=info.objDetectedOutliersCount_.constBegin();
|
||||
for(QMultiMap<int, QTransform>::const_iterator iter=info.objDetected_.constBegin();
|
||||
iter!=info.objDetected_.constEnd();
|
||||
++iter, ++iterSizes, ++iterFilePaths, ++infoIndex, ++iterInliers, ++iterOutliers)
|
||||
{
|
||||
if(pub_->get_subscription_count() || pubStamped_->get_subscription_count())
|
||||
{
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter.key(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iterSizes->width(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iterSizes->height(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m11(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m12(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m13(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m21(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m22(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m23(); ++i;
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m31(); ++i;// dx
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m32(); ++i;// dy
|
||||
msg.data[i] = msgStamped.objects.data[i] = iter->m33(); ++i;
|
||||
}
|
||||
|
||||
if(pubInfo_->get_subscription_count())
|
||||
{
|
||||
infoMsg.ids[infoIndex].data = iter.key();
|
||||
infoMsg.widths[infoIndex].data = iterSizes->width();
|
||||
infoMsg.heights[infoIndex].data = iterSizes->height();
|
||||
infoMsg.file_paths[infoIndex].data = iterFilePaths.value().toStdString();
|
||||
infoMsg.inliers[infoIndex].data = iterInliers.value();
|
||||
infoMsg.outliers[infoIndex].data = iterOutliers.value();
|
||||
infoMsg.homographies[infoIndex].data.resize(9);
|
||||
infoMsg.homographies[infoIndex].data[0] = iter->m11();
|
||||
infoMsg.homographies[infoIndex].data[1] = iter->m12();
|
||||
infoMsg.homographies[infoIndex].data[2] = iter->m13();
|
||||
infoMsg.homographies[infoIndex].data[3] = iter->m21();
|
||||
infoMsg.homographies[infoIndex].data[4] = iter->m22();
|
||||
infoMsg.homographies[infoIndex].data[5] = iter->m23();
|
||||
infoMsg.homographies[infoIndex].data[6] = iter->m31();
|
||||
infoMsg.homographies[infoIndex].data[7] = iter->m32();
|
||||
infoMsg.homographies[infoIndex].data[8] = iter->m33();
|
||||
}
|
||||
}
|
||||
if(pub_->get_subscription_count())
|
||||
{
|
||||
pub_->publish(msg);
|
||||
}
|
||||
if(pubStamped_->get_subscription_count())
|
||||
{
|
||||
// use same header as the input image (for synchronization and frame reference)
|
||||
msgStamped.header.frame_id = header.frameId_.toStdString();
|
||||
msgStamped.header.stamp.sec = header.sec_;
|
||||
msgStamped.header.stamp.nanosec = header.nsec_;
|
||||
pubStamped_->publish(msgStamped);
|
||||
}
|
||||
if(pubInfo_->get_subscription_count())
|
||||
{
|
||||
// use same header as the input image (for synchronization and frame reference)
|
||||
infoMsg.header.frame_id = header.frameId_.toStdString();
|
||||
infoMsg.header.stamp.sec = header.sec_;
|
||||
infoMsg.header.stamp.nanosec = header.nsec_;
|
||||
pubInfo_->publish(infoMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Vec3f FindObjectROS::getDepth(const cv::Mat & depthImage,
|
||||
int x, int y,
|
||||
float cx, float cy,
|
||||
float fx, float fy)
|
||||
{
|
||||
if(!(x >=0 && x<depthImage.cols && y >=0 && y<depthImage.rows))
|
||||
{
|
||||
RCLCPP_ERROR(node_->get_logger(), "Point must be inside the image (x=%d, y=%d), image size=(%d,%d)",
|
||||
x, y,
|
||||
depthImage.cols, depthImage.rows);
|
||||
return cv::Vec3f(
|
||||
std::numeric_limits<float>::quiet_NaN (),
|
||||
std::numeric_limits<float>::quiet_NaN (),
|
||||
std::numeric_limits<float>::quiet_NaN ());
|
||||
}
|
||||
|
||||
|
||||
cv::Vec3f pt;
|
||||
|
||||
// Use correct principal point from calibration
|
||||
float center_x = cx; //cameraInfo.K.at(2)
|
||||
float center_y = cy; //cameraInfo.K.at(5)
|
||||
|
||||
bool isInMM = depthImage.type() == CV_16UC1; // is in mm?
|
||||
|
||||
// Combine unit conversion (if necessary) with scaling by focal length for computing (X,Y)
|
||||
float unit_scaling = isInMM?0.001f:1.0f;
|
||||
float constant_x = unit_scaling / fx; //cameraInfo.K.at(0)
|
||||
float constant_y = unit_scaling / fy; //cameraInfo.K.at(4)
|
||||
float bad_point = std::numeric_limits<float>::quiet_NaN ();
|
||||
|
||||
float depth;
|
||||
bool isValid;
|
||||
if(isInMM)
|
||||
{
|
||||
depth = (float)depthImage.at<uint16_t>(y,x);
|
||||
isValid = depth != 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
depth = depthImage.at<float>(y,x);
|
||||
isValid = std::isfinite(depth) && depth > 0.0f;
|
||||
}
|
||||
|
||||
// Check for invalid measurements
|
||||
if (!isValid)
|
||||
{
|
||||
pt.val[0] = pt.val[1] = pt.val[2] = bad_point;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fill in XYZ
|
||||
pt.val[0] = (float(x) - center_x) * depth * constant_x;
|
||||
pt.val[1] = (float(y) - center_y) * depth * constant_y;
|
||||
pt.val[2] = depth*unit_scaling;
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
75
src/ros2/FindObjectROS.h
Normal file
75
src/ros2/FindObjectROS.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef FINDOBJECTROS_H_
|
||||
#define FINDOBJECTROS_H_
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
#include "find_object/FindObject.h"
|
||||
|
||||
#include <std_msgs/msg/float32_multi_array.hpp>
|
||||
#include "find_object_2d/msg/objects_stamped.hpp"
|
||||
#include "find_object_2d/msg/detection_info.hpp"
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QMultiMap>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QRect>
|
||||
#include <QtGui/QTransform>
|
||||
|
||||
class FindObjectROS : public find_object::FindObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
FindObjectROS(rclcpp::Node * node);
|
||||
virtual ~FindObjectROS() {}
|
||||
|
||||
public Q_SLOTS:
|
||||
void publish(const find_object::DetectionInfo & info, const find_object::Header & header, const cv::Mat & depth, float depthConstant);
|
||||
|
||||
private:
|
||||
cv::Vec3f getDepth(const cv::Mat & depthImage,
|
||||
int x, int y,
|
||||
float cx, float cy,
|
||||
float fx, float fy);
|
||||
|
||||
private:
|
||||
rclcpp::Node * node_;
|
||||
rclcpp::Publisher<std_msgs::msg::Float32MultiArray>::SharedPtr pub_;
|
||||
rclcpp::Publisher<find_object_2d::msg::ObjectsStamped>::SharedPtr pubStamped_;
|
||||
rclcpp::Publisher<find_object_2d::msg::DetectionInfo>::SharedPtr pubInfo_;
|
||||
|
||||
std::string objFramePrefix_;
|
||||
bool usePnP_;
|
||||
std::shared_ptr<tf2_ros::TransformBroadcaster> tfBroadcaster_;
|
||||
|
||||
};
|
||||
|
||||
#endif /* FINDOBJECTROS_H_ */
|
||||
217
src/ros2/find_object_2d_node.cpp
Normal file
217
src/ros2/find_object_2d_node.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "CameraROS.h"
|
||||
#include "FindObjectROS.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include "find_object/MainWindow.h"
|
||||
#include "ParametersToolBox.h"
|
||||
#include "find_object/Settings.h"
|
||||
#include <signal.h>
|
||||
|
||||
void my_handler_gui(int s){
|
||||
QApplication::closeAllWindows();
|
||||
QApplication::quit();
|
||||
}
|
||||
void my_handler(int s){
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
void setupQuitSignal(bool gui)
|
||||
{
|
||||
// Catch ctrl-c to close the gui
|
||||
struct sigaction sigIntHandler;
|
||||
if(gui)
|
||||
{
|
||||
sigIntHandler.sa_handler = my_handler_gui;
|
||||
}
|
||||
else
|
||||
{
|
||||
sigIntHandler.sa_handler = my_handler;
|
||||
}
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
}
|
||||
|
||||
class FindObjectNode : public rclcpp::Node {
|
||||
|
||||
public:
|
||||
FindObjectNode() :
|
||||
rclcpp::Node("find_object_2d"),
|
||||
findObjectROS_(0),
|
||||
camera_(0),
|
||||
gui_(true)
|
||||
{
|
||||
std::string objectsPath;
|
||||
std::string sessionPath;
|
||||
std::string settingsPath = QDir::homePath().append("/.ros/find_object_2d.ini").toStdString();
|
||||
bool subscribeDepth = false;
|
||||
|
||||
gui_ = this->declare_parameter("gui", gui_);
|
||||
objectsPath = this->declare_parameter("objects_path", objectsPath);
|
||||
sessionPath = this->declare_parameter("session_path", sessionPath);
|
||||
settingsPath = this->declare_parameter("settings_path", settingsPath);
|
||||
subscribeDepth = this->declare_parameter("subscribe_depth", subscribeDepth);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "gui=%d", gui_?1:0);
|
||||
RCLCPP_INFO(this->get_logger(), "objects_path=%s", objectsPath.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "session_path=%s", sessionPath.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "settings_path=%s", settingsPath.c_str());
|
||||
RCLCPP_INFO(this->get_logger(), "subscribe_depth = %s", subscribeDepth?"true":"false");
|
||||
|
||||
if(settingsPath.empty())
|
||||
{
|
||||
settingsPath = QDir::homePath().append("/.ros/find_object_2d.ini").toStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!sessionPath.empty())
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "\"settings_path\" parameter is ignored when \"session_path\" is set.");
|
||||
}
|
||||
|
||||
QString path = settingsPath.c_str();
|
||||
if(path.contains('~'))
|
||||
{
|
||||
path.replace('~', QDir::homePath());
|
||||
settingsPath = path.toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings, should be loaded before creating other objects
|
||||
find_object::Settings::init(settingsPath.c_str());
|
||||
|
||||
findObjectROS_ = new FindObjectROS(this);
|
||||
if(!sessionPath.empty())
|
||||
{
|
||||
if(!objectsPath.empty())
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "\"objects_path\" parameter is ignored when \"session_path\" is set.");
|
||||
}
|
||||
if(!findObjectROS_->loadSession(sessionPath.c_str()))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "Failed to load session \"%s\"", sessionPath.c_str());
|
||||
}
|
||||
}
|
||||
else if(!objectsPath.empty())
|
||||
{
|
||||
QString path = objectsPath.c_str();
|
||||
if(path.contains('~'))
|
||||
{
|
||||
path.replace('~', QDir::homePath());
|
||||
}
|
||||
if(!findObjectROS_->loadObjects(path))
|
||||
{
|
||||
RCLCPP_ERROR(this->get_logger(), "No objects loaded from path \"%s\"", path.toStdString().c_str());
|
||||
}
|
||||
}
|
||||
camera_ = new CameraROS(subscribeDepth, this);
|
||||
|
||||
// Catch ctrl-c to close the gui
|
||||
setupQuitSignal(gui_);
|
||||
}
|
||||
|
||||
void exec(int argc, char ** argv, std::shared_ptr<rclcpp::Node> node)
|
||||
{
|
||||
camera_->setupExecutor(node);
|
||||
if(gui_)
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
find_object::MainWindow mainWindow(findObjectROS_, camera_); // take ownership
|
||||
|
||||
QObject::connect(
|
||||
&mainWindow,
|
||||
SIGNAL(objectsFound(const find_object::DetectionInfo &, const find_object::Header &, const cv::Mat &, float)),
|
||||
findObjectROS_,
|
||||
SLOT(publish(const find_object::DetectionInfo &, const find_object::Header &, const cv::Mat &, float)));
|
||||
|
||||
QStringList topics = camera_->subscribedTopics();
|
||||
if(topics.size() == 1)
|
||||
{
|
||||
mainWindow.setSourceImageText(mainWindow.tr(
|
||||
"<qt>Find-Object subscribed to <b>%1</b> topic.<br/>"
|
||||
"You can remap the topic when starting the node: <br/>\"rosrun find_object_2d find_object_2d image:=your/image/topic\".<br/>"
|
||||
"</qt>").arg(topics.first()));
|
||||
}
|
||||
else if(topics.size() == 3)
|
||||
{
|
||||
mainWindow.setSourceImageText(mainWindow.tr(
|
||||
"<qt>Find-Object subscribed to : <br/> <b>%1</b> <br/> <b>%2</b> <br/> <b>%3</b><br/>"
|
||||
"</qt>").arg(topics.at(0)).arg(topics.at(1)).arg(topics.at(2)));
|
||||
}
|
||||
mainWindow.show();
|
||||
app.connect( &app, SIGNAL( lastWindowClosed() ), &app, SLOT( quit() ) );
|
||||
|
||||
// mainWindow has ownership
|
||||
findObjectROS_ = 0;
|
||||
camera_ = 0;
|
||||
|
||||
// loop
|
||||
mainWindow.startProcessing();
|
||||
app.exec();
|
||||
find_object::Settings::saveSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
// connect stuff:
|
||||
QObject::connect(camera_, SIGNAL(imageReceived(const cv::Mat &, const find_object::Header &, const cv::Mat &, float)), findObjectROS_, SLOT(detect(const cv::Mat &, const find_object::Header &, const cv::Mat &, float)));
|
||||
|
||||
//loop
|
||||
camera_->start();
|
||||
app.exec();
|
||||
|
||||
delete camera_;
|
||||
camera_=0;
|
||||
delete findObjectROS_;
|
||||
findObjectROS_=0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~FindObjectNode()
|
||||
{
|
||||
delete findObjectROS_;
|
||||
delete camera_;
|
||||
}
|
||||
|
||||
private:
|
||||
FindObjectROS * findObjectROS_;
|
||||
CameraROS * camera_;
|
||||
bool gui_;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<FindObjectNode>();
|
||||
node->exec(argc, argv, node);
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
183
src/ros2/print_objects_detected_node.cpp
Normal file
183
src/ros2/print_objects_detected_node.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <find_object_2d/msg/objects_stamped.hpp>
|
||||
#include <message_filters/subscriber.h>
|
||||
#include <message_filters/time_synchronizer.h>
|
||||
#include <image_transport/image_transport.hpp>
|
||||
#include <image_transport/subscriber_filter.hpp>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <QTransform>
|
||||
#include <QColor>
|
||||
|
||||
class PrintObjects: public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
PrintObjects() :
|
||||
Node("objects_detected")
|
||||
{
|
||||
image_transport::TransportHints hints(this);
|
||||
|
||||
imagePub_ = image_transport::create_publisher(this, "image_with_objects", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
|
||||
// Simple subscriber
|
||||
sub_ = create_subscription<std_msgs::msg::Float32MultiArray>("objects", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1), std::bind(&PrintObjects::objectsDetectedCallback, this, std::placeholders::_1));
|
||||
|
||||
// Synchronized image + objects example
|
||||
imageSub_.subscribe(this, "image", hints.getTransport(), rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
objectsSub_.subscribe(this, "objectsStamped", rclcpp::QoS(1).reliability((rmw_qos_reliability_policy_t)1).get_rmw_qos_profile());
|
||||
|
||||
exactSync_ = new message_filters::Synchronizer<MyExactSyncPolicy>(MyExactSyncPolicy(10), imageSub_, objectsSub_);
|
||||
exactSync_->registerCallback(std::bind(&PrintObjects::imageObjectsDetectedCallback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
virtual ~PrintObjects()
|
||||
{
|
||||
delete exactSync_;
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT :
|
||||
* Parameter General/MirrorView must be false
|
||||
* Parameter Homography/homographyComputed must be true
|
||||
*/
|
||||
void objectsDetectedCallback(
|
||||
const std_msgs::msg::Float32MultiArray::ConstSharedPtr msg)
|
||||
{
|
||||
printf("---\n");
|
||||
const std::vector<float> & data = msg->data;
|
||||
if(data.size())
|
||||
{
|
||||
for(unsigned int i=0; i<data.size(); i+=12)
|
||||
{
|
||||
// get data
|
||||
int id = (int)data[i];
|
||||
float objectWidth = data[i+1];
|
||||
float objectHeight = data[i+2];
|
||||
|
||||
// Find corners Qt
|
||||
QTransform qtHomography(data[i+3], data[i+4], data[i+5],
|
||||
data[i+6], data[i+7], data[i+8],
|
||||
data[i+9], data[i+10], data[i+11]);
|
||||
|
||||
QPointF qtTopLeft = qtHomography.map(QPointF(0,0));
|
||||
QPointF qtTopRight = qtHomography.map(QPointF(objectWidth,0));
|
||||
QPointF qtBottomLeft = qtHomography.map(QPointF(0,objectHeight));
|
||||
QPointF qtBottomRight = qtHomography.map(QPointF(objectWidth,objectHeight));
|
||||
|
||||
printf("Object %d detected, Qt corners at (%f,%f) (%f,%f) (%f,%f) (%f,%f)\n",
|
||||
id,
|
||||
qtTopLeft.x(), qtTopLeft.y(),
|
||||
qtTopRight.x(), qtTopRight.y(),
|
||||
qtBottomLeft.x(), qtBottomLeft.y(),
|
||||
qtBottomRight.x(), qtBottomRight.y());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("No objects detected.\n");
|
||||
}
|
||||
}
|
||||
void imageObjectsDetectedCallback(
|
||||
const sensor_msgs::msg::Image::ConstSharedPtr imageMsg,
|
||||
const find_object_2d::msg::ObjectsStamped::ConstSharedPtr objectsMsg)
|
||||
{
|
||||
if(imagePub_.getNumSubscribers() > 0)
|
||||
{
|
||||
const std::vector<float> & data = objectsMsg->objects.data;
|
||||
if(data.size())
|
||||
{
|
||||
for(unsigned int i=0; i<data.size(); i+=12)
|
||||
{
|
||||
// get data
|
||||
int id = (int)data[i];
|
||||
float objectWidth = data[i+1];
|
||||
float objectHeight = data[i+2];
|
||||
|
||||
// Find corners OpenCV
|
||||
cv::Mat cvHomography(3, 3, CV_32F);
|
||||
cvHomography.at<float>(0,0) = data[i+3];
|
||||
cvHomography.at<float>(1,0) = data[i+4];
|
||||
cvHomography.at<float>(2,0) = data[i+5];
|
||||
cvHomography.at<float>(0,1) = data[i+6];
|
||||
cvHomography.at<float>(1,1) = data[i+7];
|
||||
cvHomography.at<float>(2,1) = data[i+8];
|
||||
cvHomography.at<float>(0,2) = data[i+9];
|
||||
cvHomography.at<float>(1,2) = data[i+10];
|
||||
cvHomography.at<float>(2,2) = data[i+11];
|
||||
std::vector<cv::Point2f> inPts, outPts;
|
||||
inPts.push_back(cv::Point2f(0,0));
|
||||
inPts.push_back(cv::Point2f(objectWidth,0));
|
||||
inPts.push_back(cv::Point2f(objectWidth,objectHeight));
|
||||
inPts.push_back(cv::Point2f(0,objectHeight));
|
||||
inPts.push_back(cv::Point2f(objectWidth/2,objectHeight/2));
|
||||
cv::perspectiveTransform(inPts, outPts, cvHomography);
|
||||
|
||||
cv_bridge::CvImageConstPtr imageDepthPtr = cv_bridge::toCvShare(imageMsg);
|
||||
|
||||
cv_bridge::CvImage img;
|
||||
img = *imageDepthPtr;
|
||||
std::vector<cv::Point2i> outPtsInt;
|
||||
outPtsInt.push_back(outPts[0]);
|
||||
outPtsInt.push_back(outPts[1]);
|
||||
outPtsInt.push_back(outPts[2]);
|
||||
outPtsInt.push_back(outPts[3]);
|
||||
QColor color(QColor((Qt::GlobalColor)((id % 10 + 7)==Qt::yellow?Qt::darkYellow:(id % 10 + 7))));
|
||||
cv::Scalar cvColor(color.red(), color.green(), color.blue());
|
||||
cv::polylines(img.image, outPtsInt, true, cvColor, 3);
|
||||
cv::Point2i center = outPts[4];
|
||||
cv::putText(img.image, QString("(%1, %2)").arg(center.x).arg(center.y).toStdString(), center, cv::FONT_HERSHEY_SIMPLEX, 0.6, cvColor, 2);
|
||||
cv::circle(img.image, center, 1, cvColor, 3);
|
||||
imagePub_.publish(img.toImageMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef message_filters::sync_policies::ExactTime<
|
||||
sensor_msgs::msg::Image,
|
||||
find_object_2d::msg::ObjectsStamped> MyExactSyncPolicy;
|
||||
message_filters::Synchronizer<MyExactSyncPolicy> * exactSync_;
|
||||
image_transport::Publisher imagePub_;
|
||||
image_transport::SubscriberFilter imageSub_;
|
||||
message_filters::Subscriber<find_object_2d::msg::ObjectsStamped> objectsSub_;
|
||||
rclcpp::Subscription<std_msgs::msg::Float32MultiArray>::SharedPtr sub_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<PrintObjects>());
|
||||
rclcpp::shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
122
src/ros2/tf_example_node.cpp
Normal file
122
src/ros2/tf_example_node.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright (c) 2011-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
#include <tf2_ros/buffer.h>
|
||||
#include <find_object_2d/msg/objects_stamped.hpp>
|
||||
#include <geometry_msgs/msg/transform_stamped.hpp>
|
||||
#include <QtCore/QString>
|
||||
|
||||
class TfExample : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
TfExample() :
|
||||
Node("tf_example_node"),
|
||||
objFramePrefix_("object")
|
||||
{
|
||||
tfBuffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
//auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
|
||||
// this->get_node_base_interface(),
|
||||
// this->get_node_timers_interface());
|
||||
//tfBuffer_->setCreateTimerInterface(timer_interface);
|
||||
tfListener_ = std::make_shared<tf2_ros::TransformListener>(*tfBuffer_);
|
||||
|
||||
targetFrameId_ = this->declare_parameter("target_frame_id", targetFrameId_);
|
||||
objFramePrefix_ = this->declare_parameter("object_prefix", objFramePrefix_);
|
||||
|
||||
subs_ = create_subscription<find_object_2d::msg::ObjectsStamped>("objectsStamped", rclcpp::QoS(5).reliability((rmw_qos_reliability_policy_t)1), std::bind(&TfExample::objectsDetectedCallback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
// Here I synchronize with the ObjectsStamped topic to
|
||||
// know when the TF is ready and for which objects
|
||||
void objectsDetectedCallback(const find_object_2d::msg::ObjectsStamped::ConstSharedPtr msg)
|
||||
{
|
||||
if(msg->objects.data.size())
|
||||
{
|
||||
std::string targetFrameId = targetFrameId_;
|
||||
if(targetFrameId.empty())
|
||||
{
|
||||
targetFrameId = msg->header.frame_id;
|
||||
}
|
||||
char multiSubId = 'b';
|
||||
int previousId = -1;
|
||||
for(unsigned int i=0; i<msg->objects.data.size(); i+=12)
|
||||
{
|
||||
// get data
|
||||
int id = (int)msg->objects.data[i];
|
||||
|
||||
QString multiSuffix;
|
||||
if(id == previousId)
|
||||
{
|
||||
multiSuffix = QString("_") + multiSubId++;
|
||||
}
|
||||
else
|
||||
{
|
||||
multiSubId = 'b';
|
||||
}
|
||||
previousId = id;
|
||||
|
||||
// "object_1", "object_1_b", "object_1_c", "object_2"
|
||||
std::string objectFrameId = QString("%1_%2%3").arg(objFramePrefix_.c_str()).arg(id).arg(multiSuffix).toStdString();
|
||||
|
||||
geometry_msgs::msg::TransformStamped pose;
|
||||
try
|
||||
{
|
||||
// Get transformation from "object_#" frame to target frame
|
||||
// The timestamp matches the one sent over TF
|
||||
pose = tfBuffer_->lookupTransform(targetFrameId, objectFrameId, tf2_ros::fromMsg(msg->header.stamp));
|
||||
}
|
||||
catch(tf2::TransformException & ex)
|
||||
{
|
||||
RCLCPP_WARN(this->get_logger(), "%s",ex.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Here "pose" is the position of the object "id" in target frame.
|
||||
RCLCPP_INFO(this->get_logger(), "%s [x,y,z] [x,y,z,w] in \"%s\" frame: [%f,%f,%f] [%f,%f,%f,%f]",
|
||||
objectFrameId.c_str(), targetFrameId.c_str(),
|
||||
pose.transform.translation.x, pose.transform.translation.y, pose.transform.translation.z,
|
||||
pose.transform.rotation.x, pose.transform.rotation.y, pose.transform.rotation.z, pose.transform.rotation.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string targetFrameId_;
|
||||
std::string objFramePrefix_;
|
||||
rclcpp::Subscription<find_object_2d::msg::ObjectsStamped>::SharedPtr subs_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tfBuffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tfListener_;
|
||||
};
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<TfExample>());
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user