Advertisement

OMPL官方教程学习Constrained Planning Tutorial

阅读量:

Constrained Planning Tutorial

带约束的规划,这个应用场景就很多了

从下面这段话中可以看出,使用带约束的规划:

首先需要明确什么是 constraint。
其次要在受限的状态空间内进行规划。
其中满足 f(q): Q \rightarrow R^n 的条件即 f(q) = 0
本次教程采用的约束条件为:

f(q) = \left\| q \right\| - 1

其对应的几何形状为单位球面的限制条件。

Defining a constrained motion planning problem is straightforward, sharing significant similarities with the definition of an unconstrained planning problem. The key distinction lies in the requirement for specifying constraints and employing a confined state space, which encompasses an ambient state space. To illustrate this concept, we will demonstrate how to establish a simple constrained planning scenario: consider a point within three-dimensional Euclidean space (R3) that must lie on the surface of a sphere. This constraint is mathematically represented by the function f(q)=||q||−1.

1.Defining the Constraint

下面代码是官方给出的圆面约束代码,从中可以看出:

  1. 自定义的圆面约束方程Sphere必须继承于Constraint。
  2. 构造函数需接受两个参数:输入状态维度为3和输出状态维度为1。
  3. 该类需实现基础虚函数ompl::base::Constraint::function() ,同时还可以选择其他相关虚函数进行实现。
复制代码
    // Constraints must inherit from the constraint base class. By default, a
    // numerical approximation to the Jacobian of the constraint function is computed
    // using a central finite difference.
    class Sphere : public ob::Constraint
    {
    public:
    // ob::Constraint's constructor takes in two parameters, the dimension of
    // the ambient state space, and the dimension of the real vector space the
    // constraint maps into. For our sphere example, as we are planning in R^3, the
    // dimension of the ambient space is 3, and as our constraint outputs one real
    // value the second parameter is one (this is also the co-dimension of the
    // constraint manifold).
    Sphere() : ob::Constraint(3, 1)
    {
    }
     
    // Here we define the actual constraint function, which takes in some state "x"
    // (from the ambient space) and sets the values of "out" to the result of the
    // constraint function. Note that we are implementing `function` which has this
    // function signature, not the one that takes in ompl::base::State.
    void function(const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::VectorXd> out) const override
    {
        // The function that defines a sphere is f(q) = || q || - 1, as discussed
        // above. Eigen makes this easy to express:
        out[0] = x.norm() - 1;
    }
    };

1.1 Constraint Jacobian

雅可比矩阵是否被使用?该约束函数所对应的雅可比矩阵也是该优化问题的关键组成部分。Jacobian 矩阵也被认为是虚函数的一种形式。用户能够实现该功能。

复制代码
    // Implement the Jacobian of the constraint function. The Jacobian for the
    // constraint function is an matrix of dimension equal to the co-dimension of the
    // constraint by the ambient dimension (in this case, 1 by 3). Similar to
    // `function` above, we implement the `jacobian` method with the following
    // function signature.
    void Sphere::jacobian(const Eigen::Ref<const Eigen::VectorXd> &x, Eigen::Ref<Eigen::MatrixXd> out) const override
    {
    out = x.transpose().normalized();
    }

1.2 Projection

投影约束么。。。也是一个虚函数,用户可以实现

Among its key features, ompl::base::Constraint is distinguished by its projection functionality, encapsulated within ompl::base::Constraint::project(). This method accepts a state that does not satisfy a given constraint and projects it onto the corresponding constraint manifold, yielding a state that does satisfy said constraint. By default, ompl::base::Constraint::project() employs Newton's method, delivering strong performance across typical scenarios.

1.3 Constraint Parameters

约束参数的有两个:tolerancemaxIterations

OMPL的Constraint类中有一个setTolerance()函数用于指定tolerance属性。
maxIterationCount可以通过OMPL的Constraint类中的setMaxIterationCount()函数来设定最大迭代次数。
它们都被用来约束OMPL的Constraint类中的project()方法。
它们各自负责不同的功能模块:一个是控制精度(tolerance),另一个则是管理计算资源(iteration count)。

tolerance parameter is utilized in ompl::base::Constraint::project() to assess whether a state meets the defined constraints
maxIterations variable serves as an upper bound on the number of attempts that the projection process performs, capping the number of iterations when no suitable configuration can be identified.

2.Defining the Constrained State Space

2.1 Ambient State Space

首先要创建任意的状态空间,这个跟常规状态空间定义一样。

复制代码
    // Create the ambient space state space for the problem.
    auto rvss = std::make_shared<ob::RealVectorStateSpace>(3);
     
    // Set bounds on the space.
    ob::RealVectorBounds bounds(3);
    bounds.setLow(-2);
    bounds.setHigh(2);
     
    rvss->setBounds(bounds);

2.2 Constraint Instance

然后就是创建约束:之前的圆形约束

复制代码
    // Create our sphere constraint.
    auto constraint = std::make_shared<Sphere>();

2.3 Constrained State Space

最后创建带约束的状态空间:

从下面的代码可以看出:

  1. 基于约束条件的 **ProjectedStateSpace(math) ** 其输入由 **StateSpace(math) ** 和 ** constraint(math) ** 构成。
  2. 随后构建信息状态空间 **ConstrainedSpaceInformation(math) ** ,其输入依赖于 **ProjectedStateSpace(math) **。
  3. 带有约束条件的状态空间及其无约束版本极为相似,在名称上仅需更改即可。
复制代码
    // Combine the ambient space and the constraint into a constrained state space.
    auto css = std::make_shared<ob::ProjectedStateSpace>(rvss, constraint);
     
    // Define the constrained space information for this constrained state space.
    auto csi = std::make_shared<ob::ConstrainedSpaceInformation>(css);

从下面的话中可以看出:

具有约束条件的状态空间类别共有三种Projective State Spaces、Atlas-based State Spaces以及基于切线丛的State Space。

Having obtained both the ambient state space and the constraint, we are now able to establish the constrained state space. In this illustration, we employ ompl::base::ProjectedStateSpace , employing a projection operator-based approach to fulfill constraints. Notably, alternative constrained state spaces such as ompl::base::AtlasStateSpace and ompl::base::TangentBundleStateSpace offer comparable solutions.

3.Defining a Problem

通过PRM进行求解, 使用***SimpleSetup***构建了一个带有约束条件的空间规划问题; 其中,***SimpleSetup***的构建过程与前文相同, 仅在这一部分采用了新的输入数据类型:即为规划模型提供的是***ConstrainedSpaceInformation***类型的数据信息。

复制代码
    // Simple Setup
    auto ss = std::make_shared<og::SimpleSetup>(csi);

3.1 State Validity Checker

设置有效状态检测函数,这里的有效状态已经变成了一个圆球

复制代码
    bool obstacle(const ob::State *state)
    {
    // As ob::ConstrainedStateSpace::StateType inherits from
    // Eigen::Map<Eigen::VectorXd>, we can grab a reference to it for some easier
    // state access.
    const Eigen::Map<Eigen::VectorXd> &x = *state->as<ob::ConstrainedStateSpace::StateType>();
     
    // Alternatively, we could access the underlying real vector state with the
    // following incantation:
    //   auto x = state->as<ob::ConstrainedStateSpace::StateType>()->getState()->as<ob::RealVectorStateSpace::StateType>();
    // Note the use of "getState()" on the constrained state. This accesss the
    // underlying state that was allocated by the ambient state space.
     
    // Define a narrow band obstacle with a small hole on one side.
    // Z方向-0.1 到 0.1 绝大多是是障碍物,这相当于是一个环带
    if (-0.1 < x[2] && x[2] < 0.1)
    {
        // 环带有缺口
        if (-0.05 < x[0] && x[0] < 0.05)
        // 本来是两个缺口,但这里约束 y < 0,所以只有一个缺口
            return x[1] < 0;
     
        return false;
    }
     
    return true;
    }
     
    // Set the state validity checker in simple setup.
    ss->setStateValidityChecker(obstacle);

下面是示意图,右手定则,右x,平面向内y,上z:

3.2 Start and Goal

随后设定起始点和终点位置,在此之前无需赘述

复制代码
    // Start and goal vectors.
    Eigen::VectorXd sv(3), gv(3);
    sv << 0, 0, -1;
    gv << 0, 0,  1;
     
    // Scoped states that we will add to simple setup.
    ob::ScopedState<> start(css);
    ob::ScopedState<> goal(css);
     
    // Copy the values from the vectors into the start and goal states.
    start->as<ob::ConstrainedStateSpace::StateType>()->copy(sv);
    goal->as<ob::ConstrainedStateSpace::StateType>()->copy(gv);
     
    // If we were using an Atlas or TangentBundleStateSpace, we would also have to anchor these states to charts:
    //   css->anchorChart(start.get());
    //   css->anchorChart(goal.get());
    // Which gives a starting point for the atlas to grow.
     
    ss->setStartAndGoalStates(start, goal);

2.3 Planner

设置规划器为PRM

复制代码
    auto pp = std::make_shared<og::PRM>(csi);
    ss->setPlanner(pp);

4.Solving a Problem

终于明白这个setup 是干啥的了: 用来实际设置参数,为规划做准备

复制代码
    ss->setup();

在实现过程中引入了OMPL库中的几何路径插值函数用于插值计算

Note however that when you initially identify a path through your state space it may not exhibit continuity. This means that gaps between these states especially after any simplifications can often be quite large. If your goal is to identify paths containing closely spaced yet constraint-satisfying states throughout their progression interpolation becomes essential. In this context as shown in your code above ompl geometric PathGeometric interpolate() provides an effective means of achieving such paths.

复制代码
    // Solve a problem like normal, for 5 seconds.
    ob::PlannerStatus stat = ss->solve(5.);
    if (stat)
    {
    // Path simplification also works when using a constrained state space!
    ss->simplifySolution(5.);
     
    // Get solution path.
    auto path = ss->getSolutionPath();
     
    // Interpolation also works on constrained state spaces, and is generally required.
    path.interpolate();
     
    // Then do whatever you want with the path, like normal!
    }
    else
    OMPL_WARN("No solution found!");

最后的结果

带有约束的规划步骤总结:

  1. 生成状态空间对象:
  2. 生成约束对象:
  3. 此过程需实现一个约束函数。
  4. 包括三种不同类型的带约束的状态空间。
  5. 基于上述两种基本组件构建带约束的信息状态空间。
  6. 然后按照以下步骤进行求解:
    a) 设置起始点和目标点;
    b) 执行有效检测;
    c) 配置规划器。
  7. 最后一步是利用ompl库中的interpolate()函数对输出路径进行插值处理。

全部评论 (0)

还没有任何评论哟~