package: static_collision_avoidance
5.0 代办事项
StaticEnvironment::computeConstraint()分析
5.1 launch文件 1 2 3 4 \amr-lmpcc\static_collision_avoidance\launch └── static_collision_avoidance.launch file: 1
文件的开始是一些调试参数的设置
1 2 3 4 5 6 7 8 <arg name ="debug" default ="false" /> <arg unless ="$(arg debug)" name ="launch_prefix" value ="" /> <arg if ="$(arg debug)" name ="launch_prefix" value ="gdb --ex run --args" /> <arg name ="debug_config_parameter" default ="false" /> <arg name ="debug_kinematic_cal" default ="false" /> <arg name ="debug_collision_detection" default ="false" /> <arg name ="config" default ="$(find mobile_robot_state_publisher)" />
然后是两个param
参数
1 2 <param name ="collision_avoidance/local_map" value ="true" type ="bool" /> <param name ="simulation_mode" value ="false" type ="bool" />
为了可视化启动的costmap_2d_markers
节点
1 2 3 4 <node pkg ="costmap_2d" type ="costmap_2d_markers" name ="voxel_visualizer" > <remap from ="voxel_grid" to ="costmap/voxel_grid" /> </node >
代价地图节点costmap_2d_node
1 2 3 4 <node name ="costmap_node" pkg ="costmap_2d" type ="costmap_2d_node" > <rosparam file ="$(find static_collision_avoidance)/config/local_map_params.yaml" command ="load" ns ="costmap" /> </node >
处理静态障碍物的static_collision_avoidance_node
节点
1 2 3 <rosparam file ="$(find static_collision_avoidance)/config/static_avoidance_config.yaml" command ="load" /> <node pkg ="static_collision_avoidance" type ="static_collision_avoidance_node" name ="static_collision_avoidance_node" cwd ="node" respawn ="false" output ="screen" />
5.2 yaml文件 5.2.1 local_map_params.yaml 该文件主要包含地图,雷达等相关信息
5.2.2 static_avoidance_config.yaml 该文件主要包含机器人的描述信息,发布和订阅的话题名,避障的相关信息
5.3 头文件 1 2 3 4 \amr-lmpcc\static_collision_avoidance\include\static_collision_avoidance └── static_environment.h file: 1
在头文件中只定义了一个类StaticEnvironment
5.4 源文件 1 2 3 4 5 \amr-lmpcc\static_collision_avoidance\src ├── static_collision_avoidance_node.cpp └── static_environment.cpp file: 2
5.4.1 static_collision_avoidance_node.cpp 在该函数中定义着main
函数
文件的开头分别是ROS节点的初始化
1 ros::init(argc, argv, ros::this_node::getName());
创建类StaticEnvironment
的实例化对象node_
1 StaticEnvironment node_;
调用成员函数node_.initialize()
进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 if (!node_.initialize()) { ROS_ERROR_STREAM_NAMED("FAILED TO INITIALIZE %s" , ros::this_node::getName().c_str()); exit (1 ); }else { ROS_INFO_STREAM_NAMED("%s INITIALIZE SUCCESSFULLY!!" , ros::this_node::getName().c_str()); ros::spin(); }
5.4.2 static_environment.cpp 1. 成员函数:StaticEnvironment::initialize()
订阅的话题
costmap_node/costmap/costmap
:包costmap_2d
发布
costmap_node/costmap/costmap_updates
:包costmap_2d
发布
predicted_trajectory
:包lmpcc
发布
发布的话题
collision_free_area
:可视化工具rviz
使用
collision_constraints
:包lmpcc
使用
my_map
1 2 3 4 5 6 7 local_map_sub_ = nh_.subscribe("costmap_node/costmap/costmap" , 1 , &StaticEnvironment::LocalMapCallBack, this ); local_map_updates_sub_ = nh_.subscribe("costmap_node/costmap/costmap_updates" , 1 , &StaticEnvironment::LocalMapUpdatesCallBack, this ); pred_traj_sub_ = nh_.subscribe("predicted_trajectory" , 1 , &StaticEnvironment::PredictedTrajectoryCallback, this ); collision_free_pub_ = nh_.advertise<visualization_msgs::MarkerArray>("collision_free_area" ,1 ); collision_constraint_pub_ = nh_.advertise<static_collision_avoidance::collision_free_polygon>("collision_constraints" ,1 ); local_map_pub_ = nh_.advertise<nav_msgs::OccupancyGrid>("my_map" ,1 );
启动的服务
1 2 map_service_ = nh_.serviceClient<nav_msgs::GetMap>("static_map" );
加载参数,这里仅展示部分
1 2 3 4 5 if (!nh_.getParam ("collision_avoidance/map_resolution" , map_resolution_) ) { ROS_WARN(" Parameter '/collision_avoidance/map_resolution not set on %s node" , ros::this_node::getName().c_str()); return false ; }
可视化设置
1 2 3 4 5 6 7 8 9 10 cube1.type = visualization_msgs::Marker::CUBE; cube1.id = 60 ; cube1.color.r = 0.5 ; cube1.color.g = 0.5 ; cube1.color.b = 0.0 ; cube1.color.a = 0.1 ; cube1.header.frame_id = planning_frame_; cube1.ns = "trajectory" ; cube1.action = visualization_msgs::Marker::ADD; cube1.lifetime = ros::Duration(1 );
无障碍区域初始化,注意到这里初始化的大小为prediction_steps_
(25)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 collision_free_C1.resize(prediction_steps_); collision_free_C2.resize(prediction_steps_); collision_free_C3.resize(prediction_steps_); collision_free_C4.resize(prediction_steps_); collision_free_a1x.resize(prediction_steps_); collision_free_a1y.resize(prediction_steps_); collision_free_a2x.resize(prediction_steps_); collision_free_a2y.resize(prediction_steps_); collision_free_a3x.resize(prediction_steps_); collision_free_a3y.resize(prediction_steps_); collision_free_a4x.resize(prediction_steps_); collision_free_a4y.resize(prediction_steps_); collision_free_xmin.resize(prediction_steps_); collision_free_xmax.resize(prediction_steps_); collision_free_ymin.resize(prediction_steps_); collision_free_ymax.resize(prediction_steps_);
获取global_map_
地图信息,该服务由map_server
提供
1 2 3 4 5 6 if (map_service_.call(map_srv_)) { ROS_ERROR("Service GetMap succeeded." ); global_map_ = map_srv_.response.map ; }
2. 成员函数:StaticEnvironment::LocalMapCallBack()
1 2 3 4 5 6 7 8 9 void StaticEnvironment::LocalMapCallBack (const nav_msgs::OccupancyGrid local_map) { if (use_local_map_ ) { local_map_ = local_map; local_map_pub_.publish(local_map_); ROS_INFO("local map update received!" ); } }
3. 成员函数:StaticEnvironment::LocalMapUpdatesCallBack()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void StaticEnvironment::LocalMapUpdatesCallBack (const map_msgs::OccupancyGridUpdate local_map_update) { if (use_local_map_ ) { ROS_ERROR("StaticEnvironment::LocalMapUpdatesCallBack" ); int index = 0 ; geometry_msgs::Pose obs; for (int y = local_map_update.y; y < local_map_update.y + local_map_update.height; y++) { for (int x = local_map_update.x; x < local_map_update.x + local_map_update.width; x++) { local_map_.data[local_map_.info.width * y + x] = local_map_update.data[index++]; } } local_map_pub_.publish(local_map_); } }
4. (重要)成员函数:StaticEnvironment::PredictedTrajectoryCallback()
1 void StaticEnvironment::PredictedTrajectoryCallback (const nav_msgs::Path msg) {}
首先是传入参数的类型nav_msgs::Path
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 std_msgs/Header header uint32 seq time stamp string frame_id geometry_msgs/PoseStamped[] poses std_msgs/Header header uint32 seq time stamp string frame_id geometry_msgs/Pose pose geometry_msgs/Point position float64 x float64 y float64 z geometry_msgs/Quaternion orientation float64 x float64 y float64 z float64 w
并且结合LMPCC::publishPredictedTrajectory
注意到传入参数中有ACADO_N
个(15个)位置坐标,所含信息如下
1 2 3 pred_traj_.poses[i].pose.position.x = acadoVariables.x[i * ACADO_NX + 0 ]; pred_traj_.poses[i].pose.position.y = acadoVariables.x[i * ACADO_NX + 1 ]; pred_traj_.poses[i].pose.orientation.z = acadoVariables.x[i * ACADO_NX + 2 ];
函数的开始是局部变量的定义和参数的初始化
1 2 static_collision_avoidance::collision_free_polygon constraint_msg; pred_traj_ = msg;
然后调用了成员函数StaticEnvironment::ComputeCollisionFreeArea()
1 ComputeCollisionFreeArea();
参数的传递和发布(这里仅展示部分)
1 2 3 4 5 6 constraint_msg.collision_free_a1x = collision_free_a1x; constraint_msg.collision_free_a1y = collision_free_a1y; constraint_msg.collision_free_a2x = collision_free_a2x; constraint_msg.collision_free_a2y = collision_free_a2y; collision_constraint_pub_.publish(constraint_msg);
可视化
5. (重要)成员函数:StaticEnvironment::ComputeCollisionFreeArea()
1 void StaticEnvironment::ComputeCollisionFreeArea () {}
首先获取当前的时间
1 auto start = std ::chrono::steady_clock::now();
局部变量的定义
1 2 3 4 5 int x_path_i, y_path_i; double x_path, y_path, psi_path, theta_search, r; std ::vector <double > C_N; int search_steps = 10 ;
地图的选择
1 2 3 4 5 6 if (use_local_map_ ){ static_map_ = local_map_; }else { static_map_ = global_map_; }
初始角度(collisionfree_delta_min = 2),但是没有被使用过
1 collision_free_delta_min_ = collision_free_delta_max_;
计算预测路径上的每个位置坐标处的限制条件
❗❗❗注意此处prediction_steps_
为25,但是根据上面的分析,预测的路径中只有15个位置坐标。
1 2 3 for (int i = 0 ; i < prediction_steps_; i++){ }
首先获取一个坐标位置
1 2 3 4 x_path = pred_traj_.poses[i].pose.position.x; y_path = pred_traj_.poses[i].pose.position.y; psi_path = pred_traj_.poses[i].pose.orientation.z;
计算这个坐标 x 方向和 y 方向相对于原点的index
1 2 3 x_path_i = (int ) round((x_path - static_map_.info.origin.position.x)/static_map_.info.resolution); y_path_i = (int ) round((y_path - static_map_.info.origin.position.y)/static_map_.info.resolution);
调用成员函数computeConstraint()
计算每个位置坐标的约束条件
1 2 computeConstraint(x_path_i,y_path_i,x_path, y_path, psi_path, i);
获取结束时间,并计算时间间隔
1 2 3 auto end = std ::chrono::steady_clock::now(); te_collision_free_ = double (std ::chrono::duration_cast <std ::chrono::milliseconds> (end-start).count());
输出提示信息
1 2 if (activate_timing_output_) ROS_INFO_STREAM("Free space solve time " << te_collision_free_ << " ms" );
6. (重要)成员函数:StaticEnvironment::computeConstraint()
1 void StaticEnvironment::computeConstraint (int x_i, int y_i, double x_path, double y_path, double psi_path, int N) {}
首先进行局部变量的定义和初始化
1 2 3 4 5 6 7 std::vector<double> t1(2, 0), t2(2, 0), t3(2, 0), t4(2, 0);int x_min, x_max, y_min, y_max;int search_x, search_y;int r_max_i_min, r_max_i_max;
计算搜索范围
1 2 3 4 5 6 7 8 9 r_max_i_min = (int ) round(-collision_free_delta_max_ /static_map_.info.resolution); r_max_i_max = (int ) round(collision_free_delta_max_/static_map_.info.resolution); x_min = r_max_i_min; x_max = r_max_i_max; y_min = r_max_i_min; y_max = r_max_i_max;
初始化搜索的距离,注意这里的search_distance = 2
可以理解为栅格地图
上的两个格,而不是两米。
1 2 3 4 int search_distance = 2 ;bool search_region = true ;
分别在x的正方向和负方向,y的正方向和负方向上进行搜索,最终获得四个边界值x_min,x_max,y_min,y_max
,四个方向的处理方式一样,这里仅展示一个。
在第一轮循环中
if
语句对应x_min
是否改变
for
语句对应y
方向上下2
格的地方
如果遇到了障碍物,x_min
即为2,此时xmin
的值发生改变,不会再进入if
语句
否则,递增search_distance
,判断是否满足结束条件
不满足的话,会再次进入if
语句
注意以下程序是在一个while
循环中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (x_min == r_max_i_min) { search_x = -search_distance; for (int search_y_it = std ::max(-search_distance,y_min); search_y_it < std ::min(search_distance,y_max); search_y_it++) { if (getRotatedOccupancy(x_i, search_x, y_i, search_y_it, psi_path) > occupied_threshold_) { x_min = search_x; } } } search_distance++; search_region = (search_distance < r_max_i_max) && ( x_min == r_max_i_min || x_max == r_max_i_max || y_min == r_max_i_min || y_max == r_max_i_max );
❗❗❗重要
将边界值转换为地图上的坐标,猜测此处的0.35
为用一个圆表示机器人时的机器人半径(计算值为:0.32
),如果猜测正确,那么该值应该设置为变量 。
1 2 3 4 5 collision_free_xmin[N] = x_min*static_map_.info.resolution + 0.35 ; collision_free_xmax[N] = x_max*static_map_.info.resolution - 0.35 ; collision_free_ymin[N] = y_min*static_map_.info.resolution + 0.35 ; collision_free_ymax[N] = y_max*static_map_.info.resolution - 0.35 ;
为了直观一些,我们可以假设x_path = 0
,y_path = 0
,psi_path = 30°
进行分析,则以下代码为四个点的坐标
1 2 3 4 5 6 7 8 9 10 11 std::vector<double> sqx(4,0), sqy(4,0); sqx[0 ] = x_path + cos (psi_path)*collision_free_xmin[N] - sin (psi_path)*collision_free_ymin[N]; sqx[1 ] = x_path + cos (psi_path)*collision_free_xmin[N] - sin (psi_path)*collision_free_ymax[N]; sqx[2 ] = x_path + cos (psi_path)*collision_free_xmax[N] - sin (psi_path)*collision_free_ymax[N]; sqx[3 ] = x_path + cos (psi_path)*collision_free_xmax[N] - sin (psi_path)*collision_free_ymin[N]; sqy[0 ] = y_path + sin (psi_path)*collision_free_xmin[N] + cos (psi_path)*collision_free_ymin[N]; sqy[1 ] = y_path + sin (psi_path)*collision_free_xmin[N] + cos (psi_path)*collision_free_ymax[N]; sqy[2 ] = y_path + sin (psi_path)*collision_free_xmax[N] + cos (psi_path)*collision_free_ymax[N]; sqy[3 ] = y_path + sin (psi_path)*collision_free_xmax[N] + cos (psi_path)*collision_free_ymin[N];
将坐标进行标准化?
1 2 3 4 5 6 7 8 9 t1[0 ] = (sqx[1 ] - sqx[0 ])/sqrt ((sqx[1 ] - sqx[0 ])*(sqx[1 ] - sqx[0 ]) + (sqy[1 ] - sqy[0 ])*(sqy[1 ] - sqy[0 ])); t2[0 ] = (sqx[2 ] - sqx[1 ])/sqrt ((sqx[2 ] - sqx[1 ])*(sqx[2 ] - sqx[1 ]) + (sqy[2 ] - sqy[1 ])*(sqy[2 ] - sqy[1 ])); t3[0 ] = (sqx[3 ] - sqx[2 ])/sqrt ((sqx[3 ] - sqx[2 ])*(sqx[3 ] - sqx[2 ]) + (sqy[3 ] - sqy[2 ])*(sqy[3 ] - sqy[2 ])); t4[0 ] = (sqx[0 ] - sqx[3 ])/sqrt ((sqx[0 ] - sqx[3 ])*(sqx[0 ] - sqx[3 ]) + (sqy[0 ] - sqy[3 ])*(sqy[0 ] - sqy[3 ])); t1[1 ] = (sqy[1 ] - sqy[0 ])/sqrt ((sqx[1 ] - sqx[0 ])*(sqx[1 ] - sqx[0 ]) + (sqy[1 ] - sqy[0 ])*(sqy[1 ] - sqy[0 ])); t2[1 ] = (sqy[2 ] - sqy[1 ])/sqrt ((sqx[2 ] - sqx[1 ])*(sqx[2 ] - sqx[1 ]) + (sqy[2 ] - sqy[1 ])*(sqy[2 ] - sqy[1 ])); t3[1 ] = (sqy[3 ] - sqy[2 ])/sqrt ((sqx[3 ] - sqx[2 ])*(sqx[3 ] - sqx[2 ]) + (sqy[3 ] - sqy[2 ])*(sqy[3 ] - sqy[2 ])); t4[1 ] = (sqy[0 ] - sqy[3 ])/sqrt ((sqx[0 ] - sqx[3 ])*(sqx[0 ] - sqx[3 ]) + (sqy[0 ] - sqy[3 ])*(sqy[0 ] - sqy[3 ]));
结果如下图所示:
得到约束条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 collision_free_a1x[N] = t1[1 ]; collision_free_a2x[N] = t2[1 ]; collision_free_a3x[N] = t3[1 ]; collision_free_a4x[N] = t4[1 ]; collision_free_a1y[N] = -t1[0 ]; collision_free_a2y[N] = -t2[0 ]; collision_free_a3y[N] = -t3[0 ]; collision_free_a4y[N] = -t4[0 ]; collision_free_C1[N] = sqx[0 ]*collision_free_a1x[N] + sqy[0 ]*collision_free_a1y[N]; collision_free_C2[N] = sqx[1 ]*collision_free_a2x[N] + sqy[1 ]*collision_free_a2y[N]; collision_free_C3[N] = sqx[2 ]*collision_free_a3x[N] + sqy[2 ]*collision_free_a3y[N]; collision_free_C4[N] = sqx[3 ]*collision_free_a4x[N] + sqy[3 ]*collision_free_a4y[N];
7. 成员函数:StaticEnvironment::getRotatedOccupancy()
1 2 3 4 5 6 7 8 9 10 11 12 13 int StaticEnvironment::getRotatedOccupancy (int x_i, int search_x, int y_i, int search_y, double psi) { int x_search_rotated = (int ) round(cos (psi) * search_x - sin (psi) * search_y); int y_search_rotated = (int ) round(sin (psi) * search_x + cos (psi) * search_y); if ((x_i + x_search_rotated) > static_map_.info.width || (y_i + y_search_rotated) > static_map_.info.height || (x_i + x_search_rotated) < 0 || (y_i + y_search_rotated) < 0 ) { return (int ) 100 ; } else { return static_map_.data[static_map_.info.width * (y_i + y_search_rotated) + (x_i + x_search_rotated)]; } }