Program Listing for File raw_odometry.hpp
↰ Return to documentation for file (/tmp/ws/src/aerostack2/as2_state_estimator/plugins/raw_odometry/include/raw_odometry.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 RAW_ODOMETRY_HPP_
#define RAW_ODOMETRY_HPP_
#include <string>
#include <utility>
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include <geographic_msgs/msg/geo_point.hpp>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <as2_core/names/services.hpp>
#include <as2_core/utils/gps_utils.hpp>
#include <as2_msgs/srv/get_origin.hpp>
#include <as2_msgs/srv/set_origin.hpp>
#include "as2_state_estimator/plugin_base.hpp"
namespace raw_odometry
{
class Plugin : public as2_state_estimator_plugin_base::StateEstimatorBase
{
rclcpp::Subscription<nav_msgs::msg::Odometry>::SharedPtr odom_sub_;
rclcpp::Subscription<sensor_msgs::msg::NavSatFix>::SharedPtr gps_sub_;
rclcpp::Service<as2_msgs::srv::SetOrigin>::SharedPtr set_origin_srv_;
rclcpp::Service<as2_msgs::srv::GetOrigin>::SharedPtr get_origin_srv_;
bool use_gps_ = false;
bool set_origin_on_start_ = false;
double earth_to_map_height_ = 0.0;
double origin_lat_ = 0.0;
double origin_lon_ = 0.0;
double origin_alt_ = 0.0;
geometry_msgs::msg::TransformStamped earth_to_map_;
geographic_msgs::msg::GeoPoint::UniquePtr origin_;
sensor_msgs::msg::NavSatFix::UniquePtr gps_pose_;
public:
Plugin()
: as2_state_estimator_plugin_base::StateEstimatorBase() {}
void on_setup() override
{
std::string odom_topic = as2_names::topics::sensor_measurements::odom;
node_ptr_->get_parameter("odom_topic", odom_topic);
odom_sub_ = node_ptr_->create_subscription<nav_msgs::msg::Odometry>(
odom_topic, as2_names::topics::sensor_measurements::qos,
std::bind(&Plugin::odom_callback, this, std::placeholders::_1));
node_ptr_->get_parameter("use_gps", use_gps_);
node_ptr_->get_parameter("set_origin_on_start", set_origin_on_start_);
// publish static transform from earth to map and map to odom
geometry_msgs::msg::TransformStamped map_to_odom =
as2::tf::getTransformation(get_map_frame(), get_odom_frame(), 0, 0, 0, 0, 0, 0);
earth_to_map_ =
as2::tf::getTransformation(get_earth_frame(), get_map_frame(), 0, 0, 0, 0, 0, 0);
publish_static_transform(map_to_odom);
if (!use_gps_) {
// TODO(javilinos): MODIFY this to a initial earth to map transform (reading initial position
// from parameters or msgs )
earth_to_map_ =
as2::tf::getTransformation(get_earth_frame(), get_map_frame(), 0, 0, 0, 0, 0, 0);
publish_static_transform(earth_to_map_);
} else {
set_origin_srv_ = node_ptr_->create_service<as2_msgs::srv::SetOrigin>(
as2_names::services::gps::set_origin,
std::bind(
&Plugin::setOriginCallback, this, std::placeholders::_1,
std::placeholders::_2));
get_origin_srv_ = node_ptr_->create_service<as2_msgs::srv::GetOrigin>(
as2_names::services::gps::get_origin,
std::bind(
&Plugin::getOriginCallback, this, std::placeholders::_1,
std::placeholders::_2));
gps_sub_ = node_ptr_->create_subscription<sensor_msgs::msg::NavSatFix>(
as2_names::topics::sensor_measurements::gps, as2_names::topics::sensor_measurements::qos,
std::bind(&Plugin::gps_callback, this, std::placeholders::_1));
if (node_ptr_->has_parameter("earth_to_map_height")) {
node_ptr_->get_parameter("earth_to_map_height", earth_to_map_height_);
RCLCPP_INFO(node_ptr_->get_logger(), "Earth to map height set to %f", earth_to_map_height_);
}
if (set_origin_on_start_ && node_ptr_->has_parameter("set_origin.lat") &&
node_ptr_->has_parameter("set_origin.lon") &&
node_ptr_->has_parameter("set_origin.alt"))
{
node_ptr_->get_parameter("set_origin.lat", origin_lat_);
node_ptr_->get_parameter("set_origin.lon", origin_lon_);
node_ptr_->get_parameter("set_origin.alt", origin_alt_);
origin_ = std::make_unique<geographic_msgs::msg::GeoPoint>();
origin_->latitude = origin_lat_;
origin_->longitude = origin_lon_;
origin_->altitude = origin_alt_;
RCLCPP_INFO(
node_ptr_->get_logger(), "Origin set to %f, %f, %f", origin_lat_, origin_lon_,
origin_alt_);
} else {
RCLCPP_INFO(node_ptr_->get_logger(), "Waiting for origin to be set");
}
}
}
private:
void generate_map_frame_from_gps(
const geographic_msgs::msg::GeoPoint & origin,
const sensor_msgs::msg::NavSatFix & gps_pose)
{
as2::gps::GpsHandler gps_handler;
gps_handler.setOrigin(origin.latitude, origin.longitude, origin.altitude);
double x, y, z;
gps_handler.LatLon2Local(gps_pose.latitude, gps_pose.longitude, gps_pose.altitude, x, y, z);
if (!node_ptr_->has_parameter("earth_to_map_height")) {
earth_to_map_height_ = gps_pose.altitude - origin.altitude;
}
earth_to_map_ = as2::tf::getTransformation(
get_earth_frame(), get_map_frame(), x, y,
earth_to_map_height_, 0, 0, 0);
publish_static_transform(earth_to_map_);
}
void odom_callback(const nav_msgs::msg::Odometry::UniquePtr msg)
{
// odom should have frame_id = odom and child_frame_id = base_link
// since we only have this message for generating the tf tree we will publish the transform
// from odom to base_link directly and the transform from earth to map and map to odom will
// be the identity transform
if (msg->header.frame_id != get_odom_frame()) {
RCLCPP_ERROR(
node_ptr_->get_logger(), "Received odom in frame %s, expected %s",
msg->header.frame_id.c_str(), get_odom_frame().c_str());
return;
}
if (msg->child_frame_id != get_base_frame()) {
RCLCPP_ERROR(
node_ptr_->get_logger(),
"Received odom child_frame_id in frame %s, expected %s",
msg->child_frame_id.c_str(), get_base_frame().c_str());
return;
}
auto transform = geometry_msgs::msg::TransformStamped();
transform.header = msg->header;
transform.child_frame_id = msg->child_frame_id;
transform.transform.translation.x = msg->pose.pose.position.x;
transform.transform.translation.y = msg->pose.pose.position.y;
transform.transform.translation.z = msg->pose.pose.position.z;
transform.transform.rotation = msg->pose.pose.orientation;
publish_transform(transform); // publish transform from odom to base_link
tf2::fromMsg(transform.transform, odom_to_baselink);
tf2::fromMsg(earth_to_map_.transform, earth_to_map);
convert_odom_to_baselink_2_earth_to_baselink_transform(
odom_to_baselink, earth_to_baselink,
earth_to_map);
auto pose = geometry_msgs::msg::PoseStamped();
pose.header.frame_id = get_earth_frame();
pose.header.stamp = msg->header.stamp;
pose.pose.position.x = earth_to_baselink.getOrigin().x();
pose.pose.position.y = earth_to_baselink.getOrigin().y();
pose.pose.position.z = earth_to_baselink.getOrigin().z();
pose.pose.orientation = tf2::toMsg(earth_to_baselink.getRotation());
publish_pose(pose);
// publish twist in "base_link" frame
auto twist = geometry_msgs::msg::TwistStamped();
twist.header.frame_id = get_base_frame();
twist.header.stamp = msg->header.stamp;
twist.twist = msg->twist.twist;
publish_twist(twist);
}
void getOriginCallback(
const as2_msgs::srv::GetOrigin::Request::SharedPtr request,
as2_msgs::srv::GetOrigin::Response::SharedPtr response)
{
if (origin_) {
response->origin = *origin_;
response->success = true;
} else {
RCLCPP_WARN(node_ptr_->get_logger(), "Origin not set");
response->success = false;
}
}
void setOriginCallback(
const as2_msgs::srv::SetOrigin::Request::SharedPtr request,
as2_msgs::srv::SetOrigin::Response::SharedPtr response)
{
if (origin_) {
RCLCPP_WARN(node_ptr_->get_logger(), "Origin already set");
response->success = false;
} else {
origin_ = std::make_unique<geographic_msgs::msg::GeoPoint>(request->origin);
RCLCPP_INFO(
node_ptr_->get_logger(), "Origin set to %f, %f, %f", origin_->latitude,
origin_->longitude, origin_->altitude);
response->success = true;
generate_map_frame_from_gps(request->origin, *gps_pose_);
}
}
void gps_callback(sensor_msgs::msg::NavSatFix::UniquePtr msg)
{
// This sould only be called when the use_gps_origin is true
if (gps_pose_) {
gps_sub_.reset();
return;
}
gps_pose_ = std::move(msg);
if (set_origin_on_start_) {
if (!origin_) {
origin_ = std::make_unique<geographic_msgs::msg::GeoPoint>();
origin_->latitude = gps_pose_->latitude;
origin_->longitude = gps_pose_->longitude;
origin_->altitude = gps_pose_->altitude;
RCLCPP_WARN(node_ptr_->get_logger(), "Careful, using GPS pose as origin");
RCLCPP_INFO(
node_ptr_->get_logger(), "Origin set to %f, %f, %f", origin_lat_, origin_lon_,
origin_alt_);
}
RCLCPP_INFO(
node_ptr_->get_logger(), "GPS Callback: Map GPS pose set to %f, %f, %f",
gps_pose_->latitude, gps_pose_->longitude, gps_pose_->altitude);
generate_map_frame_from_gps(*origin_, *gps_pose_);
}
}
};
} // namespace raw_odometry
#endif // RAW_ODOMETRY_HPP_