【自主探索】rrt_exploration 源码解析

各文件运行顺序:

  1. simple.launch
  2. global_rrt_detector.cpp
  3. local_rrt_detector.cpp
  4. filter.py
  5. assigner.py

文章目录

    • 一、simple.launch
    • 二、global_rrt_detector.cpp
    • 三、local_rrt_detector.cpp
    • 四、filter.py
    • 五、assigner.py

一、simple.launch

<!-- Launch file for the rrt-detector and the assigner --><launch><arg name="eta" value="1.0"/><arg name="Geta" value="15.0"/><param name="namespace_init_count" value="1"/><node pkg="rrt_exploration" type="global_rrt_detector" name="global_detector" output="screen"><param name="eta" value="$(arg Geta)"/><param name="map_topic" value="/map"/><!-- <param name="map_topic" value="/move_base/global_costmap/costmap"/> --></node><node pkg="rrt_exploration" type="local_rrt_detector" name="local_detector" output="screen"><param name="eta" value="$(arg eta)"/><param name="map_topic" value="/map"/><!-- <param name="map_topic" value="/move_base/global_costmap/costmap"/> --><!-- <param name="robot_frame" value="/base_link"/> --><param name="robot_frame" value="base_link"/></node><node pkg="rrt_exploration" type="filter.py" name="filter" output="screen"><param name="map_topic" value="/map"/><!-- <param name="map_topic" value="/move_base/global_costmap/costmap"/> --><param name="info_radius" value="1"/> <param name="costmap_clearing_threshold" value="70"/>   <!-- 清除阈值 --><param name="goals_topic" value="/detected_points"/><param name="namespace" value=""/> <param name="n_robots" value="1"/><param name="rate" value="100"/><param name="global_costmap_topic" value="/move_base/global_costmap/costmap"/><param name="robot_frame" value="base_link"/></node><node pkg="rrt_exploration" type="assigner.py" name="assigner" output="screen"><param name="map_topic" value="/map"/><!-- <param name="map_topic" value="/move_base/global_costmap/costmap"/> --><!-- <param name="global_frame" value="/map"/> --><param name="global_frame" value="map"/><param name="info_radius" value="1"/> <param name="info_multiplier" value="3.0"/>   <!-- 信息乘数 --><param name="hysteresis_radius" value="3.0"/>   <!-- 惯性半径 --><param name="hysteresis_gain" value="2.0"/>   <!-- 惯性增益 --><param name="frontiers_topic" value="/filtered_points"/> <param name="n_robots" value="1"/><param name="namespace" value=""/><param name="delay_after_assignement" value="0.5"/> <!-- 分配后的延迟 --><param name="rate" value="100"/></node></launch>

二、global_rrt_detector.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include "stdint.h"
#include "functions.h"
#include "mtrand.h"#include "nav_msgs/OccupancyGrid.h"
#include "geometry_msgs/PointStamped.h"
#include "std_msgs/Header.h"
#include "nav_msgs/MapMetaData.h"
#include "geometry_msgs/Point.h"
#include "visualization_msgs/Marker.h"
#include <tf/transform_listener.h>// global variables
nav_msgs::OccupancyGrid mapData;
geometry_msgs::PointStamped clickedpoint;
geometry_msgs::PointStamped exploration_goal;
visualization_msgs::Marker points,line;
float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y;rdm r; // for genrating random numbers
// 1.
int main(int argc, char **argv)
{unsigned long init[4] = {0x123, 0x234, 0x345, 0x456}, length = 7;	// 初始化随机数生成器MTRand_int32 irand(init, length); // 32位整数生成器// this is an example of initializing by an array// you may use MTRand(seed) with any 32bit integer// as a seed for a simpler initializationMTRand drand; // double in [0, 1) generator, already init	[0, 1)的双精度浮点数生成器,默认已初始化// generate the same numbers as in the original C test program	ros::init(argc, argv, "global_rrt_frontier_detector");	// ROS节点初始化ros::NodeHandle nh;	// 创建ROS节点句柄// fetching all parameters	获取所有参数float eta,init_map_x,init_map_y,range;std::string map_topic,base_frame_topic;std::string ns;ns=ros::this_node::getName();ros::param::param<float>(ns+"/eta", eta, 0.5);ros::param::param<std::string>(ns+"/map_topic", map_topic, "/robot_1/map"); // 创建ROS订阅器和发布器ros::Subscriber sub= nh.subscribe(map_topic, 100 ,mapCallBack);		// 订阅地图信息ros::Subscriber rviz_sub= nh.subscribe("/clicked_point", 100 ,rvizCallBack);	// 订阅RViz中点击的点ros::Publisher targetspub = nh.advertise<geometry_msgs::PointStamped>("/detected_points", 10);	// 发布探测到的点ros::Publisher pub = nh.advertise<visualization_msgs::Marker>(ns+"_shapes", 10);	// 发布形状标记ros::Rate rate(100); 	// 设置ROS节点的循环频率// wait until map is received, when a map is received, mapData.header.seq will not be < 1  // 等待地图接收,当接收到地图时,mapData.header.seq不会小于1while (mapData.header.seq<1 or mapData.data.size()<1)  {  ros::spinOnce();  ros::Duration(0.1).sleep();}//visualizations  points and lines..	设置可视化标记(visualization markers)的属性,包括点和线的样式、颜色、大小等points.header.frame_id=mapData.header.frame_id;	//将points标记的坐标系设置为地图数据的坐标系。line.header.frame_id=mapData.header.frame_id;	//将line标记的坐标系设置为地图数据的坐标系points.header.stamp=ros::Time(0);	//将points标记的时间戳设置为0,表示最新的时间line.header.stamp=ros::Time(0);		points.ns=line.ns = "markers";	//设置points和line标记的命名空间为"markers"points.id = 0;	//设置points标记的ID为0line.id =1;points.type = points.POINTS;	//将points标记的类型设置为POINTS,表示它是由一系列点组成的标记line.type=line.LINE_LIST;	//将line标记的类型设置为LINE_LIST,表示它是由一系列线段组成的标记。//Set the marker action.  Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL)points.action =points.ADD;	//设置points标记的动作为ADD,表示将添加新标记line.action = line.ADD;	//设置line标记的动作为ADD,表示将添加新标记points.pose.orientation.w =1.0;	//设置points标记的姿态的四元数的w分量为1.0line.pose.orientation.w = 1.0;	//设置line标记的姿态的四元数的w分量为1.0line.scale.x =  0.03;	//设置line标记的线段宽度为0.03line.scale.y= 0.03;points.scale.x=0.3; points.scale.y=0.3; line.color.r =9.0/255.0;	//设置line标记的颜色为红色(R=9)line.color.g= 91.0/255.0;	//绿色(G=91)line.color.b =236.0/255.0;	//蓝色(B=236)points.color.r = 255.0/255.0;points.color.g = 0.0/255.0;points.color.b = 0.0/255.0;points.color.a=1.0;	//设置points标记的颜色的透明度为1.0line.color.a = 1.0;points.lifetime = ros::Duration();	//设置points标记的生命周期为默认值,表示它一直保持可见。line.lifetime = ros::Duration();geometry_msgs::Point p;  //创建一个geometry_msgs::Point类型的变量p,用于表示3D点的坐标。// 等待至少有5个点的可视化while(points.points.size()<5){ros::spinOnce();pub.publish(points) ;}// 对前两个点进行一些计算std::vector<float> temp1;	//创建一个名为temp1的浮点数向量,用于存储points中的第一个点的x和y坐标temp1.push_back(points.points[0].x);	//将points中的第一个点的x坐标加入到temp1中。temp1.push_back(points.points[0].y);	//将points中的第一个点的y坐标加入到temp1中。std::vector<float> temp2; temp2.push_back(points.points[2].x);temp2.push_back(points.points[0].y);init_map_x=Norm(temp1,temp2);	//调用Norm函数计算temp1和temp2表示的两个点之间的距离,并将结果存储在init_map_x中temp1.clear();	//清空temp1和temp2向量,以便重新使用temp2.clear();temp1.push_back(points.points[0].x);temp1.push_back(points.points[0].y);temp2.push_back(points.points[0].x);temp2.push_back(points.points[2].y);init_map_y=Norm(temp1,temp2);	//调用Norm函数计算temp1和temp2表示的两个点之间的距离,并将结果存储在init_map_y中。temp1.clear();		temp2.clear();// 计算起始位置的中点Xstartx=(points.points[0].x+points.points[2].x)*.5;	//计算起始位置的x中点,即前两个点的x坐标之和的一半Xstarty=(points.points[0].y+points.points[2].y)*.5;	//计算起始位置的y中点,即前两个点的y坐标之和的一半geometry_msgs::Point trans;	//创建一个名为 trans 的 geometry_msgs::Point 类型的变量trans=points.points[4];	//将 points 中的第五个点的坐标赋值给 transstd::vector< std::vector<float>  > V; 	//创建一个名为 V 的二维浮点数向量,用于存储探索算法中的一些坐标std::vector<float> xnew;	//创建一个名为 xnew 的一维浮点数向量xnew.push_back(trans.x);  //将 trans 中的 x 和 y 坐标加入到 xnew 中xnew.push_back(trans.y);V.push_back(xnew);	//将 xnew 添加到 V 中,形成一个新的探索坐标points.points.clear();	//清空 points 中的点,准备用于新的可视化pub.publish(points) ;	//发布清空后的 points,以清空可视化效果std::vector<float> frontiers;	//创建一个名为 frontiers 的一维浮点数向量int i=0;float xr,yr;std::vector<float> x_rand,x_nearest,x_new;	//创建三个一维浮点数向量 x_rand、x_nearest 和 x_new。// Main loop 主循环,在 ROS 节点保持运行状态时不断执行while (ros::ok()){// Sample free	采样自由点x_rand.clear();	//清空 x_rand 向量,用于存储采样得到的随机自由点xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx;	//通过随机采样生成 x 轴坐标yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty;x_rand.push_back( xr ); 	//将生成的 x 和 y 坐标加入到 x_rand 向量中,形成一个随机采样的自由点x_rand.push_back( yr );// Nearest	最近点x_nearest=Nearest(V,x_rand);	//找到 V 中离 x_rand 最近的点,将其存储在 x_nearest 中。// Steer 根据最近点 x_nearest 和随机点 x_rand 以及给定的步长 eta,通过 Steer 函数生成新的点,存储在 x_new 中x_new=Steer(x_nearest,x_rand,eta);// 检查从最近点 x_nearest 到新点 x_new 的路径是否在地图中是自由空间。// ObstacleFree    1:free     -1:unkown (frontier region)      0:obstacle	检查是否为自由空间char checking=ObstacleFree(x_nearest,x_new,mapData);if (checking==-1){	//如果路径为未知区域(前沿区域)exploration_goal.header.stamp=ros::Time(0);	//将 exploration_goal 的时间戳设置为当前时间exploration_goal.header.frame_id=mapData.header.frame_id;	//将 exploration_goal 的坐标系设置为地图的坐标系exploration_goal.point.x=x_new[0];	//将 exploration_goal 的 x 坐标设置为新生成点 x_new 的 x 坐标exploration_goal.point.y=x_new[1];	//将 exploration_goal 的 y 坐标设置为新生成点 x_new 的 y 坐标exploration_goal.point.z=0.0;	//将 exploration_goal 的 z 坐标设置为 0p.x=x_new[0]; 	//创建一个用于可视化的点 p,其坐标与 exploration_goal 相同p.y=x_new[1]; p.z=0.0;points.points.push_back(p);	//将点 p 添加到 points 中,用于可视化pub.publish(points) ;	//发布包含新点的可视化信息targetspub.publish(exploration_goal);	//发布包含新点的探索目标信息points.points.clear();	//清空用于可视化的点,以便下一次循环时重新填充}	  		  else if (checking==1){	//如果路径为自由空间V.push_back(x_new);	//将新生成的点 x_new 添加到 V 向量中,以便记录已经访问过的点p.x=x_new[0]; 	//创建一个用于可视化的点 p,其坐标与 x_new 相同p.y=x_new[1]; p.z=0.0;line.points.push_back(p);	//将点 p 添加到 line 中,以便可视化连接到最近点的线p.x=x_nearest[0]; 	//创建另一个用于可视化的点 p,其坐标与最近点 x_nearest 相同p.y=x_nearest[1]; p.z=0.0;line.points.push_back(p);	//将点 p 添加到 line 中,以便可视化连接到最近点的线}pub.publish(line);  //发布路径可视化信息ros::spinOnce();	//处理回调函数rate.sleep();}return 0;
}

Subscribers callback functions---------------------------------------
这两个回调函数是通过 ros::Subscriber 订阅相应的消息主题,当有新消息到达时,系统将自动调用这两个回调函数

// 2.mapCallBack 处理地图消息
void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg)
{mapData=*msg;	//当接收到 nav_msgs::OccupancyGrid 类型的消息时,将其存储在全局变量 mapData 中
}
// 3.rvizCallBack 处理来自RViz工具的点选消息
void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg)
{ //当接收到 geometry_msgs::PointStamped 类型的消息时,将其中的点坐标提取出来,并将点添加到全局的 points 中,用于后续的处理和可视化geometry_msgs::Point p;  p.x=msg->point.x;p.y=msg->point.y;p.z=msg->point.z;points.points.push_back(p);}

三、local_rrt_detector.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <iostream>
#include <string>
#include <vector>
#include "stdint.h"
#include "functions.h"
#include "mtrand.h"#include "nav_msgs/OccupancyGrid.h"
#include "geometry_msgs/PointStamped.h"
#include "std_msgs/Header.h"
#include "nav_msgs/MapMetaData.h"
#include "geometry_msgs/Point.h"
#include "visualization_msgs/Marker.h"
#include <tf/transform_listener.h>// global variables
nav_msgs::OccupancyGrid mapData;
geometry_msgs::PointStamped clickedpoint;
geometry_msgs::PointStamped exploration_goal;
visualization_msgs::Marker points,line;
float xdim,ydim,resolution,Xstartx,Xstarty,init_map_x,init_map_y;rdm r; // for genrating random numbers
// 1.
int main(int argc, char **argv)
{// ...... 和 global_rrt_detector.cpp 一样std::vector<float> frontiers;int i=0;float xr,yr;std::vector<float> x_rand,x_nearest,x_new;tf::TransformListener listener;	//用于监听并获取Transforms(坐标变换)的信息// Main loopwhile (ros::ok()){// Sample freex_rand.clear();xr=(drand()*init_map_x)-(init_map_x*0.5)+Xstartx;yr=(drand()*init_map_y)-(init_map_y*0.5)+Xstarty;x_rand.push_back( xr ); x_rand.push_back( yr );// Nearestx_nearest=Nearest(V,x_rand);// Steerx_new=Steer(x_nearest,x_rand,eta);// ObstacleFree    1:free     -1:unkown (frontier region)      0:obstaclechar   checking=ObstacleFree(x_nearest,x_new,mapData);if (checking==-1){exploration_goal.header.stamp=ros::Time(0);exploration_goal.header.frame_id=mapData.header.frame_id;exploration_goal.point.x=x_new[0];exploration_goal.point.y=x_new[1];exploration_goal.point.z=0.0;p.x=x_new[0]; p.y=x_new[1]; p.z=0.0;points.points.push_back(p);pub.publish(points) ;targetspub.publish(exploration_goal);points.points.clear();V.clear();		//清空存储在 V 中的坐标,清空 V 可以在每次探索开始时重置存储的信息,以便重新记录新的路径或位置tf::StampedTransform transform;	//声明一个 tf::StampedTransform 类型的对象 transform,用于存储变换信息,包括平移和旋转int  temp=0;while (temp==0){	//使用一个循环来尝试获取变换信息,直到成功为止try{temp=1;//使用 TransformListener 的 lookupTransform 方法获取从 base_frame_topic到 map_topic 的变换,并将结果存储在 transform 中listener.lookupTransform(map_topic, base_frame_topic , ros::Time(0), transform);}catch (tf::TransformException ex){temp=0;ros::Duration(0.1).sleep();}}x_new[0]=transform.getOrigin().x();	//从变换中获取机器人的 x 和 y 坐标,并将它们存储在 x_new 中x_new[1]=transform.getOrigin().y();V.push_back(x_new);	//将 x_new 添加到 V 中,可能是用于记录机器人的位置line.points.clear();	//清空 line.points,可能是为了清除之前绘制的路径线,以便重新绘制新的路径}else if (checking==1){V.push_back(x_new);p.x=x_new[0]; p.y=x_new[1]; p.z=0.0;line.points.push_back(p);p.x=x_nearest[0]; p.y=x_nearest[1]; p.z=0.0;line.points.push_back(p);}pub.publish(line);  ros::spinOnce();rate.sleep();}return 0;
}

Subscribers callback functions---------------------------------------

// 2.mapCallBack 处理地图消息
void mapCallBack(const nav_msgs::OccupancyGrid::ConstPtr& msg)
{mapData=*msg;
}
// 3.rvizCallBack 处理来自RViz工具的点选消息
void rvizCallBack(const geometry_msgs::PointStamped::ConstPtr& msg)
{ geometry_msgs::Point p;  p.x=msg->point.x;p.y=msg->point.y;p.z=msg->point.z;points.points.push_back(p);}

四、filter.py

# --------Include modules---------------
from copy import copy
import rospy
from visualization_msgs.msg import Marker
from geometry_msgs.msg import Point
from nav_msgs.msg import OccupancyGrid
from geometry_msgs.msg import PointStamped
import tf
from numpy import array, vstack, delete
from functions import gridValue, informationGain
from sklearn.cluster import MeanShift
from rrt_exploration.msg import PointArray# Subscribers' callbacks------------------------------
mapData = OccupancyGrid()
frontiers = []
globalmaps = []
if __name__ == '__main__':try:node()except rospy.ROSInterruptException:pass
# 1.
def node():# 声明了一些全局变量。这些变量在整个脚本中都可以使用global frontiers, mapData, global1, global2, global3, globalmaps, litraIndx, n_robots, namespace_init_count# 初始化ROS节点。这里的节点名为 'filter',并设置 anonymous 参数为 False,表示节点名不允许自动附加一个唯一标识符。rospy.init_node('filter', anonymous=False)# fetching all parametersmap_topic = rospy.get_param('~map_topic', '/map')threshold = rospy.get_param('~costmap_clearing_threshold', 70)  # 地图中用于清除障碍物的阈值# this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurateinfo_radius = rospy.get_param('~info_radius', 1.0)goals_topic = rospy.get_param('~goals_topic', '/detected_points')n_robots = rospy.get_param('~n_robots', 1)namespace = rospy.get_param('~namespace', '')namespace_init_count = rospy.get_param('namespace_init_count', 1)rateHz = rospy.get_param('~rate', 100)# global_costmap_topic = rospy.get_param(#     '~global_costmap_topic', '/move_base_node/global_costmap/costmap')global_costmap_topic = rospy.get_param('~global_costmap_topic', '/move_base/global_costmap/costmap')robot_frame = rospy.get_param('~robot_frame', 'base_link')litraIndx = len(namespace)rate = rospy.Rate(rateHz)# 创建了一个ROS Subscriber(订阅者),用于接收 map_topic 所指定的地图信息rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack)# 这部分代码主要涉及全局地图数据的初始化和订阅# 这段代码的目的是初始化全局地图数据结构并订阅全局地图话题,以便在接收到全局地图消息时执行相应的处理for i in range(0, n_robots):    # 为每个机器人创建一个 OccupancyGrid 类型的对象globalmaps.append(OccupancyGrid())  # 创建一个空的 OccupancyGrid 对象,并将其添加到 globalmaps 列表中if len(namespace) > 0:for i in range(0, n_robots):    # 为每个机器人创建一个 ROS Subscriber# 构建了每个机器人的特定命名空间,然后将其与全局地图话题 global_costmap_topic 进行组合。# 每个机器人的全局地图消息都将传递给 globalMap 函数进行处理rospy.Subscriber(namespace+str(i+namespace_init_count) +    global_costmap_topic, OccupancyGrid, globalMap)elif len(namespace) == 0:# 直接为全局地图话题创建一个 ROS Subscriber,并将消息传递给 globalMap 函数进行处理rospy.Subscriber(global_costmap_topic, OccupancyGrid, globalMap)# wait if map is not received yetwhile (len(mapData.data) < 1):  # mapData.data 存储地图的数据。条件检查确保在接收到有效的地图数据之前一直等待rospy.loginfo('Waiting for the map')rospy.sleep(0.1)pass# wait if any of robots' global costmap map is not received yet# 等待所有机器人的全局代价地图(globalmaps)准备好for i in range(0, n_robots):while (len(globalmaps[i].data) < 1):rospy.loginfo('Waiting for the global costmap')rospy.sleep(0.1)passglobal_frame = "/"+mapData.header.frame_idtfLisn = tf.TransformListener()if len(namespace) > 0:for i in range(0, n_robots):    # 通过waitForTransform等待机器人坐标系和全局坐标系之间的转换关系tfLisn.waitForTransform(global_frame[1:], namespace+str(i+namespace_init_count)+'/'+robot_frame, rospy.Time(0), rospy.Duration(10.0))elif len(namespace) == 0:tfLisn.waitForTransform(global_frame[1:], '/'+robot_frame, rospy.Time(0), rospy.Duration(10.0))rospy.Subscriber(goals_topic, PointStamped, callback=callBack,callback_args=[tfLisn, global_frame[1:]])pub = rospy.Publisher('frontiers', Marker, queue_size=10)   # 发布前沿点('frontiers')pub2 = rospy.Publisher('centroids', Marker, queue_size=10)  # 质心('centroids')filterpub = rospy.Publisher('filtered_points', PointArray, queue_size=10)   # 过滤后的点('filtered_points')rospy.loginfo("the map and global costmaps are received")# wait if no frontier is received yet# 检查frontiers列表的长度是否小于1,即是否有前沿点数据接收到。直到有前沿点数据接收到,才会跳出循环while len(frontiers) < 1:passpoints = Marker()   # 创建了两个Marker对象points_clust = Marker()# Set the frame ID and timestamp.  See the TF tutorials for information on these.points.header.frame_id = mapData.header.frame_idpoints.header.stamp = rospy.Time.now()points.ns = "markers2"points.id = 0points.type = Marker.POINTS# Set the marker action for latched frontiers.  Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL)points.action = Marker.ADDpoints.pose.orientation.w = 1.0points.scale.x = 0.2points.scale.y = 0.2points.color.r = 255.0/255.0points.color.g = 255.0/255.0points.color.b = 0.0/255.0points.color.a = 1points.lifetime = rospy.Duration()p = Point()p.z = 0pp = []pl = []points_clust.header.frame_id = mapData.header.frame_idpoints_clust.header.stamp = rospy.Time.now()points_clust.ns = "markers3"points_clust.id = 4points_clust.type = Marker.POINTS# Set the marker action for centroids.  Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL)points_clust.action = Marker.ADDpoints_clust.pose.orientation.w = 1.0points_clust.scale.x = 0.2points_clust.scale.y = 0.2points_clust.color.r = 0.0/255.0points_clust.color.g = 255.0/255.0points_clust.color.b = 0.0/255.0points_clust.color.a = 1points_clust.lifetime = rospy.Duration()temppoint = PointStamped()  # 创建了一个PointStamped消息实例,用于发布单个前沿点的信息temppoint.header.frame_id = mapData.header.frame_idtemppoint.header.stamp = rospy.Time(0)temppoint.point.z = 0.0 # 设置为0.0表示平面上的点arraypoints = PointArray()  # 创建了一个PointArray消息实例,用于发布一组前沿点的信息tempPoint = Point() # 创建了一个Point消息实例,用于存储前沿点的坐标信息tempPoint.z = 0.0# -------------------------------------------------------------------------# ---------------------     Main   Loop     -------------------------------# 这部分代码进行前沿点的聚类while not rospy.is_shutdown():# -------------------------------------------------------------------------# Clustering frontier pointscentroids = []front = copy(frontiers) # 复制前沿点的列表if len(front) > 1:  # 如果前沿点的数量大于1,执行以下聚类步骤ms = MeanShift(bandwidth=0.3)   # 创建MeanShift聚类器,指定带宽为0.3ms.fit(front)   # 使用ms.fit(front)对前沿点进行聚类centroids = ms.cluster_centers_  # 将聚类后的中心点存储在centroids中# # 如果只有一个前沿点,将centroids设置为等于frontif len(front) == 1:centroids = frontfrontiers = copy(centroids) # 将frontiers更新为centroids,以便后续的处理使用聚类后的前沿点# 这部分代码负责清理旧的前沿点# clearing old frontiersz = 0while z < len(centroids):cond = Falsetemppoint.point.x = centroids[z][0] # 将当前聚类中心的坐标设置到temppointtemppoint.point.y = centroids[z][1]for i in range(0, n_robots):    # 对于每个机器人的全局地图,检查聚类中心附近是否存在障碍物(通过cond变量判断)transformedPoint = tfLisn.transformPoint(   # 使用tfLisn.transformPoint将聚类中心的坐标从地图坐标系转换到机器人的全局地图坐标系globalmaps[i].header.frame_id, temppoint)x = array([transformedPoint.point.x, transformedPoint.point.y])cond = (gridValue(globalmaps[i], x) > threshold) or cond# 如果聚类中心附近存在障碍物(cond为True)或者聚类中心的信息增益低于阈值,删除该聚类中心if (cond or (informationGain(mapData, [centroids[z][0], centroids[z][1]], info_radius*0.5)) < 0.2):centroids = delete(centroids, (z), axis=0)z = z-1z += 1# 这段代码负责发布前沿点、聚类中心和过滤后的前沿点# publishingarraypoints.points = []for i in centroids:tempPoint.x = i[0]tempPoint.y = i[1]arraypoints.points.append(copy(tempPoint))  # 将清理后的前沿点 centroids 转换为 arraypoints 消息,用于发布filterpub.publish(arraypoints)pp = []for q in range(0, len(frontiers)):p.x = frontiers[q][0]p.y = frontiers[q][1]pp.append(copy(p))  # 将原始的前沿点 frontiers 转换为 points 消息,用于发布points.points = pppp = []for q in range(0, len(centroids)):p.x = centroids[q][0]p.y = centroids[q][1]pp.append(copy(p))  # 将聚类中心 centroids 转换为 points_clust 消息,用于发布points_clust.points = pppub.publish(points)pub2.publish(points_clust)rate.sleep()# -------------------------------------------------------------------------
# 2.用作 ROS Subscriber 接收到消息时的回调函数
def mapCallBack(data):global mapData  # 声明了一个全局变量 mapData,用于存储地图数据mapData = data  # 将接收到的地图消息赋值给全局变量 mapData,以便其他部分的程序可以使用这个地图数据
# 3.用于更新全局地图数据的回调函数,主要作用是将接收到的全局地图数据存储在全局变量 globalmaps 中,以供后续使用
def globalMap(data):global global1, globalmaps, litraIndx, namespace_init_count, n_robotsglobal1 = dataif n_robots > 1:    # # 如果有多个机器人,从连接头部分析机器人索引indx = int(data._connection_header['topic'][litraIndx])-namespace_init_countelif n_robots == 1: # 如果只有一个机器人,索引为0indx = 0globalmaps[indx] = data # 将接收到的全局地图数据存储在全局变量中的相应位置
# 4.用于处理前沿点数据的回调函数,主要作用是将接收到的前沿点数据进行坐标变换,然后存储在全局变量 frontiers 中
def callBack(data, args):global frontiers, min_distancetransformedPoint = args[0].transformPoint(args[1], data)    # 从参数中提取tf.TransformListener和目标坐标系x = [array([transformedPoint.point.x, transformedPoint.point.y])]   # 将变换后的点坐标存储在数组中if len(frontiers) > 0:  # 将坐标数组存储在全局变量frontiers中frontiers = vstack((frontiers, x))else:frontiers = x

五、assigner.py

#--------Include modules---------------
from copy import copy
import rospy
from visualization_msgs.msg import Marker
from geometry_msgs.msg import Point
from nav_msgs.msg import OccupancyGrid
import tf
from rrt_exploration.msg import PointArray
from time import time
from numpy import array
from numpy import linalg as LA
from numpy import all as All
from numpy import inf
from functions import robot,informationGain,discount
from numpy.linalg import norm# Subscribers' callbacks------------------------------
mapData=OccupancyGrid()
frontiers=[]
global1=OccupancyGrid()
global2=OccupancyGrid()
global3=OccupancyGrid()
globalmaps=[]
if __name__ == '__main__':try:node()except rospy.ROSInterruptException:pass
# Node----------------------------------------------
# 1.
def node():global frontiers,mapData,global1,global2,global3,globalmapsrospy.init_node('assigner', anonymous=False)# fetching all parametersmap_topic= rospy.get_param('~map_topic','/map')info_radius= rospy.get_param('~info_radius',1.0)					#this can be smaller than the laser scanner range, >> smaller >>less computation time>> too small is not good, info gain won't be accurateinfo_multiplier=rospy.get_param('~info_multiplier',3.0)		hysteresis_radius=rospy.get_param('~hysteresis_radius',3.0)			#at least as much as the laser scanner rangehysteresis_gain=rospy.get_param('~hysteresis_gain',2.0)				#bigger than 1 (biase robot to continue exploring current regionfrontiers_topic= rospy.get_param('~frontiers_topic','/filtered_points')	n_robots = rospy.get_param('~n_robots',1)namespace = rospy.get_param('~namespace','')namespace_init_count = rospy.get_param('namespace_init_count',1)delay_after_assignement=rospy.get_param('~delay_after_assignement',0.5)rateHz = rospy.get_param('~rate',100)rate = rospy.Rate(rateHz)#-------------------------------------------rospy.Subscriber(map_topic, OccupancyGrid, mapCallBack)rospy.Subscriber(frontiers_topic, PointArray, callBack)#---------------------------------------------------------------------------------------------------------------# wait if no frontier is received yet while len(frontiers)<1:passcentroids=copy(frontiers)	#wait if map is not received yetwhile (len(mapData.data)<1):passrobots=[]if len(namespace)>0:for i in range(0,n_robots):robots.append(robot(namespace+str(i+namespace_init_count)))elif len(namespace)==0:robots.append(robot(namespace))for i in range(0,n_robots):robots[i].sendGoal(robots[i].getPosition())#-------------------------------------------------------------------------#---------------------     Main   Loop     -------------------------------#-------------------------------------------------------------------------while not rospy.is_shutdown():centroids=copy(frontiers)	# 将全局变量 frontiers 复制给局部变量 centroids,以确保在循环执行期间 frontiers 不被修改#Get information gain for each frontier pointinfoGain=[]	# 创建一个空列表,用于存储每个前沿点的信息增益for ip in range(0,len(centroids)):	# 遍历 centroids 中的前沿点,计算每个点的信息增益,并将结果添加到 infoGain 列表中infoGain.append(informationGain(mapData,[centroids[ip][0],centroids[ip][1]],info_radius))#-------------------------------------------------------------------------			#get number of available/busy robots	分别创建两个空列表,用于存储可用和繁忙的机器人的索引na=[] #available robotsnb=[] #busy robotsfor i in range(0,n_robots):	# 遍历所有机器人,将其状态为 1 的索引添加到 nb 中,其他情况添加到 na 中。if (robots[i].getState()==1):nb.append(i)else:na.append(i)	rospy.loginfo("available robots: "+str(na))		# 记录可用机器人的信息#get dicount and update informationGain	遍历所有机器人,计算折扣并更新信息增益for i in nb+na:	# 这个循环遍历繁忙(nb)和可用(na)的机器人,并通过 discount 函数计算折扣,然后更新信息增益infoGain=discount(mapData,robots[i].assigned_point,centroids,infoGain,info_radius)# 创建三个空列表          revenue_record=[]centroid_record=[]id_record=[]# 在嵌套循环中,对于每个可用机器人和每个前沿点,计算收入、保存前沿点和机器人的信息for ir in na:for ip in range(0,len(centroids)):cost=norm(robots[ir].getPosition()-centroids[ip])	# 计算机器人与前沿点之间的距离	threshold=1	# 设置阈值(此处为1,但未在后续代码中使用)information_gain=infoGain[ip]	# 获取前沿点的信息增益if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius):information_gain*=hysteresis_gain	# 如果机器人与前沿点之间的距离小于等于 hysteresis_radius,则应用滞后增益revenue=information_gain*info_multiplier-cost	revenue_record.append(revenue)	# 计算收入,将其添加到 revenue_recordcentroid_record.append(centroids[ip])	# 前沿点添加到 centroid_recordid_record.append(ir)	# 机器人 ID 添加到 id_recordif len(na)<1:	# 在没有可用机器人的情况下revenue_record=[]centroid_record=[]id_record=[]for ir in nb:for ip in range(0,len(centroids)):cost=norm(robots[ir].getPosition()-centroids[ip])		threshold=1information_gain=infoGain[ip]if (norm(robots[ir].getPosition()-centroids[ip])<=hysteresis_radius):information_gain*=hysteresis_gain# 在计算收入之前,检查前沿点是否在当前机器人的分配点附近(在 hysteresis_radius 内),如果是,则应用 hysteresis_gainif ((norm(centroids[ip]-robots[ir].assigned_point))<hysteresis_radius):information_gain=informationGain(mapData,[centroids[ip][0],centroids[ip][1]],info_radius)*hysteresis_gainrevenue=information_gain*info_multiplier-costrevenue_record.append(revenue)centroid_record.append(centroids[ip])id_record.append(ir)rospy.loginfo("revenue record: "+str(revenue_record))	rospy.loginfo("centroid record: "+str(centroid_record))	rospy.loginfo("robot IDs record: "+str(id_record))	# 检查是否存在收入记录,并在有记录时选择收入最大的机器人	if (len(id_record)>0):	# 如果 id_record 列表的长度大于零,表示有机器人被分配了任务winner_id=revenue_record.index(max(revenue_record))	# 找到 revenue_record 中最大值对应的索引,这个索引对应于赢家机器人的索引robots[id_record[winner_id]].sendGoal(centroid_record[winner_id])	# 通过 id_record 找到赢家机器人的 ID,并使用该 ID 发送目标位置# 记录分配的信息到 ROS 日志rospy.loginfo(namespace+str(namespace_init_count+id_record[winner_id])+"  assigned to  "+str(centroid_record[winner_id]))	rospy.sleep(delay_after_assignement)rate.sleep()
# 2.处理接收到的地图数据 OccupancyGrid 数据
# 将其赋值给全局变量 mapData。在ROS中,OccupancyGrid 通常表示地图的占用情况,包含有关环境中障碍物的信息
def mapCallBack(data):global mapDatamapData=data
# 3.处理接收到的前沿点 PointArray 数据
# 将其中的每个点的 x 和 y 坐标提取出来,组成一个数组,并将这些数组存储在 frontiers 全局变量中
def callBack(data):global frontiersfrontiers=[]for point in data.points:frontiers.append(array([point.x,point.y]))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何排查线上问题的?

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、预警层面 1.1 做好监控告警 1.2 定位报警层面 二、近期版本 2.1 判断最近有没有发版本 2.2 回归最近的版本 三、日志告警…

用队列实现栈,力扣

题目地址&#xff1a; 225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 难度&#xff1a;简单 今天刷用队列实现栈&#xff0c;大家有兴趣可以点上看看题目要求&#xff0c;试着做一下。 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 方法…

MySQL8改库,表,字段编码及排序规则

修改数据库编码格式语句 SELECT CONCAT(ALTER DATABASE ,SCHEMA_NAME, CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;) as 修改数据库编码格式语句 FROM information_schema.SCHEMATA WHERE 11 AND SCHEMA_NAME 数据库名称 #要修改的数据库名称 -- 修改所有自建库 -…

conda环境报错: Solving environment: failed with initial frozen solve.

出现的情况&#xff1a; 解决方法&#xff1a; 参考了许多博客 建议的方法&#xff1a; 创建一个虚拟环境 conda create -n torch_1.3 python3.6 激活虚拟环境 conda activate torch_1.3 conda安装 conda install pytorch1.5.0 如果报错每个包单独安装就可以了&#x…

使用CFimagehost源码自建无需数据库支持的PHP图片托管服务

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

k8s-learning-why we need pod

应用场景 应用从虚拟机迁移到容器中 为什么虚拟机中的应用不能无缝迁移到容器中 虚拟机中应用&#xff1a;一组进程&#xff0c;被管理在systemd或者supervisord中 容器的本质&#xff1a;一个容器一个进程 所以将运行在虚拟机中的应用无缝迁移到容器中&#xff0c;与容器…

addslashes()函数

addslashes() 函数是 PHP 中用于在字符串中的特定字符前添加反斜杠 \ 的函数。它通常用于准备字符串&#xff0c;以防止其中的字符被误解为具有特殊含义的字符。这个函数的主要用途是在构建 SQL 查询语句或其他需要转义特殊字符的上下文中&#xff0c;以防范一些安全问题&#…

ubuntu20.04里面安装目标检测数据标注软件labelImg的详细过程

1.在github克隆仓库到本地 地址&#xff1a;https://github.com/Ruolingdeng/labelImg.git 或者百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1p-478j5WOTN0TKmv3qh-YQ?pwdl8bj 提取码&#xff1a;l8bj 2、进入到labelimg的文件夹&#xff0c;安装pyqt相关依赖包 …

好的CRM系统是什么样的?有没有推荐的CRM?

企业如果引入一套优秀的CRM系统&#xff0c;能极大地提升企业的效率&#xff0c;调动人员积极性。同时&#xff0c;带有自动化功能的CRM系统能帮助企业省去日常经营中琐碎无意义的活动&#xff0c;让企业员工把更多时间集中到业务开发上来&#xff0c;实现经济效益的提升。那么…

下一站 GenAI @你!站稳扶好,“码”上发车

点击下方链接&#xff0c;精彩抢先看https://dev.amazoncloud.cn/column/article/657a74432b6d177219412733?trkcndc-detail 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最…

飞天使-linux操作的一些技巧与知识点4-ansible常用的技巧,配置等

文章目录 ansible配置文件的优先级尝试开始进行操作ansible常用模块ansible 的playbook示例安装phpplaybook中变量的引用 ansible yum install -y ansible 测试是否可用 ansible localhost -m ping /etc/ansible/ansible.cfg &#xff1a;主配置文件&#xff0c;配置 ansible…

解决固定资产盘点问题,易点易动来帮忙!

固定资产盘点是企业管理中不可或缺的环节&#xff0c;然而&#xff0c;很多企业在固定资产盘点方面面临一系列问题&#xff1a; 盘点过程繁琐&#xff1a;传统的手动盘点方式需要耗费大量人力和时间&#xff0c;容易出现疏漏和错误&#xff0c;效率低下&#xff1b; 数据记录不…

物联网有哪些关键技术?

物联网有哪些关键技术? 物联网正在以前所未有的速度改变我们的生活和工作方式。作为一个庞大的网络&#xff0c;物联网连接了各种设备&#xff0c;使其能够相互通信和交互&#xff0c;进而实现智能化的目标。然而&#xff0c;要实现物联网的发展和应用&#xff0c;关键技术的支…

随记-nginx docker + SSL 配置 - 配置等资源挂宿主机

随记-Nginx docker SSL 配置 - 配置等资源挂宿主机等 笔者动手配置&#xff0c;随手写的笔者&#xff0c;保证可操作 话说现在padmon是不是已经有代替docker的趋势了&#xff0c;谁能告诉我一把&#xff1f; 配置前准备 # 拉取nginx镜像 docker pull nginx #启动(暂时) doc…

Docker技术基础梳理 - Docker网络管理

为什么需要容器的网络管理&#xff1f; 容器的网络默认与宿主机、与其他容器相互隔离&#xff0c;且容器中可以运行一些网络应用&#xff0c;比如nginx、web应用、数据库等&#xff0c;如果需要让外部也可以访问这些容器中运行的网络应用&#xff0c;那么就需要配置网络来实现…

深度学习代码片段收集

print(number of model params, sum(p.numel() for p in model.parameters() if p.requires_grad))sum(p.numel() for p in model.parameters() if p.requires_grad )可以用来计算参与训练的参数量 model.parameters() 返回模型中所有参数的迭代器。 if p.requires_grad: 这部…

【ITK库学习】使用itk库进行图像滤波ImageFilter:二阶微分

目录 1、itkRecursiveGaussianImageFliter 递归高斯滤波器2、itkLapacianRecursiveGaussianImageFiter 拉普拉斯高斯滤波器 1、itkRecursiveGaussianImageFliter 递归高斯滤波器 该类用于计算具有高斯核近似值的 IIR 卷积的基类。 该类是递归滤波器的基类&#xff0c;它与高斯…

Netty详细文档

Netty教程 文章目录 Netty教程 Netty简介Netty 的介绍Netty 的应用场景互联网行业游戏行业大数据领域其它开源项目使用到 Netty Netty 的学习资料参考 Java BIO编程I/O 模型BIO、NIO、AIO 使用场景分析Java BIO 基本介绍Java BIO 工作机制Java BIO 应用实例问题分析 Java NIO编…

不得不知的Aspera替代方案,让文件传输变得更轻松

aspera作为一款高效的文件传输工具&#xff0c;可以让用户在网络状况不佳时仍然能够进行快速的大文件传输。然而&#xff0c;由于aspera的高昂价格和复杂的部署难度&#xff0c;很多用户开始寻找aspera替代方案来解决文件传输问题。 1、CFT CFT是一款由BMC Software公司开发的…

中国法拍房数量统计预测模型_2023年法拍房数量竟是。。

法拍房主要平台 法拍房主要平台有3家&#xff0c;分别是阿里、京东和北交互联平台。目前官方认定纳入网络司法拍卖的平台共有7家&#xff0c;其中阿里资产司法拍卖平台的挂拍量最大。 阿里法拍房数据显示2017年&#xff0c;全国法拍房9000套&#xff1b;2018年&#xff0c;法…