Program Listing for File mocap_pose.hpp
↰ Return to documentation for file (/tmp/ws/src/aerostack2/as2_state_estimator/plugins/mocap_pose/include/mocap_pose.hpp
)
// 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.
#ifndef MOCAP_POSE_HPP_
#define MOCAP_POSE_HPP_
#include <tf2/LinearMath/Quaternion.h>
#include <tf2/LinearMath/Vector3.h>
#include <string>
#include <vector>
#include <mocap4r2_msgs/msg/rigid_bodies.hpp>
#include <rclcpp/duration.hpp>
#include "as2_state_estimator/plugin_base.hpp"
namespace mocap_pose
{
class Plugin : public as2_state_estimator_plugin_base::StateEstimatorBase
{
rclcpp::Subscription<mocap4r2_msgs::msg::RigidBodies>::SharedPtr rigid_bodies_sub_;
tf2::Transform earth_to_map_ = tf2::Transform::getIdentity();
const tf2::Transform map_to_odom_ = tf2::Transform::getIdentity(); // ALWAYS IDENTITY
tf2::Transform odom_to_base_ = tf2::Transform::getIdentity();
bool has_earth_to_map_ = false;
std::string mocap_topic_;
std::string rigid_body_name_;
double twist_alpha_ = 1.0;
double orientation_alpha_ = 1.0;
geometry_msgs::msg::PoseStamped last_pose_msg_;
public:
Plugin()
: as2_state_estimator_plugin_base::StateEstimatorBase() {}
void on_setup() override
{
node_ptr_->get_parameter("mocap_topic", mocap_topic_);
if (mocap_topic_.empty()) {
RCLCPP_ERROR(node_ptr_->get_logger(), "Parameter 'mocap_topic' not set");
throw std::runtime_error("Parameter 'mocap_topic' not set");
}
node_ptr_->get_parameter("rigid_body_name", rigid_body_name_);
if (rigid_body_name_.empty()) {
RCLCPP_ERROR(node_ptr_->get_logger(), "Parameter 'rigid_body_name' not set");
throw std::runtime_error("Parameter 'rigid_body_name' not set");
}
try {
twist_alpha_ = node_ptr_->get_parameter("twist_smooth_filter_cte").as_double();
} catch (const rclcpp::ParameterTypeException & e) {
RCLCPP_INFO(
node_ptr_->get_logger(),
"Parameter 'twist_smooth_filter_cte' not set. Filter disabled. Using default value: %f",
twist_alpha_);
}
try {
orientation_alpha_ = node_ptr_->get_parameter("orientation_smooth_filter_cte").as_double();
} catch (const rclcpp::ParameterTypeException & e) {
RCLCPP_INFO(
node_ptr_->get_logger(),
"Parameter 'orientation_smooth_filter_cte' not set. Filter disabled. Using "
"default value: %f",
orientation_alpha_);
}
rigid_bodies_sub_ = node_ptr_->create_subscription<mocap4r2_msgs::msg::RigidBodies>(
mocap_topic_, rclcpp::QoS(1000),
std::bind(&Plugin::rigid_bodies_callback, this, std::placeholders::_1));
// publish static transform from earth to map and map to odom
// TODO(javilinos): MODIFY this to a initial earth to map transform
// (reading initial position from parameters or msgs )
geometry_msgs::msg::TransformStamped map_to_odom =
as2::tf::getTransformation(get_map_frame(), get_odom_frame(), 0, 0, 0, 0, 0, 0);
publish_static_transform(map_to_odom);
has_earth_to_map_ = false;
}
const geometry_msgs::msg::TwistStamped & twist_from_pose(
const geometry_msgs::msg::PoseStamped & pose,
std::vector<tf2::Transform> * data = nullptr)
{
const auto last_time = twist_msg_.header.stamp;
auto dt = (rclcpp::Time(pose.header.stamp) - last_time).seconds();
// RCLCPP_INFO(node_ptr_->get_logger(), "dt: %f", dt);
if (dt <= 0) {
RCLCPP_WARN(node_ptr_->get_logger(), "dt <= 0");
return twist_msg_;
}
static tf2::Vector3 last_pose =
tf2::Vector3(pose.pose.position.x, pose.pose.position.y, pose.pose.position.z);
tf2::Vector3 current_pose =
tf2::Vector3(pose.pose.position.x, pose.pose.position.y, pose.pose.position.z);
tf2::Vector3 vel = (current_pose - last_pose) / dt;
last_pose = current_pose;
vel = twist_alpha_ * vel + (1 - twist_alpha_) * tf2::Vector3(
twist_msg_.twist.linear.x,
twist_msg_.twist.linear.y,
twist_msg_.twist.linear.z);
twist_msg_.header.stamp = pose.header.stamp;
twist_msg_.twist.linear.x = vel.x();
twist_msg_.twist.linear.y = vel.y();
twist_msg_.twist.linear.z = vel.z();
// TODO(javilinos): add angular velocity -> this_could_be_obtained_from_imu
twist_msg_.twist.angular.x = 0;
twist_msg_.twist.angular.y = 0;
twist_msg_.twist.angular.z = 0;
if (data != nullptr) {
std::vector<tf2::Transform> * transforms = (std::vector<tf2::Transform> *)data;
tf2::Transform earth_to_map = transforms->at(0);
tf2::Transform map_to_odom = transforms->at(1);
tf2::Transform odom_to_base = transforms->at(2);
vel = tf2::quatRotate(
(odom_to_base.inverse() * map_to_odom.inverse() * earth_to_map.inverse()).getRotation(),
vel);
// FIXME: CLEAN STATIC
static geometry_msgs::msg::TwistStamped twist_msg;
twist_msg.header.stamp = pose.header.stamp;
twist_msg.header.frame_id = get_base_frame();
twist_msg.twist.linear.x = vel.x();
twist_msg.twist.linear.y = vel.y();
twist_msg.twist.linear.z = vel.z();
return twist_msg;
} else {
return twist_msg_;
}
}
geometry_msgs::msg::TwistStamped twist_msg_;
private:
void rigid_bodies_callback(const mocap4r2_msgs::msg::RigidBodies::SharedPtr msg)
{
auto pose_msg = geometry_msgs::msg::PoseStamped();
pose_msg.header = msg->header;
for (const auto & rigid_body : msg->rigidbodies) {
if (rigid_body.rigid_body_name == rigid_body_name_) {
pose_msg.pose = rigid_body.pose;
break;
}
}
process_mocap_pose(pose_msg);
}
void process_mocap_pose(const geometry_msgs::msg::PoseStamped & msg)
{
// mocap_pose could have a different frame_id, we will publish the transform from earth to
// base_link without checking origin frame_id
if (!has_earth_to_map_) {
earth_to_map_ = tf2::Transform(
tf2::Quaternion(
msg.pose.orientation.x, msg.pose.orientation.y, msg.pose.orientation.z,
msg.pose.orientation.w),
tf2::Vector3(msg.pose.position.x, msg.pose.position.y, msg.pose.position.z));
geometry_msgs::msg::TransformStamped earth_to_map;
earth_to_map.transform = tf2::toMsg(earth_to_map_);
earth_to_map.header.stamp = msg.header.stamp;
earth_to_map.header.frame_id = get_earth_frame();
earth_to_map.child_frame_id = get_map_frame();
publish_static_transform(earth_to_map);
has_earth_to_map_ = true;
}
odom_to_base_ =
map_to_odom_.inverse() * earth_to_map_.inverse() *
tf2::Transform(
tf2::Quaternion(
msg.pose.orientation.x, msg.pose.orientation.y,
msg.pose.orientation.z, msg.pose.orientation.w),
tf2::Vector3(msg.pose.position.x, msg.pose.position.y, msg.pose.position.z));
geometry_msgs::msg::TransformStamped odom_to_base_msg;
odom_to_base_msg.transform = tf2::toMsg(odom_to_base_);
odom_to_base_msg.header.stamp = msg.header.stamp;
odom_to_base_msg.header.frame_id = get_odom_frame();
odom_to_base_msg.child_frame_id = get_base_frame();
publish_transform(odom_to_base_msg);
// Publish pose
geometry_msgs::msg::PoseStamped pose_msg;
pose_msg.header.stamp = msg.header.stamp;
pose_msg.header.frame_id = get_earth_frame();
pose_msg.pose = msg.pose;
pose_msg.pose.orientation.x = orientation_alpha_ * msg.pose.orientation.x +
(1 - orientation_alpha_) * last_pose_msg_.pose.orientation.x;
pose_msg.pose.orientation.y = orientation_alpha_ * msg.pose.orientation.y +
(1 - orientation_alpha_) * last_pose_msg_.pose.orientation.y;
pose_msg.pose.orientation.z = orientation_alpha_ * msg.pose.orientation.z +
(1 - orientation_alpha_) * last_pose_msg_.pose.orientation.z;
pose_msg.pose.orientation.w = orientation_alpha_ * msg.pose.orientation.w +
(1 - orientation_alpha_) * last_pose_msg_.pose.orientation.w;
publish_pose(pose_msg);
last_pose_msg_ = pose_msg;
// Compute twist from mocap_pose
auto data = std::vector<tf2::Transform>{earth_to_map_, map_to_odom_, odom_to_base_};
publish_twist(twist_from_pose(pose_msg, &data));
}
};
} // namespace mocap_pose
#endif // MOCAP_POSE_HPP_