Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optical frame publisher from subt/mbzirc #257

Open
wants to merge 10 commits into
base: galactic
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions ros_ign_bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ add_library(${bridge_lib}
${generated_files}
)

message(STATUS ${GZ_MSGS_VER})

ament_target_dependencies(${bridge_lib}
rclcpp
rclcpp_components
Expand Down Expand Up @@ -296,7 +294,6 @@ if(BUILD_TESTING)
target_include_directories(${PROJECT_NAME}_bridge_config PRIVATE src)
ament_target_dependencies(${PROJECT_NAME}_bridge_config rclcpp)
target_link_libraries(${PROJECT_NAME}_bridge_config ${bridge_lib})

endif()

ament_export_dependencies(
Expand Down
62 changes: 48 additions & 14 deletions ros_ign_gazebo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ endif()
find_package(ament_cmake REQUIRED)
find_package(ignition-math6 REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(std_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)

# Fortress
if("$ENV{IGNITION_VERSION}" STREQUAL "fortress")
Expand Down Expand Up @@ -46,18 +51,39 @@ ign_find_package(gflags
PKGCONFIG gflags)

add_executable(create src/create.cpp)
ament_target_dependencies(create
rclcpp
ignition-math6
std_msgs
)
target_link_libraries(create
gflags
ignition-msgs${IGN_MSGS_VER}::core
ignition-transport${IGN_TRANSPORT_VER}::core
rclcpp::rclcpp
${std_msgs_TARGETS}
)

add_library(
${PROJECT_NAME}
SHARED
src/optical_frame_publisher.cpp
src/Stopwatch.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")

ament_target_dependencies(
${PROJECT_NAME}
rclcpp
rclcpp_components
tf2
tf2_ros
geometry_msgs
sensor_msgs
)

rclcpp_components_register_node(${PROJECT_NAME}
PLUGIN "ros_ign_utils::OpticalFramePublisher"
EXECUTABLE optical_frame_publisher
)

add_library(${PROJECT_NAME} SHARED src/Stopwatch.cpp)
ament_target_dependencies(${PROJECT_NAME}
rclcpp
)
Expand Down Expand Up @@ -91,18 +117,16 @@ install(TARGETS
DESTINATION lib/${PROJECT_NAME}
)

install(
TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}
install(TARGETS ${PROJECT_NAME}
EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

ament_export_targets(${PROJECT_NAME})

ament_export_dependencies(rclcpp)
RUNTIME DESTINATION bin)

if(BUILD_TESTING)
# Disable some broken tests for now
set(ament_cmake_copyright_FOUND TRUE)

find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

Expand Down Expand Up @@ -133,4 +157,14 @@ if(BUILD_TESTING)

endif()

# Export dependencies
ament_export_dependencies(rclcpp)

# Export old-style CMake variables
ament_export_include_directories("include/${PROJECT_NAME}")
ament_export_libraries(${PROJECT_NAME})

# Export modern CMake targets
ament_export_targets(export_${PROJECT_NAME})

ament_package()
47 changes: 47 additions & 0 deletions ros_ign_gazebo/include/ros_ign_gazebo/optical_frame_publisher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* 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.
*
*/

#ifndef ROS_IGN_GAZEBO__OPTICAL_FRAME_PUBLISHER_HPP_
#define ROS_IGN_GAZEBO__OPTICAL_FRAME_PUBLISHER_HPP_

#include <rclcpp/rclcpp.hpp>

#include <memory>

namespace ros_ign_gazebo
{

/// \brief ROS Node to transform image coordinate conventions to match
/// ROS REP 103 (https://www.ros.org/reps/rep-0103.html)
class OpticalFramePublisher : public rclcpp::Node
{
public:
/// \param[in] options additional controls for creating the ROS node
/// \brief Constructor
explicit OpticalFramePublisher(const rclcpp::NodeOptions & options);
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved

private:
/// \brief Forward declaration of private implementation
struct Impl;

/// \brief Pointer to implementation
std::unique_ptr<Impl> dataPtr;
};

} // namespace ros_ign_gazebo

#endif // ROS_IGN_GAZEBO__OPTICAL_FRAME_PUBLISHER_HPP_
5 changes: 5 additions & 0 deletions ros_ign_gazebo/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@

<depend>libgflags-dev</depend>
<depend>rclcpp</depend>
<depend>rclcpp_components</depend>
<depend>ignition-math6</depend>
<depend>geometry_msgs</depend>
<depend>sensor_msgs</depend>
<depend>std_msgs</depend>
<depend>tf2</depend>
<depend>tf2_ros</depend>

<!-- Fortress -->
<depend condition="$IGNITION_VERSION == fortress">ignition-gazebo6</depend>
Expand Down
204 changes: 204 additions & 0 deletions ros_ign_gazebo/src/optical_frame_publisher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* 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 "ros_ign_gazebo/optical_frame_publisher.hpp"

#include <tf2/LinearMath/Quaternion.h>
#include <tf2_ros/static_transform_broadcaster.h>

#include <geometry_msgs/msg/transform_stamped.hpp>
#include <sensor_msgs/msg/image.hpp>
#include <sensor_msgs/msg/camera_info.hpp>

#include <rclcpp/rclcpp.hpp>

#include <rclcpp_components/register_node_macro.hpp>
#include <memory>
#include <string>


using std::placeholders::_1;
using namespace std::chrono_literals;

namespace ros_ign_gazebo
{
struct OpticalFramePublisher::Impl
{
/// \brief Check for downstream subscriptions and enable upstream
/// subscriptions based on that
void CheckSubscribers();

/// \brief Start upstream subscription for images
void ImageConnect();

/// \brief Start upstream subscription for camera info
void CameraInfoConnect();

/// \brief Image callback
/// \param[in] msg incoming image from from upstream
void UpdateImageFrame(const sensor_msgs::msg::Image & msg);
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved

/// \brief Camera info callback
/// \param[in] msg incoming camera information from upstream
void UpdateCameraInfoFrame(const sensor_msgs::msg::CameraInfo & msg);
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved

/// \brief Publish new TF between frame and optical_frame
/// \param[in] frame parent frame (camera frame)
/// \param[in] child_frame child frame (camera optical frame)
void PublishTF(const std::string & frame, const std::string & child_frame);
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved

/// \brief Interface for creating publications/subscriptions
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics;

/// \brief timer with callback to check for publisher subscription count
rclcpp::TimerBase::SharedPtr timer;

/// \brief Static transform broadcaster that broadcasts the optical frame id
std::unique_ptr<tf2_ros::StaticTransformBroadcaster> tf_broadcaster_static;

/// \brief Publisher for the image optical frame topic
rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_pub;

/// \brief Publisher for the camera info optical frame topic
rclcpp::Publisher<sensor_msgs::msg::CameraInfo>::SharedPtr info_pub;

/// \brief Subscriber for the original image topic
rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr image_sub;

/// \brief Subscriber for the original camera_info topic
rclcpp::Subscription<sensor_msgs::msg::CameraInfo>::SharedPtr info_sub;

/// \brief Publish camera info or just the image
bool publish_camera_info;

/// \brief New frame id of published messages
std::string new_frame_id;
};

void OpticalFramePublisher::Impl::CheckSubscribers()
{
if (image_pub->get_subscription_count() > 0u) {
ImageConnect();
}

if (info_pub && info_pub->get_subscription_count() > 0u) {
CameraInfoConnect();
}
}

void OpticalFramePublisher::Impl::ImageConnect()
{
// Skip connecting if publisher is already created (non-nullptr)
if (image_sub) {
return;
}

image_sub = rclcpp::create_subscription<sensor_msgs::msg::Image>(
node_topics,
"input/image", 10,
std::bind(&OpticalFramePublisher::Impl::UpdateImageFrame, this, _1));
}

void OpticalFramePublisher::Impl::CameraInfoConnect()
{
// Skip connecting if publisher is already created (non-nullptr)
if (info_sub) {
return;
}

info_sub = rclcpp::create_subscription<sensor_msgs::msg::CameraInfo>(
node_topics,
"input/camera_info", 10,
std::bind(&OpticalFramePublisher::Impl::UpdateCameraInfoFrame, this, _1));
}

void OpticalFramePublisher::Impl::UpdateImageFrame(const sensor_msgs::msg::Image & msg)
{
if (image_pub->get_subscription_count() == 0u && image_sub) {
image_sub.reset();
return;
}
if (new_frame_id.empty()) {
new_frame_id = msg.header.frame_id + "_optical";
PublishTF(msg.header.frame_id, new_frame_id);
}
auto m = msg;
m.header.frame_id = new_frame_id;
image_pub->publish(m);
}

void OpticalFramePublisher::Impl::UpdateCameraInfoFrame(const sensor_msgs::msg::CameraInfo & msg)
{
if (info_pub->get_subscription_count() == 0u && info_sub) {
info_sub.reset();
return;
}
if (new_frame_id.empty()) {
new_frame_id = msg.header.frame_id + "_optical";
PublishTF(msg.header.frame_id, new_frame_id);
}
auto m = msg;
m.header.frame_id = new_frame_id;
info_pub->publish(m);
}

void OpticalFramePublisher::Impl::PublishTF(
const std::string & frame,
const std::string & child_frame)
{
geometry_msgs::msg::TransformStamped tfStamped;
tfStamped.header.frame_id = frame;
tfStamped.child_frame_id = child_frame;
tfStamped.transform.translation.x = 0.0;
tfStamped.transform.translation.y = 0.0;
tfStamped.transform.translation.z = 0.0;
tf2::Quaternion q;
// converts x forward to z forward
q.setRPY(-M_PI / 2.0, 0, -M_PI / 2.0);
tfStamped.transform.rotation.x = q.x();
tfStamped.transform.rotation.y = q.y();
tfStamped.transform.rotation.z = q.z();
tfStamped.transform.rotation.w = q.w();
tf_broadcaster_static->sendTransform(tfStamped);
}

OpticalFramePublisher::OpticalFramePublisher(const rclcpp::NodeOptions & options)
: Node("optical_frame_publisher", options),
dataPtr(std::make_unique<Impl>())
{
dataPtr->node_topics = this->get_node_topics_interface();

dataPtr->tf_broadcaster_static =
std::make_unique<tf2_ros::StaticTransformBroadcaster>(*this);

dataPtr->publish_camera_info = this->declare_parameter("publish_camera_info", true);

dataPtr->image_pub = this->create_publisher<sensor_msgs::msg::Image>(
"output/image", 10);

if (dataPtr->publish_camera_info) {
dataPtr->info_pub = this->create_publisher<sensor_msgs::msg::CameraInfo>(
"output/camera_info", 10);
}

dataPtr->timer = this->create_wall_timer(
100ms,
std::bind(&OpticalFramePublisher::Impl::CheckSubscribers, dataPtr.get()));
}
} // namespace ros_ign_gazebo

RCLCPP_COMPONENTS_REGISTER_NODE(ros_ign_gazebo::OpticalFramePublisher)