前言

之前被安排了活,一个局部区域机器运动控制的工作,大致是一个机器位于一个极限区域时候,机器要进入一个特殊的机制,使得机器可以安全的走出来。其中用到了bezier曲线进行优化路径,今天写一下,正好也给大家分享一下工作和实践的情况。

作者:良知犹存

转载授权以及围观:欢迎关注微信公众号:羽林君

或者添加作者个人微信:become_me


贝塞尔曲线基本介绍

线段都可以被拆分成两个坐标的差来表示,如下面一阶的贝塞尔曲线,P0到P1,可以用一个t进行拆分这段线,分别是线段 t(P0~P1)、线段 1-t(P0~P1),P0和P1叫做, 这条条贝塞尔的两个控制点,而贝塞尔曲线至少要有两个控制点(就是下面的这条直线,一阶贝塞尔曲线)。在贝塞尔曲线与控制点位置相关,这意味着在曲线生成过程中,我们可以通过调节控制点的位置,进而调整整个曲线。

贝塞尔的阶数和次数是一样的,二阶贝塞尔,三个点,最高次数二次。例:二阶贝塞尔:三个点,两个线段,以所有等比的点组合成的曲线叫做二阶贝塞尔曲线。

接下来给大家介绍一下贝塞尔曲线的推导工程,也比较简单,并且网上的介绍也挺多的:

一阶:

这里面有两个控制点为$ P_0 (0,0) 和P_1 (1,1)$ ,对应的曲线方程为:

$$ B\big( t \big) = P_t = (1 - t) P_0 + tP_1 = P_0 + (P_1 - P_0)t $$

tϵ[0,1]

这个方程可以理解为,从$P_0$出发,朝着$P_1$的方向前进$||P_1-P_0||t$的距离,从而得到了点B(t)的位置。t从0逐渐递增到1,这个过程完成,就成了我们所看到的曲线。

另外,之所以是一阶贝塞尔曲线是因为方程是关于t的一阶多项式,多阶也是一样。

二阶:

有三个控制点,这里的 P0、P1、P2 分别称之为控制点,曲线的产生完全与这三个点位置相关。

与一阶有些区别就在于三个控制点形成两个线段,每个线段上有一个点在运动,于是得到两个点;

再使用两个点形成一个线段,这个线段上有一个点在运动,于是得到一个点;最后一个点的运动轨迹便构成了二阶贝塞尔曲线。



对应的曲线方程为:

$$ P_a = (1 - t) P_0 + tP_1 = P_0 + (P_1 - P_0)t $$

$$ P_b = (1 - t) P_1 + tP_2 = P_1 + (P_2 - P_1)t $$

$$ P_t = (1 - t) P_a + tP_b = P_a + (P_b - P_a)t $$

这是一条迭代公式,每次迭代都会少掉一个“点”。

最后得:

$$ B\big( t \big) = P_t = (1 - t)^2 P_0 + 2t(t -1)P_1 + t^2 P_2 $$

三阶:

有四个控制点

设控制点为P0,P1,P2和P4,曲线方程为:

$$ B\big( t \big) = P_t = (1 - t)^3 P_0 + 3t(t -1)^2t P_1 + 3t^2(1-t) P_2+t^3P_3 $$

配图这是matlab生成的gif动画,大家想要的也可以找我,代码私发给大家。

N阶:

我们发现,实际上是每轮都是 n 个点,形成 n-1 条线段,每个线段上有一个点在运动,那么就只关注这 n-1 个点,循环往复。最终只剩一个点时,它的轨迹便是结果。

如此一来,你会发现贝塞尔曲线内的递归结构。实际上,上述介绍的分别是一阶、二阶、三阶的贝塞尔曲线,贝塞尔曲线可以由阶数递归定义。

N阶贝塞尔曲线公式:

$$ B\big( t \big) = \sum\limits_{i=0}^{n} \big(_{i}^{n} \big) P_i(1-t)^{n-i} t^i ,t\in [0,1]$$

贝塞尔曲线应用

贝塞尔曲线在动画中有应用,前端以及一些其他显示要求;此外在路径规划过程中,也会使用贝塞尔曲线进行规划好路径再优化,我就是使用了后者进行优化规划好的路径,使得机器行走更加顺畅,不过使用中大家需要按照机器实际相应来进行调整t的精度以及阶数。

由于贝塞尔曲线本身的数学表达式便是一条递归式,所以决定采用递归的方式来实现。代码如下,BezierCurve函数实现贝塞尔曲线迭代,UseBezierOptimizePath函数的第二个参数进行控制使用的阶数,最后调用opencv实现可视化效果。

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <vector>
using namespace cv;
using std::cout;
using std::endl;
using std::vector; template <typename T>
T BezierCurve(T src)
{
if (src.size() < 1)
return src;
const float step = 0.003;//1.0/step
T res;
if (src.size() == 1) {//递归结束条件
for (float t = 0; t < 1; t += step)
res.push_back(src[0]);
return res;
}
T first_part{};
T second_part{};
first_part.assign(src.begin(), src.end() - 1);
second_part.assign(src.begin() + 1, src.end()); T pln1 = BezierCurve(first_part);
T pln2 = BezierCurve(second_part);
for (float t = 0; t < 1; t += step)
{
typename T::iterator::value_type temp{};
temp += pln1[cvRound(1.0 / step * t)] * (1.0 - t) ;
temp += pln2[cvRound(1.0 / step * t)] * t;
res.emplace_back(temp);
}
return res;
}
template <typename T>
T UseBezierOptimizePath(T path,uint8_t order_number)
{
if(path.size() < order_number)
return {};
T new_path{};
for(uint8_t i=0;i<path.size()-(order_number-1);i+=(order_number-1))
{
T tmp = BezierCurve(T(&path[i],&path[ i + order_number]));
new_path.insert(new_path.begin(),tmp.begin(),tmp.end());
} return new_path;
} int main(int argc, char const* argv[])
{
while (1) {
cout<< endl;
cout<< endl;
cout<< endl;
vector<Point2f> path;
RNG rng; for (int i = 1; i <8; i++)
path.push_back(Point2f(i * 800 / 8, random() % 800));//rng.uniform(0,800)));//cvRandInt(rng) % 800));
Mat img(900, 1200, CV_8UC3);
img = 0; for(uint8_t i =0;i < path.size() -1;i++)
{
cout<< path[i]<< ","<< endl;
line(img,Point(path[i].x, path[i].y),Point(path[i+1].x, path[i+1].y), Scalar(255, 0, 0), 16, LINE_AA, 0);
}
cout<< endl;
// imshow("line", img);
for (int i = 0; i < path.size(); i++)
circle(img, path[i], 3, Scalar(0, 0, 255), 10); //BGR // vector<Point2f> bezierPath = bezierCurve(path);
vector<Point2f> bezierPath = UseBezierOptimizePath(path,4);
for (int i = 0; i < bezierPath.size(); i++) {
// circle(img, bezierPath[i], 3, Scalar(0, 255, 255), 3); //BGR
img.at<cv::Vec3b>(cvRound(bezierPath[i].y), cvRound(bezierPath[i].x)) = { 0, 255, 255 };
// printf("pose(%f %f)\n",bezierPath[i].x,bezierPath[i].y);
imshow("black", img);
// waitKey(10);
}
if (waitKey(0) == 'q')
break;
}
return 0;
}

显示效果如下:

三阶

四阶

结语

这就是我自己的一些设不贝塞尔曲线的使用分享。如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。


作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推荐阅读

【1】jetson nano开发使用的基础详细分享

【2】Linux开发coredump文件分析实战分享

【3】CPU中的程序是怎么运行起来的 必读

【4】cartographer环境建立以及建图测试

【5】设计模式之简单工厂模式、工厂模式、抽象工厂模式的对比

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。

(Bezier)贝塞尔曲在路径规划的运用的更多相关文章

  1. Unity路径规划

    Unity路径规划  转自:http://www.cnblogs.com/zsb517/p/4090629.html 背景 酷跑游戏中涉及到弯道.不规则道路. 找来一些酷跑游戏的案例来看,很多都是只有 ...

  2. 游戏AI之路径规划(3)

    目录 使用路径点(Way Point)作为节点 洪水填充算法创建路径点 使用导航网(Navigation Mesh)作为节点 区域分割 预计算 路径查询表 路径成本查询表 寻路的改进 平均帧运算 路径 ...

  3. 【路径规划】 Optimal Trajectory Generation for Dynamic Street Scenarios in a Frenet Frame (附python代码实例)

    参考与前言 2010年,论文 Optimal Trajectory Generation for Dynamic Street Scenarios in a Frenet Frame 地址:https ...

  4. 【BZOJ-3627】路径规划 分层图 + Dijkstra + spfa

    3627: [JLOI2014]路径规划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 186  Solved: 70[Submit][Status] ...

  5. 基于谷歌地图的Dijkstra算法水路路径规划

    最终效果图如下: 还是图.邻接表,可以模拟出几个对象=>节点.边.路径.三个类分别如下: Node 节点: using System; using System.Collections.Gene ...

  6. iOS百度地图路径规划和POI检索详细总结-b

    路径规划.png 百度地图的使用 百度地图API的导入网上说了许多坑,不过我遇到的比较少,这里就放两个比较常见的吧.坑一: 奥联WIFI_xcodeproj.png 如上图所示,在infoplist里 ...

  7. COJ 0500 杨老师的路径规划(MST)最小生成树

    杨老师的路径规划(MST) 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 为满足同学们需求,杨老师在实验楼4层新建了好多个计算 ...

  8. canvas-js贝塞尔曲代码在线生成工具

    canvas贝塞尔曲代码在线生成工具 可以快速生成二次.三次贝塞尔曲线的源码生成器,方便经常使用到canvas画图的同学使用,可以直接预览效果随意画出自己想要的图像. 生成源码效果预览: canvas ...

  9. octomap中3d-rrt路径规划

    路径规划 碰撞冲突检测 在octomap中制定起止点,目标点,使用rrt规划一条路径出来,没有运动学,动力学的限制,只要能避开障碍物. 效果如下: #include "ros/ros.h&q ...

随机推荐

  1. C++面试问题汇总

    作者:勿忘心安~~链接:https://www.nowcoder.com/discuss/197611来源:牛客网 1 C++基础: (1)多态是怎么样的?写个样例? https://www.cnbl ...

  2. 利用Properties类关联相关配置文件

    文件目录 代码: package Lianxi;import java.io.FileInputStream;import java.io.FileNotFoundException;import j ...

  3. 【C语言】预处理、宏定义、内联函数

    一.由源码到可执行程序的过程 1. 预处理: 源码经过预处理器的预处理变成预处理过的.i中间文件 1 gcc -E test.c -o test.i 2. 编译: 中间文件经过编译器编译形成.s的汇编 ...

  4. 算法 | 串匹配算法之KMP算法及其优化

    主串 s:A B D A B C A B C 子串 t:  A B C A B 问题:在主串 s 中是否存在一段 t 的子串呢? 形如上述问题,就是串匹配类问题.[串匹配--百度百科] 串匹配问题是一 ...

  5. Numpy的数学统计函数

    Numpy的数学统计函数 本节内容: 1.Numpy有哪些数学统计函数: 函数名 说明 np.sum 所有元素的和 np.prod 所有元素的乘积 np.cumsum 元素的累积加和 np.cumpr ...

  6. mplab使用小知识

    选择Debugger->Select Tool->MPLAB SIM可以使用MPALB中的软件调试 StopWatch可以观察程序运行时间 注意:在测试时需要注意红圈内晶振是不是和单片机上 ...

  7. 5_终值定理和稳态误差_Final Value Theorem & Steady State Error

  8. css预编译--sass进阶篇

    基础篇中主要介绍了一些sass的基本特性,进阶篇中,主要是写一些我们常用的sass控制命令,函数和规则. 控制命令 可能看过基础篇的朋友会发现在有些代码中出现@if @else @each等,熟悉JS ...

  9. css写作建议和性能优化小结

    1.前言 还有几天就到国庆中秋了,快要放假了,先祝大家节日快乐!之前写过js的写作建议和技巧,那么今天就来聊聊css吧!说到css,每一个网页都离不开css,但是对于css,很多开发者的想法就是,cs ...

  10. break,return,continue的区别和作用

    学习目标: 理解break.return.continue在循环中的区别和作用 学习内容: 1.break break表示结束当前所在的循环. 循环输出到3,当i等于4后,跳出当前循环,继续向下执行循 ...