如何定期更新 ROS 代价地图位置?

How to update ROS costmap position periodically?

我遇到了以下问题:

我有一个占用网格,该网格基于从我的机器人上插入的摄像头接收到的环境信息。由于机器人应该在未知区域四处移动,因此其原始上下文中没有 "global costmap"。我们使用从相机接收到的信息作为 "global costmap"。目前,全局成本图配置为链接到 base_link-frame 的 static_layer(我需要它是 static_layer,因为它有多个成本值,只有 static_layer 及其 "trinary_costmap" 参数可以选择映射中间 cost_values 范围 0-255)。由于它链接到 base_link-frame,它会永久地与框架一起移动。但是由于成本地图以每秒 2 个占用网格的频率更新,这种永久移动会伪造成本地图信息(因为在新的占用网格发布之前它应该是静态地图)。

我的想法是根据 the ros tutorial for adding a new dynamic frame 创建一个新框架,它采用 base_link 的当前位置并以特定速率更新其位置。
因此,我的源代码如下所示:

int main(int argc, char** argv) {
        ros::init(argc, argv, "path_segmentation_client");     // initialise ROS
        ros::NodeHandle nh;                                   // create NodeHandle for the node
        ros::ServiceClient client = nh.serviceClient<path_detection_cnn::segmentation_to_occgrid>("segmentation_to_occgrid");    //create client for segmentation_to_occgrid-service
        path_detection_cnn::segmentation_to_occgrid srv;    //create service-instance
        double rate;    //Variable to store loop rate
        nh.getParam("/path_segmentation_client/looprate", rate);    //get loop-rate from parameters
        ros::Rate r(rate);    //apply loop-rate to ros
        ros::Publisher map_pub = nh.advertise<nav_msgs::OccupancyGrid>("prediction_occ_grid",1000);    //declare publisher for publishing occupancy grid

        static tf2_ros::StaticTransformBroadcaster brs;
        geometry_msgs::TransformStamped global_costmap_link;
        global_costmap_link.header.stamp = ros::Time::now();
        global_costmap_link.header.frame_id = "base_link";
        global_costmap_link.child_frame_id = "global_costmap_link";
        global_costmap_link.transform.translation.x = 0; 
        global_costmap_link.transform.translation.y = 0;
        global_costmap_link.transform.translation.z = 0.0;
        global_costmap_link.transform.rotation.x = 0;
        global_costmap_link.transform.rotation.y = 0;
        global_costmap_link.transform.rotation.z = 0;
        global_costmap_link.transform.rotation.w = 1;
        while(nh.ok()){
            client.waitForExistence();
            if (client.call(srv))
            {
                nav_msgs::OccupancyGrid occGrid = get_occupancy_grid(srv.response.topViewMsg);    //convert topView to Occupancy Grid
                map_pub.publish(occGrid);    // publish occupancyGrid
                brs.sendTransform(global_costmap_link); //update location of costmap_origin
                r.sleep();    //sleep to keep loop rate
            }
            else
            {
                ROS_ERROR("Failed to call service segmentation_to_occgrid.");
            }
        }

        return 0;
}

如您所见,我使用客户端服务系统获取数据并将其转换为占用网格,创建一个名为 "global_costmap_link" 的框架作为 base_link 的子框架并发布frame和内格的占用率相同。这样做,不幸的是,新的 "global_costmap_link"-frame 也继续与 base_link-frame 一起移动,而不是停留在它发布的位置,并且只在重新发布时移动到 base_link 的新位置- 按比例发布。

有谁知道如何解决这个问题?或者有人有其他方法 如何配置我的成本图,使其与 base_link-frame 一起移动但根据给定速率延迟?

更多信息,这里是我的 global_costmap_param.yaml:

global_costmap:
  global_frame: base_link
  map_topic: "prediction_occ_grid"
  update_frequency: 2.0
  publish_frequency: 2.0
  rolling_window: false
  always_send_full_costmap: true
  /move_base/global_costmap/static_layer/trinary_costmap: false
  height: 26.9
  width: 36.4
  mode: scale
  plugins: 
     - {name: static_layer, type: "costmap_2d::StaticLayer"}

我在 ROS Melodic 上使用了 costmap2d 库。

传统上,总是有一个全局代价地图:如果没有固定的世界坐标或静态地图参考作为全局原点,它就被称为map frame,原点被初始化为你的初始值位置。然后你使用 ekf 或类似的 (robot_localization/amcl) 来(自动)计算来自 base_link to map 的变换。然后你会有一个以 base_link 为中心的本地代价地图,它只翻译。

您可以只启动 costmap node 本身,它支持不同的发布和更新率。如果您正在使用它,这也应该由 move_base 处理,因为它 wraps/calls 成本图与您的 laserscan/pointcloud 输入。

只要你有来自 base_link to map 的转换,你就不需要将代价地图绑定到你的 base_link,因此它不会移动。

由于在我的情况下更新整个代价地图比只更新新发现的区域更好,我通过在我发布占用网格的相同代码(并且以相同的速率)中发布一个新帧来解决它。然后只需将全局成本图的框架设置为新发布的框架即可。看我的代码打击:

我发布占用网格的脚本:

...
#include <tf2_ros/static_transform_broadcaster.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_ros/buffer.h>
...
int main(int argc, char** argv) {
        ...
        double rate;    //Variable to store loop rate
        nh.getParam("/path_segmentation_client/looprate", rate);    //get loop-rate from parameters
        ros::Rate r(rate);    //apply loop-rate to ros
        ros::Publisher map_pub = nh.advertise<nav_msgs::OccupancyGrid>("prediction_occ_grid",1000);    //declare publisher for publishing occupancy grid

        static tf2_ros::StaticTransformBroadcaster brs;
        tf2_ros::Buffer tfBuffer;
        tf2_ros::TransformListener tfListener(tfBuffer);
        geometry_msgs::TransformStamped global_costmap_link;
        global_costmap_link.header.stamp = ros::Time::now();
        global_costmap_link.header.frame_id = "map";
        global_costmap_link.child_frame_id = "global_costmap_link";

        while(nh.ok()){
            client.waitForExistence();
            if (client.call(srv))
            {
                nav_msgs::OccupancyGrid occGrid = get_occupancy_grid(srv.response.topViewMsg);    //convert topView to Occupancy Grid
                map_pub.publish(occGrid);    // publish occupancyGrid 
                geometry_msgs::TransformStamped listenerStamped;
                try{
                    listenerStamped = tfBuffer.lookupTransform("map", "d435_link", ros::Time::now());
                }
                catch(tf2::TransformException &ex){
                    ROS_WARN("Error in path_segmentation: %s", ex.what());
                    continue;
                }
                global_costmap_link.transform.translation.x = listenerStamped.transform.translation.x; 
                global_costmap_link.transform.translation.y = listenerStamped.transform.translation.y;
                global_costmap_link.transform.translation.z = listenerStamped.transform.translation.z;
                global_costmap_link.transform.rotation.x = listenerStamped.transform.rotation.x;
                global_costmap_link.transform.rotation.y = listenerStamped.transform.rotation.y;
                global_costmap_link.transform.rotation.z = listenerStamped.transform.rotation.z;
                global_costmap_link.transform.rotation.w = listenerStamped.transform.rotation.w;
                brs.sendTransform(global_costmap_link); //update location of costmap_origin
                r.sleep();    //sleep to keep loop rate
            }
            else
            {
                ROS_ERROR("Failed to call service segmentation_to_occgrid.");
            }
        }

        return 0;
}

在global_costmap_params.yaml中:

global_frame: global_costmap_link