(Bezier)贝塞尔曲在路径规划的运用
前言
之前被安排了活,一个局部区域机器运动控制的工作,大致是一个机器位于一个极限区域时候,机器要进入一个特殊的机制,使得机器可以安全的走出来。其中用到了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 ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
推荐阅读
【3】CPU中的程序是怎么运行起来的 必读
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。
(Bezier)贝塞尔曲在路径规划的运用的更多相关文章
- Unity路径规划
Unity路径规划 转自:http://www.cnblogs.com/zsb517/p/4090629.html 背景 酷跑游戏中涉及到弯道.不规则道路. 找来一些酷跑游戏的案例来看,很多都是只有 ...
- 游戏AI之路径规划(3)
目录 使用路径点(Way Point)作为节点 洪水填充算法创建路径点 使用导航网(Navigation Mesh)作为节点 区域分割 预计算 路径查询表 路径成本查询表 寻路的改进 平均帧运算 路径 ...
- 【路径规划】 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 ...
- 【BZOJ-3627】路径规划 分层图 + Dijkstra + spfa
3627: [JLOI2014]路径规划 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 186 Solved: 70[Submit][Status] ...
- 基于谷歌地图的Dijkstra算法水路路径规划
最终效果图如下: 还是图.邻接表,可以模拟出几个对象=>节点.边.路径.三个类分别如下: Node 节点: using System; using System.Collections.Gene ...
- iOS百度地图路径规划和POI检索详细总结-b
路径规划.png 百度地图的使用 百度地图API的导入网上说了许多坑,不过我遇到的比较少,这里就放两个比较常见的吧.坑一: 奥联WIFI_xcodeproj.png 如上图所示,在infoplist里 ...
- COJ 0500 杨老师的路径规划(MST)最小生成树
杨老师的路径规划(MST) 难度级别:B: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 为满足同学们需求,杨老师在实验楼4层新建了好多个计算 ...
- canvas-js贝塞尔曲代码在线生成工具
canvas贝塞尔曲代码在线生成工具 可以快速生成二次.三次贝塞尔曲线的源码生成器,方便经常使用到canvas画图的同学使用,可以直接预览效果随意画出自己想要的图像. 生成源码效果预览: canvas ...
- octomap中3d-rrt路径规划
路径规划 碰撞冲突检测 在octomap中制定起止点,目标点,使用rrt规划一条路径出来,没有运动学,动力学的限制,只要能避开障碍物. 效果如下: #include "ros/ros.h&q ...
随机推荐
- 解释 MySQL 外连接、内连接与自连接的区别 ?
先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一 个表的所有记录和另一个表中的所有记录一一匹配. 内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合 条 ...
- java-LinkedMap
输入一组数,输出是按每个出现的频率,比如1,3,3,4,5,9,9,9,3,3,输出为3,3,3,3,9,9,9,1,4,5如果频率一样就按原顺序输出. package com.lyb.array;i ...
- Linux 搭建Apollo
简介 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理场景 ...
- 惯性传感器(IMU)
近两年来,车联网.自动驾驶.无人驾驶.汽车智能化.网联化等成为了汽车行业的热点话题,未来汽车一定是朝着安全.可靠及舒适的方向发展.而这一切背后的发展都离不开传感器的作用,今天我们就来聊聊用途越来越广的 ...
- 深入理解ES6之《ES7》
指数运算符 Math.pow是可以进行求幂运算的求幂运算符是两个星号 let result = 5 ** 2 console.log(result) //25 console.log(result = ...
- 设计模式-观察者模式Observe的实现
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> / ...
- 记一次曲折的CVE-2018-1270复现分析
前言 前两天接到朋友对某个授权目标的漏扫结果,也算是初次接触到这个漏洞,就想着顺手分析一下复现一下,因为分析这个漏洞的文章也比较少,所以刚开始比较迷,进度也比较慢. 漏洞复现 使用vulhub搭建环境 ...
- Android Studio连接SQLite数据库与SQLite Studio实时同步的实现
最近学习用到了android开发连接数据库这一块,发现连接成功后,都要先访问安卓项目的数据库路径data/data/项目/databases,然后把对应的db文件拷出来,再在SQLite的可视化工具中 ...
- 移动端页面中点击input输入框的时候弹出的键盘将输入框挡住的问题
使用的是vux框架, 以为是框架问题, 后来发现是把当前页面的高度写死为了height:200%: 只要把高度去掉就能让页面自动弹到输入框的上方:
- caioj 1000: [视频]整数运算[水题]
题目大意:输入两个整数a和b,输出他们的和. 题解:水题不用题解,简单看一下就知道了-- 代码: #include <cstdio> int a, b; int main() { whil ...