g2o边和顶点的定义
所有边都在头文件base_teb_edges.h
中,顶点就是g2o_types\vertex_timediff.h
和 g2o_types\vertex_pose.h
。两个顶点都比较简单,值得注意的是vertex_timediff.h
的setToOriginImpl
1
2
3
4virtual void setToOriginImpl()
{
_estimate = 0.1; // 不是0,不明白为什么
}
一元边:1
2template <int D, typename E, typename VertexXi>
class BaseTebUnaryEdge : public g2o::BaseUnaryEdge<D, E, VertexXi>
EdgeViaPoint:以指定的经过点位置与待优化位姿(顶点)的误差作为目标。(保证轨迹会经过指定点)
EdgeObstacle:以障碍物与待优化位姿(顶点)的距离经过惩罚函数后的输出作为目标(保证离障碍物大于一定距离)
EdgeTimeOptimal:以时间间隔dt最小作为目标(保证时间最短轨迹)
二元边1
2template <int D, typename E, typename VertexXi, typename VertexXj>
class BaseTebBinaryEdge : public g2o::BaseBinaryEdge<D, E, VertexXi, VertexXj>
主要是考虑了运动学的约束关系(车的相邻的两个位姿态必须落在一个圆弧上)。二元边的两个顶点分别是两个位姿,还限制了最小转弯半径。
多元边(构造函数中resize顶点的个数):1
2template <int D, typename E>
class BaseTebMultiEdge : public g2o::BaseMultiEdge<D, E>
EdgeVelocity:以实际速度和限定最大速度经过惩罚函数的输出作为目标函数,顶点为相邻的位姿和时间间隔。(限制实际速度不超过最大,程序中引入了
sigmoid
函数决定速度的符号(根据论文内的说法,引入此函数是因为优化算法只能求解连续函数)Edgevelocityholonomic:与
EdgeVelocity
的区别在于,ds是机器人本体坐标系下的,然后去求速度在本体坐标系下的表示。其余思路一致
1 | class VertexPose : public g2o::BaseVertex<3, PoseSE2 > |
初始化 optimizer
planner_
初始化在if (cfg_.hcp.enable_homotopy_class_planning)
的判断里,然后实际调用TebOptimalPlanner::initialize
或者HomotopyClassPlanner::initialize
,前者调用了TebOptimalPlanner::initOptimizer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26boost::shared_ptr<g2o::SparseOptimizer> TebOptimalPlanner::initOptimizer()
{
// Call register_g2o_types once, even for multiple TebOptimalPlanner instances (thread-safe)
static boost::once_flag flag = BOOST_ONCE_INIT;
// 将为TEB定义的顶点和边注册到g2o::Factory
boost::call_once(®isterG2OTypes, flag);
// allocating the optimizer
boost::shared_ptr<g2o::SparseOptimizer> optimizer = boost::make_shared<g2o::SparseOptimizer>();
// linear solver utilized for optimization
// 针对稀疏矩阵,写死为 g2o::LinearSolverCSparse
// typedef g2o::LinearSolverCSparse<TEBBlockSolver::PoseMatrixType> TEBLinearSolver;
TEBLinearSolver* linearSolver = new TEBLinearSolver(); // see typedef in optimization.h
linearSolver->setBlockOrdering(true);
// block solver utilized for optimization
// typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > TEBBlockSolver;
TEBBlockSolver* blockSolver = new TEBBlockSolver(linearSolver);
g2o::OptimizationAlgorithmLevenberg* solver =
new g2o::OptimizationAlgorithmLevenberg(blockSolver);
optimizer->setAlgorithm(solver);
optimizer->initMultiThreading(); // required for >Eigen 3.1
return optimizer;
}
注意算法求解器写死为 利文伯格法 和 g2o::LinearSolverCSparse
构建超图 buildGraph
1 | bool TebOptimalPlanner::buildGraph(double weight_multiplier) |
添加 vertex
1 | void TebOptimalPlanner::AddTEBVertices() |
这里的Vertex没有调用setEstimate
函数,不设置初值。
添加约束
1 | void TebOptimalPlanner::AddEdgesVelocity() |
error有两项,分别是线速度与线速度线速度是否在设定好的区间内。
只要看懂了论文和g2o的应用套路,这里的添加的edge大部分都能看懂。比较复杂而且重要的是AddEdgesObstacles
和 AddEdgesViaPoints
。 via_point
规定了轨迹应当经过这些点,否则会产生相应的cost。via_point边会与原规划的路径中与其距离最近的Pose顶点相连。
对于加速度约束,还要注意 vel_start_.first
和 vel_goal_.first
,前者一般会添加,后者基本是false,信息矩阵一直是同一个。
1 | void TebOptimalPlanner::clearGraph() |
局部规划可以让路径显示graph,在rviz中配置