深入分析常规ROS机器人如何使用Navigation导航包实现实时定位

   本篇文章主要分析,常规的ROS机器人是如何使用Navigation导航包实现实时定位的,定位精度的决定性因素等内容,结构上分为详细介绍、概括总结、深入思考三大部分。


   注:本文首发于古月居,原文链接如下:

   https://www.guyuehome.com/45110

   本篇文章我首发在古月居,因版权原因,在CSDN不能放全文,只能放一小部分,欢迎大家前往古月居查看完整文章!!!,链接如上↑↑↑


在这里插入图片描述


   一、详细介绍

   常规的ROS机器人一般都会搭载,轮式里程计(编码器),姿态传感器(IMU)、激光雷达等感知传感器。

   rqt_graph是ROS中进行分析的常用工具,下图是航天三院开发的轻舟机器人运行时的节点关系图(点击或拖拽可查看大图),从下图可以看出Navigation导航包的“指挥中心“”move_base节点订阅了/odom_ekf节点发布的/odom_ekf话题,/odom_ekf中的内容是机器人搭载的轮式里程计(编码器)经过推位得到定位信息/odom与姿态传感器(IMU)的定位信息经过/robot_pose_ekf节点,即使用扩展卡尔曼滤波器(EKF)进行融合后的定位信息。

   那么我们的ROS机器人是否就是使用这个定位信息作为机器人的实时位置进行路径规划及其他应用呢?答案是否定的,下面给出解释

   move_base节点中是通过调用getRobotPose函数来获取机器人当前的位姿的

getRobotPose(global_pose, planner_costmap_ros_);

   getRobotPose函数的核心代码如下,可以看出getRobotPose函数实际上是通过监听tf树中的map坐标系与base_link坐标系的关系,从而得到map坐标系下的base_link的坐标,也就是map坐标系下机器人的位姿信息,也就是说机器人的实时定位信息是通过监听tf树中map坐标系与base_link坐标系的变换关系来计算获得的,并非使用了订阅的/odom_ekf话题中的消息。

    tf2::toMsg(tf2::Transform::getIdentity(), global_pose.pose);
    geometry_msgs::PoseStamped robot_pose;
    tf2::toMsg(tf2::Transform::getIdentity(), robot_pose.pose);
    robot_pose.header.frame_id = robot_base_frame_;
    robot_pose.header.stamp = ros::Time(); // latest available
    ros::Time current_time = ros::Time::now();  // save time for checking tf delay later

    // get robot pose on the given costmap frame
    try
    {
       //通过tf获取map到base_link的关系,那么也就是map下base_link的坐标,也就是map下机器人的坐标
      tf_.transform(robot_pose, global_pose, costmap->getGlobalFrameID());
    }

   tf中的transform函数的具体代码如下:(lookupTransform是tf树的监听函数)


  //tf中的transform函数的具体代码如下:

 template <class T>
    T& transform(const T& in, T& out, 
		 const std::string& target_frame, ros::Duration timeout=ros::Duration(0.0)) const
  {
    // do the transform
    tf2::doTransform(in, out, lookupTransform(target_frame, tf2::getFrameId(in), tf2::getTimestamp(in), timeout));
    return out;
  }

   轻舟机器人运行时的tf树如下图所示,可以看出map坐标系与base_link坐标系之间还存在一个odom坐标系,map坐标系与odom的坐标变换关系是由/amcl节点广播出来的,odom坐标系与base_link坐标系的坐标变换关系是/robot_pose_ekf节点广播出来的,所以,我们可以先大胆的推测,机器人的实时定位信息跟/amcl节点与/robot_pose_ekf节点均有关,且/amcl节点给出的定位信息是借助激光雷达的数据,采用粒子滤波算法(PF)估计出来的,而/robot_pose_ekf节点给出的定位信息是里程计信息和IMU信息经过扩展卡尔曼滤波(EKF)融合后得到的。

   那么它们之间的关系又是怎样的呢?下面通过解读amcl包中广播odom与map坐标系的tf关系的过程来进行解释。

   本部分的源码如下:

   geometry_msgs::PoseStamped odom_to_map;
      try
      {
        tf2::Quaternion q;
        q.setRPY(0, 0, hyps[max_weight_hyp].pf_pose_mean.v[2]);
        tf2::Transform tmp_tf(q, tf2::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0],        
                                              hyps[max_weight_hyp].pf_pose_mean.v[1],
                                              0.0));

        geometry_msgs::PoseStamped tmp_tf_stamped;
        tmp_tf_stamped.header.frame_id = base_frame_id_;
        tmp_tf_stamped.header.stamp = laser_scan->header.stamp;
        tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);

        this->tf_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);
      }
      catch(const tf2::TransformException&)
      {
        ROS_DEBUG("Failed to subtract base to odom transform");
        return;
      }

      tf2::convert(odom_to_map.pose, latest_tf_);
      latest_tf_valid_ = true;

      if (tf_broadcast_ == true)
      {
        // We want to send a transform that is good up until a
        // tolerance time so that odom can be used
        ros::Time transform_expiration = (laser_scan->header.stamp +
                                          transform_tolerance_);
        geometry_msgs::TransformStamped tmp_tf_stamped;
        tmp_tf_stamped.header.frame_id = global_frame_id_;
        tmp_tf_stamped.header.stamp = transform_expiration;
        tmp_tf_stamped.child_frame_id = odom_frame_id_;
        tf2::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);

        this->tfb_->sendTransform(tmp_tf_stamped);
        sent_first_transform_ = true;
      }

   以上源码可提取关键内容,总结如下:

   (1)获取base_link在世界坐标系map的坐标变换,即base_link在map下的坐标,存放在tmp_tf 中

tf2::Transform tmp_tf(q, tf2::Vector3(hyps[max_weight_hyp].pf_pose_mean.v[0], hyps[max_weight_hyp].pf_pose_mean.v[1]0.0));

   (2)将tmp_tf通过求逆变换inverse()表示为世界坐标系map到base_link的坐标变换,即map在base_link下的坐标,存放在tmp_tf_stamped.pose中

tf2::toMsg(tmp_tf.inverse(), tmp_tf_stamped.pose);

   (3)使用transform变换获取map到odom的变换,即map原点在odom坐标系下的坐标,存放在odom_to_map中,并进行了格式转换存放在latest_tf_中。

this->tf_->transform(tmp_tf_stamped, odom_to_map, odom_frame_id_);

   这里的具体实现过程如下:tmp_tf_stamped中存放的是世界坐标系map到base_link的坐标变换,根据此处传入的参数可知transform函数中监听了base_link到odom坐标系的坐标变换,因此,可以看成将世界坐标系map到base_link的坐标变换再进行了一次从base_link到odom的变换,进而得到了map到odom的坐标变换,即map原点在odom坐标系下的坐标,存放在odom_to_map中

在这里插入图片描述


   (4)最后,对latest_tf_求逆,得到odom–>map的变换,即odom在map坐标系下的坐标。

tf2::convert(latest_tf_.inverse(), tmp_tf_stamped.transform);

   (5)广播odom–>map的坐标变换关系,即可实现对EKF的修正。


   二、概括总结

   本部分内容欢迎前往古月居查看,链接如下:


   三、深入思考

   本部分内容欢迎前往古月居查看,链接如下:


   https://www.guyuehome.com/45110

   本篇文章我首发在古月居,因版权原因,在CSDN不能放全文,只能放一小部分,欢迎大家前往古月居查看完整文章!!!,链接如上↑↑↑


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
AMCL(Adaptive Monte Carlo Localization)是ROS中常用的定位算法,它可以在机器人运动时根据传感器数据进行自适应定位。本文将介绍如何使用C++实现ROS机器人使用amcl功能进行定位。 1. 安装AMCL功能 在终端中输入以下命令,安装AMCL功能和依赖项。 ``` sudo apt-get install ros-kinetic-amcl sudo apt-get install ros-kinetic-move-base-msgs sudo apt-get install ros-kinetic-geometry-msgs ``` 2. 创建ROS 在终端中输入以下命令,创建ROS。 ``` catkin_create_pkg amcl_demo roscpp std_msgs geometry_msgs move_base_msgs ``` 3. 创建ROS节点 在src文件夹中创建amcl_demo.cpp文件,并添加以下代码。 ```cpp #include <ros/ros.h> #include <geometry_msgs/PoseWithCovarianceStamped.h> #include <geometry_msgs/PoseStamped.h> #include <move_base_msgs/MoveBaseAction.h> #include <actionlib/client/simple_action_client.h> typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient; class AmclDemo { public: AmclDemo() { // 订阅AMCL定位结果 amcl_pose_sub_ = nh_.subscribe("/amcl_pose", 1, &AmclDemo::amclPoseCallback, this); // 发布目标点 goal_pub_ = nh_.advertise<geometry_msgs::PoseStamped>("/move_base_simple/goal", 1); // 连接move_base节点 move_base_client_ = new MoveBaseClient("move_base", true); ROS_INFO("Waiting for move_base action server..."); move_base_client_->waitForServer(); ROS_INFO("Connected to move_base action server"); } private: void amclPoseCallback(const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) { // 获取机器人在地图上的位置 current_pose_ = msg->pose.pose; } void setGoal(double x, double y, double theta) { // 发布目标点 geometry_msgs::PoseStamped goal; goal.header.frame_id = "map"; goal.header.stamp = ros::Time::now(); goal.pose.position.x = x; goal.pose.position.y = y; goal.pose.orientation = tf::createQuaternionMsgFromYaw(theta); goal_pub_.publish(goal); // 发送目标点给move_base节点 move_base_msgs::MoveBaseGoal move_goal; move_goal.target_pose = goal; move_base_client_->sendGoal(move_goal); } ros::NodeHandle nh_; ros::Subscriber amcl_pose_sub_; ros::Publisher goal_pub_; MoveBaseClient *move_base_client_; geometry_msgs::Pose current_pose_; }; int main(int argc, char **argv) { ros::init(argc, argv, "amcl_demo"); AmclDemo amcl_demo; ros::spin(); return 0; } ``` 4. 编译ROS 在终端中输入以下命令,编译ROS。 ``` cd ~/catkin_ws catkin_make ``` 5. 运行ROS节点 在终端中输入以下命令,运行ROS节点。 ``` rosrun amcl_demo amcl_demo ``` 6. 发布目标点 在终端中输入以下命令,发布目标点。 ``` rostopic pub /move_base_simple/goal geometry_msgs/PoseStamped '{header: {stamp: now, frame_id: "map"}, pose: {position: {x: 1.0, y: 1.0, z: 0.0}, orientation: {w: 1.0}}}' ``` 以上就是使用C++实现ROS机器人使用amcl功能进行定位的全部流程。需要注意的是,具体的实现方式需要根据机器人的硬件和软件环境进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕羽★

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值