ROS小车记录系列(四)利用cartographer定位,修改源码发布pose话题

(四)利用cartographer定位


__半个月没有更新了,一直在解决建图效果问题,因为是用的自己的IMU,误差有点大(-_-!!!),陆续买了两个imu,误差依旧大,转几圈都找不到北了。加上我的odom计算是依赖IMU的,最终结果就是一片模糊,还不如纯激光雷达效果。最终还是狠下心,花了2千,买了个精度稳定性都靠谱的。
__定位这事本来想用amcl做,后来听说现在大家都用cartographer了,我也跟着做一下,按照 这篇博客一通配置,果然跑起来了,看到定位效果确实好啊,又准稳定性又好。但是,重点来了,但是定位后我要pose信息啊,不然后面怎么导航,查看topic list,卵,没有pose相关话题。。。这篇文章就记录这一骚操作,从cartographer中发布出pose信息,简单两步走。
(更正1、实际上move_base是通过TF树查询位置的,不需要单独再发布pose信息,这里发出来就留给上位机界面展示车子位置使用吧;
更正2:使用这种方式获取位姿信息,只有在cartographer配置参数provide_odom_frame = true时才有效,而如果使用odom数据建图、导航,该参数需要设置为false,所以下述方法无效,发布的信息是错误的。

第一,cartographer源码目录,打开src/cartographer_ros/cartographer_ros/cartographer_ros/node.h文件,182行插入一句:

::ros::Publisher _pose_pub;

第二,cartographer源码目录,打开src/cartographer_ros/cartographer_ros/cartographer_ros/node.cc文件,修改后完整include如下:

#include "Eigen/Core"
#include "cartographer/common/configuration_file_resolver.h"
#include "cartographer/common/lua_parameter_dictionary.h"
#include "cartographer/common/make_unique.h"
#include "cartographer/common/port.h"
#include "cartographer/common/time.h"
#include "cartographer/mapping/proto/submap_visualization.pb.h"
#include "cartographer/mapping/pose_graph_interface.h"
#include "cartographer/sensor/point_cloud.h"
#include "cartographer/transform/rigid_transform.h"
#include "cartographer/transform/transform.h"
#include "cartographer_ros/msg_conversion.h"
#include "cartographer_ros/sensor_bridge.h"
#include "cartographer_ros/tf_bridge.h"
#include "cartographer_ros/time_conversion.h"
#include "cartographer_ros_msgs/StatusCode.h"
#include "cartographer_ros_msgs/StatusResponse.h"
#include "glog/logging.h"
#include "nav_msgs/Odometry.h"
#include "ros/serialization.h"
#include "sensor_msgs/PointCloud2.h"
#include "tf2_eigen/tf2_eigen.h"
#include "visualization_msgs/MarkerArray.h"
#include "tf/transform_broadcaster.h"
#include <geometry_msgs/Pose2D.h>

182行找到函数:

void Node::PublishTrajectoryStates(const ::ros::WallTimerEvent& timer_event){

下面插入一句:

_pose_pub = node_handle_.advertise<geometry_msgs::Pose2D>("pose_nav", 10);

235行插入:

geometry_msgs::Pose2D pos_now;

264行插入:

geometry_msgs::Transform transform = ToGeometryMsgTransform(
                  tracking_to_map * (*trajectory_state.published_to_tracking));
pos_now.x = static_cast<double>(transform.translation.x);
pos_now.y = static_cast<double>(transform.translation.y);
pos_now.theta = tf::getYaw(transform.rotation);
_pose_pub.publish(pos_now);

好了,最终重新编译一下cartographer:

catkin_make_isolated --install --use-ninja

现在,启动机器,启动建图或者定位,查看下topic,惊不惊喜,意不意外,多了个topic:pose_nav,查看下,刷刷的发布着位置信息,频率100Hz。
|
|
|
|
|
PS:还是贴个完整的修改后函数吧:

void Node::PublishTrajectoryStates(const ::ros::WallTimerEvent& timer_event) {
  carto::common::MutexLocker lock(&mutex_);
  _pose_pub = node_handle_.advertise<geometry_msgs::Pose2D>("pose_nav", 10);
  for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {
    const auto& trajectory_state = entry.second;

    auto& extrapolator = extrapolators_.at(entry.first);
    // We only publish a point cloud if it has changed. It is not needed at high
    // frequency, and republishing it would be computationally wasteful.
    if (trajectory_state.local_slam_data->time !=
        extrapolator.GetLastPoseTime()) {
      if (scan_matched_point_cloud_publisher_.getNumSubscribers() > 0) {
        // TODO(gaschler): Consider using other message without time
        // information.
        carto::sensor::TimedPointCloud point_cloud;
        point_cloud.reserve(trajectory_state.local_slam_data
                                ->range_data_in_local.returns.size());
        for (const Eigen::Vector3f point :
             trajectory_state.local_slam_data->range_data_in_local.returns) {
          Eigen::Vector4f point_time;
          point_time << point, 0.f;
          point_cloud.push_back(point_time);
        }
        scan_matched_point_cloud_publisher_.publish(ToPointCloud2Message(
            carto::common::ToUniversal(trajectory_state.local_slam_data->time),
            node_options_.map_frame,
            carto::sensor::TransformTimedPointCloud(
                point_cloud, trajectory_state.local_to_map.cast<float>())));
      }
      extrapolator.AddPose(trajectory_state.local_slam_data->time,
                           trajectory_state.local_slam_data->local_pose);
    }

    geometry_msgs::TransformStamped stamped_transform;
    // If we do not publish a new point cloud, we still allow time of the
    // published poses to advance. If we already know a newer pose, we use its
    // time instead. Since tf knows how to interpolate, providing newer
    // information is better.
    const ::cartographer::common::Time now = std::max(
        FromRos(ros::Time::now()), extrapolator.GetLastExtrapolatedTime());
    stamped_transform.header.stamp = ToRos(now);

    const Rigid3d tracking_to_local = [&] {
      if (trajectory_state.trajectory_options.publish_frame_projected_to_2d) {
        return carto::transform::Embed3D(
            carto::transform::Project2D(extrapolator.ExtrapolatePose(now)));
      }
      return extrapolator.ExtrapolatePose(now);
    }();

    const Rigid3d tracking_to_map =
        trajectory_state.local_to_map * tracking_to_local;

    geometry_msgs::Pose2D pos_now;
    if (trajectory_state.published_to_tracking != nullptr) {
      if (trajectory_state.trajectory_options.provide_odom_frame) {
        std::vector<geometry_msgs::TransformStamped> stamped_transforms;

        stamped_transform.header.frame_id = node_options_.map_frame;
        stamped_transform.child_frame_id =
            trajectory_state.trajectory_options.odom_frame;
        stamped_transform.transform =
            ToGeometryMsgTransform(trajectory_state.local_to_map);
        stamped_transforms.push_back(stamped_transform);

        stamped_transform.header.frame_id =
            trajectory_state.trajectory_options.odom_frame;
        stamped_transform.child_frame_id =
            trajectory_state.trajectory_options.published_frame;
        stamped_transform.transform = ToGeometryMsgTransform(
            tracking_to_local * (*trajectory_state.published_to_tracking));
        stamped_transforms.push_back(stamped_transform);

        tf_broadcaster_.sendTransform(stamped_transforms);
      } else {
        stamped_transform.header.frame_id = node_options_.map_frame;
        stamped_transform.child_frame_id =
            trajectory_state.trajectory_options.published_frame;
        stamped_transform.transform = ToGeometryMsgTransform(
            tracking_to_map * (*trajectory_state.published_to_tracking));
        tf_broadcaster_.sendTransform(stamped_transform);
      }

      geometry_msgs::Transform transform = ToGeometryMsgTransform(
                tracking_to_map * (*trajectory_state.published_to_tracking));

      pos_now.x = static_cast<double>(transform.translation.x);
      pos_now.y = static_cast<double>(transform.translation.y);
      pos_now.theta = tf::getYaw(transform.rotation);
      _pose_pub.publish(pos_now);
    }
  }
}
Cartographer_ros 是一个在 ROS 中使用 Google Cartographer 进行 2D 和 3D SLAM 的软件包。它提供了一个 ROS 节点,可以将 Cartographer 的输出与 ROS 生态系统中的其他软件包集成起来。 Cartographer_ros 的源代码框架如下: 1. launch 文件:Cartographer_ros 使用 launch 文件来配置和启动 ROS 节点。launch 文件包括 cartographer.launch、demo_backpack_2d.launch、demo_backpack_3d.launch 等。 2. ROS 节点:Cartographer_ros 的主要节点是 cartographer_node,它负责运行 Cartographer,并将数据发布ROS 话题上。cartographer_node 本身是由多个模块组成的,每个模块负责处理不同的任务。 3. ROS 话题cartographer_node 将数据发布ROS 话题上,这些话题包括激光雷达数据、里程计数据、地图数据等。 4. Cartographer 模块:CartographerCartographer_ros 的核心部分,它包括了多个模块,如 sensor_bridge、mapping、localization 等。这些模块负责处理传感器数据、建立地图和定位机器人。 5. ROS 服务:Cartographer_ros 还提供了一些 ROS 服务,比如 start_trajectory、finish_trajectory 等,这些服务可以用于控制 Cartographer 的运行。 6. 消息定义:Cartographer_ros 使用了自定义的消息类型,如 cartographer_ros_msgs/TrajectoryState、cartographer_ros_msgs/SubmapList 等。 总的来说,Cartographer_ros 的源代码框架是基于 ROS 架构设计的,它通过 ROS 节点、话题和服务来实现 Cartographer 的集成和控制。
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值