贝塞尔曲线原理、推导及Matlab实现
一、简介
贝塞尔曲线提出
在数学的数值分析领域中,贝塞尔曲线(English:Bézier curve)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝兹曲面,其中贝兹三角是一种特殊的实例。
贝塞尔曲线于1962年,由法国工程师皮埃尔·贝兹(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。
贝塞尔曲线与F1的渊源
说到皮埃尔·贝兹,就不得不提他作为雷诺公司的一名工程师,曾引领了设计和制造业的转型,将它们从单纯使用数学和计算工具带向了计算机辅助设计和三维模型,同时是实体造型、几何模型和物理模型领域的奠定者之一。
而雷诺公司早期于1977年开始,作为车队和发动机供应商在不同时期里参加了F1(一级方程式赛事)。在1977年的最后五站比赛中,雷诺以厂队的身份踏进了F1比赛,并且将涡轮增压器引入其第一款赛车雷诺 RS01中却遭众人嘲笑,然而在法国的分站赢得胜利之后,不到两年时间,所有一流车队都改用涡轮发动机。在1992年雷诺凭英国车手文素和威廉士车队拿下史上第一个年度车队冠军,而接下来五年的年度车队冠军也都是使用雷诺发动机(除1995年是班尼顿,其余皆为威廉士),不过随着其在98年退出F1,雷诺在F1的霸业也宣告结束。雷诺在2000年接手贝纳通车队重返一级方程式赛车。在2002年,车队改名为雷诺一级方程式车队,更在2005年和2006年赛季中同时夺得车队和车手世界冠军。
现今雷诺公司旗下的F1车队为AlpineF1车队(法语:Alpine F1 Team),全名倍世AlpineF1车队(法语:BWT Alpine F1 Team)。本赛季Alpine车队的新赛车A524在赛季伊始便严重缺乏竞争力;赛季揭幕战巴林大奖赛上,排位赛中双车排名垫底在第一节即被淘汰,正赛17、18名带回。在沙特大奖赛,双车17、18位发车,正赛加斯利因变速箱问题退赛,奥康带回13名。位列厂队垫底,心疼他们的两名车手。
跑题了,赶紧回归正题。
二、原理
特性
贝塞尔曲线有如下特性:
- 通过\(n\)个控制点\(P_1, P_2, P_3, ..., P_n\)控制曲线形状。
- 曲线只经过起点\(P_1\)和终点\(P_n\).
构建贝塞尔曲线
- 一次曲线(线性):
一次贝塞尔曲线由两个端点\(P_0\)和\(P_1\)组成,曲线由线段直接连接这两个点,\(B(t)\)描述一条由\(P_0\)至\(P_1\)的直线,可以视为一个\(P_0\)至\(P_1\)的连续中间点\(Q_0\)。
- 二次曲线:
二次贝塞尔曲线由定点\(P_0, P_1, P_2\)的函数\(B(t)\)描述。为构建贝塞尔曲线,引入中间点\(Q_0, Q_1\):- \(P_0, P_1\)之间的连续点\(Q_0\),描述一条线性贝塞尔曲线;
- \(P_1, P_2\)之间的连续点\(Q_1\),描述一条线性贝塞尔曲线;
- \(Q_0, Q_1\)之间的连续点\(B(t)\),描述一条二次贝塞尔曲线。
- 三次曲线:
对于三次曲线,由定点\(P_0, P_1, P_2, P_3\)的函数\(B(t)\)描述。可以加入三个可由线性贝塞尔曲线描述的中间点\(Q_0, Q_1, Q_2\),和两个由二次贝塞尔曲线描述的点\(R_0, R_1\)来描述:- \(P_0, P_1\)之间的连续点\(Q_0\),描述一条线性贝塞尔曲线;
- \(P_1, P_2\)之间的连续点\(Q_1\),描述一条线性贝塞尔曲线;
- \(P_2, P_3\)之间的连续点\(Q_2\),描述一条线性贝塞尔曲线;
- \(Q_0, Q_1\)之间的连续点\(R_0\),描述一条二次贝塞尔曲线;
- \(Q_1, Q_2\)之间的连续点\(R_1\),描述一条二次贝塞尔曲线;
- \(R_0, R_1\)之间的连续点\(B(t)\),描述一条三次贝塞尔曲线。
- 四次曲线:
对于四次曲线,由定点\(P_0, P_1, P_2, P_3, P_4\)的函数\(B(t)\)描述。可以加入四个可由线性贝塞尔曲线描述的中间点\(Q_0, Q_1, Q_2, Q_3\),三个由二次贝塞尔曲线描述的点\(R_0, R_1, R_2\),和两个由三次贝塞尔曲线描述的点\(S_0, S_1\)来描述:- \(P_0, P_1\)之间的连续点\(Q_0\),描述一条线性贝塞尔曲线;
- \(P_1, P_2\)之间的连续点\(Q_1\),描述一条线性贝塞尔曲线;
- \(P_2, P_3\)之间的连续点\(Q_2\),描述一条线性贝塞尔曲线;
- \(P_3, P_4\)之间的连续点\(Q_3\),描述一条线性贝塞尔曲线;
- \(Q_0, Q_1\)之间的连续点\(R_0\),描述一条二次贝塞尔曲线;
- \(Q_1, Q_2\)之间的连续点\(R_1\),描述一条二次贝塞尔曲线;
- \(Q_2, Q_3\)之间的连续点\(R_2\),描述一条二次贝塞尔曲线;
- \(R_0, R_1\)之间的连续点\(S_0\),描述一条三次贝塞尔曲线;
- \(R_1, R_2\)之间的连续点\(S_1\),描述一条三次贝塞尔曲线;
- \(S_0, S_1\)之间的连续点\(B(t)\),描述一条四次贝塞尔曲线。
- 高阶曲线:
由上文可知,为构建更高阶的曲线,需要引入更多的中间点。同理可推得五次曲线:
构建贝塞尔曲线公式
- 一次曲线(线性):
设\(P_0\)的坐标为\((\alpha, \beta)\),\(P_1\)的坐标为\((\lambda, \mu)\),引入中间点\(Q_0\)的坐标为\((x, y)\),则有:
可推得:
同理,可得:
可推得:
整理可得:
可将式\((3)\)简写为:
得到一次贝塞尔曲线公式。
2. 二次曲线:
设\(P_0, P_1\)上的中间点为\(Q_0\),\(P_1, P_2\)上的中间点为\(Q_1\),\(Q_0, Q_1\)上的中间点为\(R_0\),\(R_0\)即构建曲线的连续点。
带入式\((4)\)可得:
将式\((5)\)中的\(Q_0\)和\(Q_1\)代入\(R_0\)中,可得二次贝塞尔曲线公式:
- 三次曲线:
同理,通过引入中间点\(Q_0, Q_1, Q_2, R_0, R_1, S_0\),代入使用式\((6)\),可得三次贝塞尔曲线公式:
- 高阶曲线:
可以迭代得到四次、五次贝塞尔曲线的公式:
三、推导
一般化
观察上文中\(1\)至\(5\)阶贝塞尔曲线,发现其各项中的常数系数满足组合数规律,故对于\(n\)阶贝塞尔曲线,给出定点\(P_0, P_1, P_2, ..., P_n\),其贝塞尔曲线可用下式表示:
可化简为:
式中组合数:
多项式表达
同时,式\((11)\)也可表示为:
其中多项式:
式\((13)\)被称作\(n\)阶Bernsteain多项式。
Bernsteain多项式:
假设开展一个实验,其成功的概率为\(u\)。若连续进行这个实验\(n\)次,成功的次数恰好为\(i\)次,那么其成功概率是多少呢?
能够确定成功概率为:\(b_{i, n}(t) = \left( \begin{matrix} n \\ i \end{matrix} \right) (1 - t) ^ {n - i} t ^ i, i = 0, 1, 2, \cdots, n\).
这便是Bernsteain多项式。
四、应用(Matlab实现)
实现目标
- 能够进行控制点坐标的输入。
- 计算贝塞尔曲线。
- 绘制贝塞尔曲线及其控制点。
脚本编写
main.m(主函数,调用输入函数、贝塞尔曲线计算函数及绘制贝塞尔曲线)
%% 初始化
clc
clear
%% 输入
n = input("请输入控制点个数:\n");
P = pos_input(n);
%% 调用贝塞尔曲线计算函数
P = P'; % 坐标矩阵转置,便于曲线运算及绘图
[t, p] = bezier_curve(P, n - 1); % 阶数为控制点个数 - 1
%% 绘制贝塞尔曲线
plot(t, p, '-b'); % 绘制贝塞尔曲线
hold on;
plot(P(1,:), P(2,:), '-ro'); % 绘制控制点
title(sprintf('贝塞尔曲线 (阶数: %d)', n - 1));
xlabel('X');
ylabel('Y');
grid on;
legend('贝塞尔曲线', '控制点');
hold off;
bezier_curve.m(贝塞尔曲线函数,计算n阶贝塞尔曲线)
function [Px, Py] = bezier_curve(control_points, order)
% 计算贝塞尔曲线上的点
t = linspace(0, 1, 1000); % 在[0, 1]范围内生成点
curve = zeros(2, numel(t)); % 初始化曲线矩阵,numel函数用于获取数组元素的数目
for i = 0 : order
curve = curve + nchoosek(order, i) * ((1 - t) .^ (order - i)) .* ...
(t .^ i) .* control_points(:, i + 1); % 该计算公式即上文中式(11),nchoosek函数用于计算组合数
end
Px = curve(1, :);
Py = curve(2, :);
end
pos_input.m(输入函数,输入控制点坐标)
function P = pos_input(I)
% 输入贝塞尔曲线控制点
P = ones(I, 2); % 初始化坐标矩阵
for i = 1 : I
P(i, :) = input("请输入输入坐标点P" + num2str(i - 1) + "(以一维矩阵形式输入): \n");
end
end
图像结果
六阶贝塞尔曲线:
十四阶贝塞尔曲线: