引言
本文继续对地形建模开源代码中关于probabilistic road map的构建进行解读。
代码解读
- 话题订阅
订阅了两个话题,目标点prm_goal和局部占据地图occupancy_map_local_height
subGoal = nh.subscribe<geometry_msgs::PoseStamped>("/prm_goal", 5, &TraversabilityPRM::goalPosHandler, this);
subElevationMap = nh.subscribe<elevation_msgs::OccupancyElevation>("/occupancy_map_local_height", 5, &TraversabilityPRM::elevationMapHandler, this);
- roadmap构建
首先更新地图的边界,然后对代价地图进行更新,然后构建roadmap
void elevationMapHandler(const elevation_msgs::OccupancyElevation::ConstPtr& mapMsg){
std::lock_guard<std::mutex> lock(mtx);
elevationMap = *mapMsg;
updateMapBoundary();
updateCostMap();
buildRoadMap();
}
下面重点查看roadmap
void buildRoadMap(){
// 1. generate samples
generateSamples();
// 2. Add edges and update state height if map is changed
updateStatesAndEdges();
// 3. Planning
bfsSearch();
// 4. Visualize Roadmap
publishPRM();
// 5. Publish path to move_base and stop planning
publishPathStop();
// 6. Convert PRM Graph into point cloud for external usage
publishRoadmap2Cloud();
}
首先,产生采样点,将合适的采样点加入到kd-tree中,更新nodeList
void generateSamples(){
double sampling_start_time = ros::WallTime::now().toSec();
while (ros::WallTime::now().toSec() - sampling_start_time < 0.002 && ros::ok()){
state_t* newState = new state_t;
if (sampleState(newState)){
// 1.1 Too close discard
if (nodeList.size() != 0 && stateTooClose(newState) == true){
delete newState;
continue;
}
// 1.2 Save new state and insert to KD-tree
newState->stateId = nodeList.size(); // mark state ID
nodeList.push_back(newState);
insertIntoKdtree(newState);
}
else
delete newState;
}
}
接下来是更新节点和边
在地图中心邻域内找到采样点,遍历所有采样点之间的关系,如果两个高度差过大,那么就删去对应的边,如果距离过于远,也删去对应的边。然后执行edgepropagation,如果两点之间的连线不穿过障碍物,就进行连接,更新距离代价。
void updateStatesAndEdges(){
getRobotState();
// 0. find local map center
mapCenter->x[0] = (map_min[0] + map_max[0]) / 2;
mapCenter->x[1] = (map_min[1] + map_max[1]) / 2;
mapCenter->x[2] = robotState->x[2];
// 1. add edges for the nodes that are within a certain radius of mapCenter
vector<state_t*> nearStates;
getNearStates(mapCenter, nearStates, neighborSearchRadius);
if (nearStates.size() == 0)
return;
// 3. update states height values (because the map is changing all the time)
for (int i = 0; i < nearStates.size(); ++i){
float thisHeight = getStateHeight(nearStates[i]);
if (thisHeight != -FLT_MAX) // new height can be -FLT_MAX since the map is shifted a bit every time (round error)
nearStates[i]->x[2] = thisHeight;
}
// 4. loop through all neighbors
float edgeCosts[NUM_COSTS];
neighbor_t thisNeighbor;
for (int i = 0; i < nearStates.size(); ++i){
for (int j = i+1; j < nearStates.size(); ++j){
// 4.1 height difference larger than threshold, too steep to connect
if (abs(nearStates[i]->x[2] - nearStates[j]->x[2]) > neighborConnectHeight){
deleteEdge(nearStates[i], nearStates[j]);
continue;
}
// 4.2 distance larger than x, too far to connect
float distanceBetween = distance(nearStates[i]->x, nearStates[j]->x);
if (distanceBetween > neighborConnectRadius || distanceBetween < 0.3){
deleteEdge(nearStates[i], nearStates[j]);
continue;
}
// 4.3 this edge is connectable
if(edgePropagation(nearStates[i], nearStates[j], edgeCosts) == true){
// even if edge already exists, we still need to update costs (because elevation may change)
deleteEdge(nearStates[i], nearStates[j]);
for (int k = 0; k < NUM_COSTS; ++k)
thisNeighbor.edgeCosts[k] = edgeCosts[k];
thisNeighbor.neighbor = nearStates[j];
nearStates[i]->neighborList.push_back(thisNeighbor);
thisNeighbor.neighbor = nearStates[i];
nearStates[j]->neighborList.push_back(thisNeighbor);
}else{ // edge is not connectable, delete old edge if it exists
deleteEdge(nearStates[i], nearStates[j]);
}
}
}
}
只是判断连接线上有无障碍物,很简单。
bool edgePropagation(state_t *state_from, state_t *state_to, float edgeCosts[NUM_COSTS]){
// 0. initialize edgeCosts
for (int i = 0; i < NUM_COSTS; ++i)
edgeCosts[i] = 0;
// 1. segment the edge for collision checking
int steps = floor(distance(state_from->x, state_to->x) / (mapResolution));
float stepX = (state_to->x[0]-state_from->x[0]) / steps;
float stepY = (state_to->x[1]-state_from->x[1]) / steps;
float stepZ = (state_to->x[2]-state_from->x[2]) / steps;
// 2. allocate memory for a state, this state must be deleted after collision checking
state_t *stateCurr = new state_t;;
stateCurr->x[0] = state_from->x[0];
stateCurr->x[1] = state_from->x[1];
stateCurr->x[2] = state_from->x[2];
int rounded_x, rounded_y, indexInLocalMap;
// 3. collision checking loop
for (int stepCount = 0; stepCount < steps; ++stepCount){
stateCurr->x[0] += stepX;
stateCurr->x[1] += stepY;
stateCurr->x[2] += stepZ;
rounded_x = (int)((stateCurr->x[0] - map_min[0]) / mapResolution);
rounded_y = (int)((stateCurr->x[1] - map_min[1]) / mapResolution);
indexInLocalMap = rounded_x + rounded_y * elevationMap.occupancy.info.width;
if (isIncollision(rounded_x, rounded_y, indexInLocalMap)){
delete stateCurr;
return false;
}
// costs propagation
if (costUpdateFlag[2])
edgeCosts[2] = edgeCosts[2] + mapResolution; // distance cost
}
delete stateCurr;
return true;
}
接下来是基于bfs(),进行机器人状态点到目标点的路径搜索。基本思想是从机器人最近的状态开始进行宽度优先搜索,找到起点到任意一个节点的最近路径。然后找到距离目标点最近的状态,就找到了最近路径。
bool bfsSearch(){
pathList.clear();
globalPath.poses.clear();
// 0. Planning or not
if (planningFlag == false)
return true;
// 1. reset costs and parents
for (int i = 0; i < nodeList.size(); ++i){
for (int j = 0; j < NUM_COSTS; ++j)
nodeList[i]->costsToRoot[j] = FLT_MAX;
nodeList[i]->parentState = NULL;
}
// 2. find the state that is the closest to the robot
state_t *startState = NULL;
vector<state_t*> nearRobotStates;
getNearStates(robotState, nearRobotStates, 2);
if (nearRobotStates.size() == 0)
return false;
float nearRobotDist = FLT_MAX;
for (int i = 0; i < nearRobotStates.size(); ++i){
float dist = distance(nearRobotStates[i]->x, robotState->x);
if (dist < nearRobotDist && nearRobotStates[i]->neighborList.size() != 0){
nearRobotDist = dist;
startState = nearRobotStates[i];
}
}
if (startState == NULL || startState->neighborList.size() == 0)
return false;
for (int i = 0; i < NUM_COSTS; ++i)
startState->costsToRoot[i] = 0;
// 3. BFS search
float thisCost;
vector<state_t*> Queue;
Queue.push_back(startState);
while(Queue.size() > 0 && ros::ok()){
// find the state that can offer lowest cost in this depth and remove it from Queue
state_t *fromState = minCostStateInQueue(Queue);
Queue.erase(remove(Queue.begin(), Queue.end(), fromState), Queue.end());
// stop searching if minimum cost path to goal is found
// if (fromState == nearestGoalState)
// break;
// loop through all neighbors of this state
for (int i = 0; i < fromState->neighborList.size(); ++i){
state_t *toState = fromState->neighborList[i].neighbor;
// Loop through cost hierarchy
for (vector<int>::const_iterator iter = costHierarchy.begin(); iter != costHierarchy.end(); iter++){
int costIndex = *iter;
thisCost = fromState->costsToRoot[costIndex] + fromState->neighborList[i].edgeCosts[costIndex];
// If cost can be improved, update this node with new costs
if (thisCost < toState->costsToRoot[costIndex]){
updateCosts(fromState, toState, i); // update toState's costToRoot
toState->parentState = fromState; // change parent for toState
Queue.push_back(toState);
}
// If cost is same, go to compare secondary cost
else if (thisCost == toState->costsToRoot[costIndex]){
continue;
}
// If cost becomes higher, abort this propagation
else
break;
}
}
}
// 4. Find valid nearestGoalState in PRM
nearestGoalState = getNearestState(goalState);
// find near states to nearestGoalState
vector<state_t*> nearGoalStates;
getNearStates(nearestGoalState, nearGoalStates, 20);
if (nearGoalStates.size() > 0){
float nearRobotDist = FLT_MAX;
for (int i = 0; i < nearGoalStates.size(); ++i){
float dist = distance(nearGoalStates[i]->x, goalState->x);
if (dist < nearRobotDist && nearGoalStates[i]->parentState != NULL){
nearRobotDist = dist;
nearestGoalState = nearGoalStates[i];
}
}
}
// the nearest goal state is invalid
if (nearestGoalState == NULL || nearestGoalState->parentState == NULL) // no path to the nearestGoalState is found
return false;
// 4. Extract path
state_t *thisState = nearestGoalState;
while (thisState->parentState != NULL){
pathList.insert(pathList.begin(), thisState);
thisState = thisState->parentState;
}
pathList.insert(pathList.begin(), robotState); // add current robot state
// pathList.push_back(goalState); // add goal state
// 5. Smooth path
smoothPath();
return true;
}
版权声明:本文为jiweinanyi原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。