【Apollo Common代码解析-计算点到线段的距离】

计算点到线段的最短距离,在路径规划领域使用较多,是为基础函数,降低其耗时,对整个规划的效率有益。
先以apollo中使用DistanceTo函数实现为例。

 double LineSegment2d::DistanceTo(const Vec2d &point) const
      {
        if (length_ <= kMathEpsilon)
        {
          return point.DistanceTo(start_);
        }
        const double x0 = point.x() - start_.x();
        const double y0 = point.y() - start_.y();
        const double proj = x0 * unit_direction_.x() + y0 * unit_direction_.y();
        if (proj <= 0.0)
        {
          return hypot(x0, y0);
        }
        if (proj >= length_)
        {
          return point.DistanceTo(end_);
        }
        return std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
      }

如果长度很小那么该线段只是一个点,计算两点之间的距离即可。
x0,y0组成的向量即为向量SP,unit_direction_为向量SE的单位向量图中红色的箭头,
那么proj的结果是向量SP和向量unit_direction_的内积,内积的大小是指向量SP在单位向量unit_direction_的投影,如果这个投影距离大于了线段的长度,那么说明该点point距离end_点是最近的,只需要返回这两个点之间的距离即可。
std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
上面这一句明显是叉乘的结果,叉乘的含义是两个向量组成的平行四边形的面积,由于unit_direction_是单位向量,那么该面积实际上就是垂线。
在这里插入图片描述
下面是另外求线段到点的最短距离的算法
假设线段是ab,求点c到线段的距离,其原理是算出三个点的相互距离,找出线段ac.bc中最大的一个max_cab,求出c点到直线ab的距离c_ab,然后用max_cab的平方-c_ab的平方得到的实际上就是ad的距离,如果ad长度小于线段长度,那么垂足就在线段上,如果大于ab那么垂足就在线段之外,只需要取ac,bc的最小值即可。
在这里插入图片描述

  double Point2LineSegment(const Point3d &c, const Point3d &a, const Point3d &b)
    {
        auto dis_between2points = [](const Point3d &Point1, const Point3d &Point2) -> double
        {
            return sqrt((Point1.x - Point2.x) * (Point1.x - Point2.x) + (Point1.y - Point2.y) * (Point1.y - Point2.y));
        };
        double min_dis = 0;
        // 先求出三个点之间的距离
        double ac = dis_between2points(a, c);
        double ab = dis_between2points(a, b);
        double bc = dis_between2points(c, b);
        double max_cab = max(ac, bc);
        // 计算点c到直线AB的距离
        // 根据两个点的坐标求直线方程
        double A = b.y - a.y;
        double B = a.x - b.x;
        double C = b.x * a.y - a.x * b.y;
        auto distance2Line = [](const double x, const double y, const double A, const double B, const double C) -> double
        {
            return std::abs(A * x + B * y + C) / std::sqrt(A * A + B * B);
        };
        double c_ab = distance2Line(c.x, c.y, A, B, C);
        double delt = pow(max_cab, 2) - pow(c_ab, 2);
        if (delt > ab * ab)
        {
            // 说明垂足在线段之外
            min_dis = min(ac, bc);
        }
        else
        { // 说明垂足在线段之内
            min_dis = c_ab;
        }
        return min_dis;
    }

以下是测试用例:

 planner::common::math::Vec2d p0(2, 3);
    planner::common::math::Vec2d p1(5, 4);
    planner::common::math::Vec2d p2(3, 7);

    planner::common::math::LineSegment2d line_segment2d_(p0, p1);
    double dis1 = line_segment2d_.DistanceTo(p2);

    //
    planner::common::math::Vec2d unit_direction_;
    const double dx = p1.x() - p0.x();
    const double dy = p1.y() - p0.y();
    double length_ = hypot(dx, dy);
    unit_direction_ =
        (planner::common::math::Vec2d(dx / length_, dy / length_));
    const double x0 = p2.x() - p0.x();
    const double y0 = p2.y() - p0.y();
    const double proj = x0 * unit_direction_.x() + y0 * unit_direction_.y();
    if (proj <= 0.0)
    {
        // return hypot(x0, y0);
    }
    if (proj >= length_)
    {
        // return p2.DistanceTo(p1);
    }
    double dis4 = std::abs(x0 * unit_direction_.y() - y0 * unit_direction_.x());
    //
    planner::Point3d O;
    planner::Point3d q0(p0.x(), p0.y(), 0);
    planner::Point3d q1(p1.x(), p1.y(), 0);
    planner::Point3d q2(p2.x(), p2.y(), 0);
    planner::Point3d unit_direction(unit_direction_.x(), unit_direction_.y(), 0);
    planner::Point3d p02(x0, y0, 0);
    planner::Point3d p01(p1.x() - p0.x(), p1.y() - p0.y(), 0);
    double dis2 = planner::Point2LineSegment(q2, q0, q1);
    MatplotVisualization_.PlotLineSegmentArrow(O, q0, planner::plot_black_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(O, q1, planner::plot_black_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(O, q2, planner::plot_black_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(O, unit_direction, planner::plot_red_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(O, p02, planner::plot_blue_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(O, p01, planner::plot_green_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(q0, q1, planner::plot_magenta_arrow);
    // MatplotVisualization_.PlotLineSegmentArrow(q1, q2, planner::plot_magenta_arrow);
    MatplotVisualization_.PlotLineSegmentArrow(q0, q2, planner::plot_magenta_arrow);
    MatplotVisualization_.PlotOnePoint(q0, planner::plot_red_point);
    MatplotVisualization_.PlotOnePoint(q1, planner::plot_red_point);
    MatplotVisualization_.PlotOnePoint(q2, planner::plot_red_point);
    MatplotVisualization_.PlotOnePoint(unit_direction, planner::plot_black_point);
    MatplotVisualization_.PlotOnePoint(p02, planner::plot_green_point);
    MatplotVisualization_.PlotOnePoint(p02, planner::plot_blue_point);

    MatplotVisualization_.PlotPltPause(0.1);
    MatplotVisualization_.PlotShow();
    int break_point = 0;

下面是耗时比较:

 planner::common::math::Vec2d p0(2, 3);
    planner::common::math::Vec2d p1(5, 4);
    planner::common::math::Vec2d p2(3, 7);
    auto start_time1 = system_clock::now();
    for (size_t i = 0; i < 10000; i++)
    {
        planner::common::math::LineSegment2d line_segment2d_(p0, p1);
        double dis1 = line_segment2d_.DistanceTo(p2);
    }
    duration<double> cost_time1 = system_clock::now() - start_time1;
    cout << "LineSegment2d time: " << cost_time1.count() << std::endl;
    auto start_time3 = system_clock::now();
    for (size_t i = 0; i < 10000; i++)
    {
        double dis1 =  planner::common::math::LineSegment2d::DistanceToLineSegment(p2,p0,p1) ;
    }
    duration<double> cost_time3 = system_clock::now() - start_time3;
    cout << "DistanceToLineSegment time: " << cost_time3.count() << std::endl;
    planner::Point3d q0(p0.x(), p0.y(), 0);
    planner::Point3d q1(p1.x(), p1.y(), 0);
    planner::Point3d q2(p2.x(), p2.y(), 0);
    auto start_time2 = system_clock::now();
    for (size_t i = 0; i < 10000; i++)
    {
        double dis2 = planner::Point2LineSegment(q2, q0, q1);
    }
    duration<double> cost_time2 = system_clock::now() - start_time2;
    cout << "Point2LineSegment time: " << cost_time2.count() << std::endl;

耗时的结果(单位:s)如下所示,类的构建是很耗时的,采用静态函数,可以提高计算速度。
LineSegment2d time: 0.00124451
DistanceToLineSegment time: 0.0002523
Point2LineSegment time: 0.00047673

以上为个人学习使用,请批评指正,如有侵权,请联系删除。

参考文献
【1】百度apollo源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈喽汽车人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值