Program Listing for File as2_external_object_to_tf.cpp
↰ Return to documentation for file (/tmp/ws/src/aerostack2/as2_utilities/as2_external_object_to_tf/src/as2_external_object_to_tf.cpp
)
// Copyright 2024 Universidad Politécnica de Madrid
//
// 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 Universidad Politécnica de Madrid 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 "as2_external_object_to_tf.hpp"
#include "as2_core/utils/frame_utils.hpp"
#include "yaml-cpp/yaml.h"
As2ExternalObjectToTf::As2ExternalObjectToTf()
: as2::Node("external_object_to_tf")
{
try {
this->declare_parameter("config_file", "config/external_objects.yaml");
this->get_parameter("config_file", config_path_);
} catch (const std::exception & e) {
RCLCPP_ERROR(this->get_logger(), "config_file parameter not set: %s", e.what());
config_path_ = "config/external_objects.yaml";
}
try {
this->declare_parameter("mocap_topic", "mocap/pose");
this->get_parameter("mocap_topic", mocap_topic_);
} catch (const std::exception & e) {
RCLCPP_WARN(this->get_logger(), "mocap_topic parameter not set: %s", e.what());
mocap_topic_ = "mocap/pose";
}
this->get_parameter("use_sim_time", use_sim_time);
}
void As2ExternalObjectToTf::poseCallback(
const geometry_msgs::msg::PoseStamped::SharedPtr _msg,
std::string frame_id,
std::string parent_frame_id)
{
if (parent_frame_id == "msg_defined") {
parent_frame_id = _msg->header.frame_id;
}
publishPoseAsTransform(_msg, frame_id, parent_frame_id);
}
void As2ExternalObjectToTf::gpsCallback(
const sensor_msgs::msg::NavSatFix::SharedPtr _msg,
std::string frame_id,
std::string parent_frame_id)
{
gps_poses[frame_id].gps_pose = _msg;
if (gps_poses[frame_id].azimuth != NULL) {
tfBroadcaster->sendTransform(
gpsToTransform(
gps_poses[frame_id].gps_pose,
gps_poses[frame_id].azimuth->data, 0.0, frame_id,
parent_frame_id));
}
}
void As2ExternalObjectToTf::azimuthCallback(
const std_msgs::msg::Float32::SharedPtr _msg,
std::string frame_id,
std::string parent_frame_id)
{
gps_poses[frame_id].azimuth = _msg;
}
void As2ExternalObjectToTf::mocapCallback(
const mocap4r2_msgs::msg::RigidBodies::SharedPtr msg,
std::vector<std::tuple<std::string, std::string>> mappings)
{
for (auto mapping : mappings) {
for (auto body : msg->rigidbodies) {
if (body.rigid_body_name == std::get<0>(mapping)) {
std::shared_ptr<geometry_msgs::msg::Pose> pose =
std::make_shared<geometry_msgs::msg::Pose>(body.pose);
publishPoseAsTransform(pose, std::get<1>(mapping), "earth");
}
}
}
}
void As2ExternalObjectToTf::addStaticTransform(
const std::shared_ptr<as2_msgs::srv::AddStaticTransform::Request> request,
const std::shared_ptr<as2_msgs::srv::AddStaticTransform::Response> response)
{
geometry_msgs::msg::TransformStamped static_transform;
static_transform.child_frame_id = request->child_frame_id;
static_transform.header.frame_id = request->frame_id;
static_transform.header.stamp = this->get_clock()->now();
static_transform.transform = request->transform;
staticTfBroadcaster->sendTransform(static_transform);
response->success = true;
}
void As2ExternalObjectToTf::addStaticTransformGps(
const std::shared_ptr<as2_msgs::srv::AddStaticTransformGps::Request> request,
const std::shared_ptr<as2_msgs::srv::AddStaticTransformGps::Response>
response)
{
if (!origin_set_) {
setupGPS();
origin_set_ = true;
}
geometry_msgs::msg::TransformStamped static_transform;
static_transform = gpsToTransform(
std::make_shared<sensor_msgs::msg::NavSatFix>(request->gps_position), request->azimuth,
request->elevation, request->child_frame_id, request->frame_id);
static_transform.header.stamp = this->get_clock()->now();
staticTfBroadcaster->sendTransform(static_transform);
response->success = true;
}
geometry_msgs::msg::Quaternion As2ExternalObjectToTf::azimuthToQuaternion(
std_msgs::msg::Float32::SharedPtr azimuth)
{
float azimuthRad = azimuth->data * M_PI / 180.0;
azimuthRad += M_PI / 2;
if (azimuthRad > M_PI) {
azimuthRad -= (2 * M_PI);
}
geometry_msgs::msg::Quaternion q;
double halfYaw = azimuthRad * 0.5;
q.x = 0.0;
q.y = 0.0;
q.z = cos(halfYaw);
q.w = sin(halfYaw);
return q;
}
geometry_msgs::msg::Quaternion As2ExternalObjectToTf::azimuthPitchToQuaternion(
float azimuth = 0.0,
float elevation = 0.0)
{
double rotated_azimuth = azimuth - 90;
if (rotated_azimuth < 0) {
rotated_azimuth += 360.0;
} else if (rotated_azimuth >= 360.0) {
rotated_azimuth -= 360.0;
}
float azimuthRad = rotated_azimuth * M_PI / 180.0;
float elevationRad = elevation * M_PI / 180.0;
if (elevationRad > M_PI) {
elevationRad -= 2.0 * M_PI;
} else if (elevationRad < -M_PI) {
elevationRad += 2.0 * M_PI;
}
geometry_msgs::msg::Quaternion q;
as2::frame::eulerToQuaternion(0.0, elevationRad, -azimuthRad, q);
return q;
}
geometry_msgs::msg::TransformStamped As2ExternalObjectToTf::gpsToTransform(
const sensor_msgs::msg::NavSatFix::SharedPtr gps_pose,
const float azimuth,
const float elevation,
const std::string frame_id,
const std::string parent_frame_id)
{
geometry_msgs::msg::TransformStamped transform;
transform.header.stamp = gps_pose->header.stamp;
transform.header.frame_id = parent_frame_id;
transform.child_frame_id = frame_id;
sensor_msgs::msg::NavSatFix * rawPtr = gps_pose.get();
gps_handler->LatLon2Local(
*rawPtr, transform.transform.translation.x,
transform.transform.translation.y, transform.transform.translation.z);
transform.transform.rotation = azimuthPitchToQuaternion(azimuth, elevation);
return transform;
}
void As2ExternalObjectToTf::publishPoseAsTransform(
const geometry_msgs::msg::PoseStamped::SharedPtr msg,
std::string frame_id,
std::string parent_frame_id)
{
geometry_msgs::msg::TransformStamped transform;
transform.child_frame_id = frame_id;
transform.header.frame_id = parent_frame_id;
transform.header.stamp = msg->header.stamp;
transform.transform.rotation = msg->pose.orientation;
transform.transform.translation.x = msg->pose.position.x;
transform.transform.translation.y = msg->pose.position.y;
transform.transform.translation.z = msg->pose.position.z;
tfBroadcaster->sendTransform(transform);
}
void As2ExternalObjectToTf::publishPoseAsTransform(
const geometry_msgs::msg::Pose::SharedPtr msg,
std::string frame_id,
std::string parent_frame_id)
{
geometry_msgs::msg::PoseStamped::SharedPtr pose_msg =
std::make_shared<geometry_msgs::msg::PoseStamped>();
pose_msg->pose = *msg;
pose_msg->header.stamp = this->get_clock()->now();
publishPoseAsTransform(pose_msg, frame_id, parent_frame_id);
}
void As2ExternalObjectToTf::loadObjects(const std::string path)
{
try { // Load objects from yaml file
RCLCPP_INFO(this->get_logger(), "Loading objects from config file: %s", config_path_.c_str());
YAML::Node config = YAML::LoadFile(config_path_); // Load the config file
auto objects = config["objects"]; // Get the objects from the config file
for (YAML::const_iterator object = objects.begin(); object != objects.end();
++object) // Iterate over the objects
{
std::string type = (*object)["type"].as<std::string>(); // Get the type of the object
if ((*object)["type"].as<std::string>() == "pose") { // If the object is a pose
std::string parent_frame = ((*object)["parent_frame"].IsDefined()) ?
(*object)["parent_frame"].as<std::string>() :
"msg_defined";
std::function<void(std::shared_ptr<geometry_msgs::msg::PoseStamped>)> poseFnc =
std::bind(
&As2ExternalObjectToTf::poseCallback, this, std::placeholders::_1,
(*object)["frame"].as<std::string>(), parent_frame);
pose_subs_.push_back(
this->create_subscription<geometry_msgs::msg::PoseStamped>(
(*object)["pose_topic"].as<std::string>(), as2_names::topics::self_localization::qos,
poseFnc));
} else if ((*object)["type"].as<std::string>() == "gps") { // If the object is a gps
if (!origin_set_) {
setupGPS();
origin_set_ = true;
}
As2ExternalObjectToTf::gps_poses[(*object)["frame"].as<std::string>()] = gps_object();
std::string parent_frame = ((*object)["parent_frame"].IsDefined()) ?
(*object)["parent_frame"].as<std::string>() :
"earth";
std::function<void(std::shared_ptr<sensor_msgs::msg::NavSatFix>)> gpsFnc =
std::bind(
&As2ExternalObjectToTf::gpsCallback, this, std::placeholders::_1,
(*object)["frame"].as<std::string>(), parent_frame);
std::function<void(std::shared_ptr<std_msgs::msg::Float32>)> azimuthFnc =
std::bind(
&As2ExternalObjectToTf::azimuthCallback, this, std::placeholders::_1,
(*object)["frame"].as<std::string>(), parent_frame);
gps_subs_.push_back(
this->create_subscription<sensor_msgs::msg::NavSatFix>(
(*object)["gps_topic"].as<std::string>(), as2_names::topics::self_localization::qos,
gpsFnc));
azimuth_subs_.push_back(
this->create_subscription<std_msgs::msg::Float32>(
(*object)["azimuth_topic"].as<std::string>(), as2_names::topics::self_localization::qos,
azimuthFnc));
} else if ((*object)["type"].as<std::string>() == "pose_static") {
std::string parent_frame = ((*object)["parent_frame"].IsDefined()) ?
(*object)["parent_frame"].as<std::string>() :
"earth";
std::string frame = (*object)["frame"].as<std::string>();
geometry_msgs::msg::TransformStamped static_transform;
static_transform.header.frame_id = parent_frame;
static_transform.child_frame_id = frame;
static_transform.transform.translation.x = (*object)["pose"]["position"]["x"].as<double>();
static_transform.transform.translation.y = (*object)["pose"]["position"]["y"].as<double>();
static_transform.transform.translation.z = (*object)["pose"]["position"]["z"].as<double>();
double roll = (*object)["pose"]["orientation"]["r"].as<double>();
double pitch = (*object)["pose"]["orientation"]["p"].as<double>();
double yaw = (*object)["pose"]["orientation"]["y"].as<double>();
geometry_msgs::msg::Quaternion quat;
as2::frame::eulerToQuaternion(roll, pitch, yaw, quat);
static_transform.transform.rotation = quat;
staticTfBroadcaster->sendTransform(static_transform);
} else if ((*object)["type"].as<std::string>() == "gps_static") {
if (!origin_set_) {
setupGPS();
origin_set_ = true;
}
std::string parent_frame = ((*object)["parent_frame"].IsDefined()) ?
(*object)["parent_frame"].as<std::string>() :
"earth";
std::string frame = (*object)["frame"].as<std::string>();
std::shared_ptr<sensor_msgs::msg::NavSatFix> gps_pose =
std::make_shared<sensor_msgs::msg::NavSatFix>();
gps_pose->latitude = (*object)["gps_pose"]["lat"].as<double>();
gps_pose->longitude = (*object)["gps_pose"]["lon"].as<double>();
gps_pose->altitude = (*object)["gps_pose"]["alt"].as<double>();
float bank = ((*object)["orientation"]["bank"].IsDefined()) ?
(*object)["orientation"]["bank"].as<float>() :
0.0;
float elevation = ((*object)["orientation"]["elevation"].IsDefined()) ?
(*object)["orientation"]["elevation"].as<float>() :
0.0;
float azimuth = ((*object)["orientation"]["azimuth"].IsDefined()) ?
(*object)["orientation"]["azimuth"].as<float>() :
0.0;
geometry_msgs::msg::TransformStamped static_transform;
static_transform = gpsToTransform(gps_pose, azimuth, elevation, frame, parent_frame);
staticTfBroadcaster->sendTransform(static_transform);
} else if ((*object)["type"].as<std::string>() == "mocap") {
std::vector<std::tuple<std::string, std::string>> mappings;
for (auto mapping : (*object)["rigid_bodies"]) {
mappings.push_back(
std::make_tuple(
mapping["rigid_body"].as<std::string>(),
mapping["frame"].as<std::string>()));
}
std::function<void(mocap4r2_msgs::msg::RigidBodies::SharedPtr)> mocapFnc =
std::bind(&As2ExternalObjectToTf::mocapCallback, this, std::placeholders::_1, mappings);
this->create_subscription<mocap4r2_msgs::msg::RigidBodies>(
mocap_topic_, as2_names::topics::self_localization::qos, mocapFnc);
} else {
RCLCPP_WARN(
this->get_logger(),
"Invalid type for object '%s', types are: pose || gps || pose_static || "
"gps_static || mocap",
type.c_str());
}
RCLCPP_INFO(
this->get_logger(), "Object '%s' Succesfully loaded from config file",
type.c_str());
}
} catch (YAML::Exception & e) {
RCLCPP_ERROR(this->get_logger(), "YAML error: %s", e.what());
}
setTrasformSrv = this->create_service<as2_msgs::srv::AddStaticTransform>(
this->generate_local_name("add_static_transform"),
std::bind(
&As2ExternalObjectToTf::addStaticTransform, this, std::placeholders::_1,
std::placeholders::_2));
setTrasformGpsSrv = this->create_service<as2_msgs::srv::AddStaticTransformGps>(
this->generate_local_name("add_static_transform_gps"),
std::bind(
&As2ExternalObjectToTf::addStaticTransformGps, this, std::placeholders::_1,
std::placeholders::_2));
}
void As2ExternalObjectToTf::setupNode()
{
tfBroadcaster = std::make_unique<tf2_ros::TransformBroadcaster>(*this);
staticTfBroadcaster = std::make_unique<tf2_ros::StaticTransformBroadcaster>(*this);
loadObjects(config_path_);
}
void As2ExternalObjectToTf::setupGPS()
{
get_origin_srv_ = this->create_client<as2_msgs::srv::GetOrigin>(
as2_names::services::gps::get_origin); // Should be same origin for every drone ?
while (!get_origin_srv_->wait_for_service(std::chrono::seconds(3))) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(this->get_logger(), "Interrupted while waiting for the service. Exiting.");
break;
}
RCLCPP_INFO(this->get_logger(), "service not available, waiting again...");
}
auto request = std::make_shared<as2_msgs::srv::GetOrigin::Request>();
request->structure_needs_at_least_one_member = 0;
bool success = false; // TO-DO: Improve this
// Wait for the result.
while (!success) {
auto result = get_origin_srv_->async_send_request(request);
if (rclcpp::spin_until_future_complete(
this->get_node_base_interface(), result,
std::chrono::seconds(1)) ==
rclcpp::FutureReturnCode::SUCCESS)
{
// ;
} else {
RCLCPP_ERROR(this->get_logger(), "Failed to call service get origin");
return;
}
auto result_obj = *result.get();
success = result_obj.success;
if (success) {
origin_ = std::make_unique<geographic_msgs::msg::GeoPoint>(result_obj.origin);
RCLCPP_INFO(
this->get_logger(), "Origin in: lat: %f, lon %f, alt: %f", origin_->latitude,
origin_->longitude, origin_->altitude);
gps_handler = std::make_unique<as2::gps::GpsHandler>(
origin_->latitude, origin_->longitude,
origin_->altitude);
} else {
RCLCPP_WARN(this->get_logger(), "Get origin request not successful, trying again...");
}
}
}
void As2ExternalObjectToTf::run() {}
void As2ExternalObjectToTf::cleanupNode() {}
using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
CallbackReturn As2ExternalObjectToTf::on_configure(const rclcpp_lifecycle::State & _state)
{
// Set subscriptions, publishers, services, actions, etc. here.
setupNode();
return CallbackReturn::SUCCESS;
}
CallbackReturn As2ExternalObjectToTf::on_deactivate(const rclcpp_lifecycle::State & _state)
{
// Clean up subscriptions, publishers, services, actions, etc. here.
return CallbackReturn::SUCCESS;
}
CallbackReturn As2ExternalObjectToTf::on_shutdown(const rclcpp_lifecycle::State & _state)
{
// Clean other resources here.
return CallbackReturn::SUCCESS;
}