Ros

rose_slice之DataptrLayer

一、自主探索建图

自主探索建图顶层逻辑

  1. 找到个最佳导航点goal。认为导航到goal,能最好探索地图。
  2. move_base操作机器人导航到goal。
  3. 如果认为地图建得差不多了,结束建图,否则回到步骤1。

建图增加了两个问题。一是如何找到到最佳导航点goal,二是如何判断地图建得差不多了。针对第二个问题,往往混合在了第一个。因为找goal时,通常用迭代法,如果向迭代次数设置个阈值,超过阈值都没找到,那就认为已建出地图。

如何找到最佳导航点goal。这里说下ros官方提供的rrt_exploration功能包的两种思路。

 

1.1 rrt_detector

RRT:快速扩展随机树,Rapidly exploring Random Tree。

bool tnode_wrapper::exploration_detect_rrt(const costmap_2d::Costmap2D& costmap, const tpose2d& robot_pose, 
    geometry_msgs::Point& unk)
{
	......
	(init_map_x, init_map_y)是全局代价地图尺寸,是map坐标系。

	bool result = false;
	int rest_try = 100;
	while (rest_try >= 0) {
		x_rand.x = (rand() % (init_map_x - 2)) + 1;
		x_rand.y = (rand() % (init_map_y - 2)) + 1; 
		clip(x_rand, init_map_x, init_map_y);

		// Nearest
		x_nearest = Nearest(V, x_rand);
		clip(x_nearest, init_map_x, init_map_y);

		// Steer
		x_new = Steer(x_nearest, x_rand, eta);

		rest_try --;
		// ObstacleFree    1:free     -1:unkown (frontier region)      0:obstacle
		char  checking = ObstacleFree(costmap, x_nearest, x_new);

x_rand是在[1, 1, init_map_x - 2, init_map_y - 2]这个矩形内产生的随机栅格。严格的话,应该要确保它不在树(V)中。要注意ros在进行全局规划时,要求goal必须位在{0, 0, size_x, size_y}矩形。

Nearset是遍历树,找到树中一个离x_rand最近结点,记为x_nearest。

Steer是此次要扩展出结点。如果x_rand离Nearset不到eta,则结果就是x_rand。否则,结果是从x_nearset到x_rand直线中的一点,距离x_nearset点eta米的位置。x_new存储着结果。

ObstacleFree。走出一条从x_nearest到xnew的直线。有三种结果。

  1. ObstacleFree_result_other。过程中没到遇到未知栅格或障碍栅格。
  2. ObstacleFree_result_obs。过程中遇到障碍栅格。
  3. ObstacleFree_result_unk。过程中遇到未知栅格。此时将中止走路,参数xnew会被改成未知栅格坐标。
		if (checking == ObstacleFree_result_unk) {
			costmap.mapToWorld(x_new.x, x_new.y, world_x, world_y);
			unk.x = world_x;
			unk.y = world_y;
			unk.z = 0.0;
			result = true;
			break;

过程中遇到未知栅格,xnew是那个未知栅格坐标,认为xnew就是最佳goal。找到了个goal,结束此次查找。        

		} else if (checking == ObstacleFree_result_other) {
	 		V.push_back(x_new);

			costmap.mapToWorld(x_new.x, x_new.y, world_x, world_y);
			V_.push_back(SDL_FPoint{(float)world_x, (float)world_y});

过程中没到遇到未知栅格或障碍栅格,把x_new加入到树中。

		}

		// pub.publish(line);   
	}
	return result;
}

1.2 opencv_detector

这是将栅格地图进行一系列opencv操作,然后找到个最佳goal。

 

二、tright_exploration::updateMap逻辑

  • master->updateOrigin(new_origin_x, new_origin_y)。根据此次栅格图position,更新主代价地图的map坐标系原点在world坐示系下的坐标(origin_x_, origin_y_)。
  • dataptr_layer_->set_mat(info, data)。dataptr层会用此次栅格图更新私有代价地图。
  • layered_costmap_->updateMap(robot_x, robot_y, robot_yaw)。更新主代价地图。
  • footprint_pub_.publish(footprint)。发布此刻足迹topic。
  • publisher_->publishCostmap()。发布此刻代价地图topic。

 

一、DataptrLayer

1.1 set_dataptr

set_dataptr作用是扣出栅格图中的3mx3m区域,“复制”到Dataptr层的私有代价地图。“复制”加引号,因为不是原则复制,要用interpretValue转换下。

void DataptrLayer::set_dataptr(const nav_msgs::MapMetaData& info, const int8_t* data)
{
    resetMaps();

重置整张私有价地图,方法是所有栅格赋值NO_INFORMATION(0xff)。

    const Costmap2D* master = layered_costmap_->getCostmap();

    int start_x;
    int start_y;
    master->worldToMapNoBounds(info.origin.position.x, info.origin.position.y, start_x, start_y);
图1 set_dataptr

将world坐标系下坐标info.origin.position,转换到map坐标系下坐标(start_x, start_y),这两坐标位置关系参考图1。因为得到start_x、start_y极可能不在代价地图内,须调用忽略范围限制的worldToMapNoBounds。

    int end_size_x;
    int end_size_y;
    int costmap_start_x = 0;
    int costmap_start_y = 0;
    if (start_x <= 0) {
        start_x *= -1;
        end_size_x = size_x_;

        if (start_x + end_size_x > (int)info.width) {
            // overrun border: east
            end_size_x = info.width - start_x;
        }
    } else {
        // overrun border: west
        costmap_start_x = start_x;
        end_size_x = size_x_ - costmap_start_x;
        start_x = 0;
    }

在x方向,计算后面for循环要用的start_x、end_size_x、costmap_start_x。参考图1所示,计算时要考虑三处情况。

    if (start_y <= 0) {
        start_y *= -1;
        end_size_y = size_y_;
        if (start_y + end_size_y > (int)info.height) {
            // overrun border: north
            end_size_y = info.height - start_y;
        }
    } else {
        // overrun border: south
        costmap_start_y = start_y;
        end_size_y = size_y_ - costmap_start_y;
        start_y = 0;
    }

类似x,在y方向,计算后面for循环要用的start_y、end_size_y、costmap_start_y。

    int src_index = 0;
    int dst_index = 0;
    // initialize the costmap with static data
    for (int y = 0; y < end_size_y; ++ y) {
        const int src_y_line = (y + start_y) * info.width;
        const int dst_y_line = (y + costmap_start_y) * size_x_;
        for (int x = 0; x < end_size_x; ++ x) {
            src_index = (x + start_x) + src_y_line;
            dst_index = (x + costmap_start_x) + dst_y_line;

            unsigned char value = data[src_index];
            costmap_[dst_index] = interpretValue(value);
        }
    }
}

set_dataptr要调用master->worldToMapNoBounds(...)得到start_x、start_y,worldToMapNoBounds要使用主代价地图的(orign_x_, origin_y_),所以在set_dataptr前须调用master->updateOrigin(...),先得到正确的(orign_x_, origin_y_)。

在整个过程中,私有copstmap尺寸总是1.5mx1.5m,它的origin_x_、origin_y_没有作用,保诗着初始值(0, 0)。

全部评论: 0

    写评论: