参考别人的博客学习
根据之前一章只是大致了解了B样条数学原理,实际读代码还有疑惑。
控制点是什么?和规划出的路径点什么关系?
控制点可以说我们规划出的路径点,即n等于轨迹点个数。也可以不是轨迹点,通过线性方程反解出控制点
B样条曲线是少C0,即至少是连续的,但不能保证C1(即可导,无折现)甚至更高C2.。。
p3,p4为 规划出的点,若直接当作控制点生成B样条,则因为点的位置注定P3处不可导。若要可导则必定要移动P4到上面,那怎么求出P4的位置呢? 求出样条基函数后线性方程反解出控制点即可得到平滑的P4。
即C1连续以上控制点只能生成。
规划出的路径大多是开B样条曲线(Open Curves),即首尾没用有连接一起。所以U的范围由之前的[U0,Um],变为 [Up,Um-p].
代码初始化有些细节
这里直接粘贴大佬解释后的代码。
这里明确一下流程:调用evaluateDeBoorT函数,将参数t转换为u,后调用evaluateDeBoor计算出变量u,然后就是evaluateDeBoor里将基函数和控制点累加相乘,返回的是曲线上的点,即我们想要拟合完后的某i个路径点
注意:B样条最后出来的拟合函数参数是t,De Boor算法来计算B样条基函数的参数是u,所以要把u转换成t。转换方法很简单就是线性转换。
通俗讲: 3级样条基函数Nm-3,3,需要4个0级样条计算得来,对应范围如图[Um-3,Um+1)四个定义域的连接,通过转换定义域对应t的[0,4)
U=t_cur + i * dt+u_(p_)
pos = traj[0].evaluateDeBoorT(t_cur + i * dt); //这里调用evaluateDeBoorT 把t转成u
Eigen::VectorXd NonUniformBspline::evaluateDeBoorT(const double& t) {
return evaluateDeBoor(t + u_(p_));
}
/*De Boor递归算法,计算传入参数u处的B样条曲线值,返回B-样条曲线在参数 u 处的B样条曲线函数*/
Eigen::VectorXd NonUniformBspline::evaluateDeBoor(const double& u) {
//限制参数 u 的范围: 将参数 u 限制在 B-样条曲线有效范围内,即 [u_(p_), u_(m_ - p_)]。
double ub = min(max(u_(p_), u), u_(m_ - p_)); //体现出是open B样条
// determine which [ui,ui+1] lay in
//找到输入的u是第几个节点处
int k = p_;
while (true) {
if (u_(k + 1) >= ub) break; //因为下限是u_(p_)所以从k开始,又因为u是非递减的所以每次检查k+1即可
++k;
}
//通过k找到与当前节点相关的控制点k-p_到k
/* deBoor's alg */
vector<Eigen::VectorXd> d;
for (int i = 0; i <= p_; ++i) {
d.push_back(control_points_.row(k - p_ + i));
// cout << d[i].transpose() << endl;
}
//De Boor递归计算控制点,计算过程中使用了混合参数 alpha,该参数用于混合现有控制点以生成新的控制点。
//数学推导https://en.wikipedia.org/wiki/De_Boor%27s_algorithm
for (int r = 1; r <= p_; ++r) {//外循环次数
for (int i = p_; i >= r; --i) {//内循环控制节点,次数高一次,控制节点少一个(自己写以下就能理解)
double alpha = (ub - u_[i + k - p_]) / (u_[i + 1 + k - r] - u_[i + k - p_]);
// cout << "alpha: " << alpha << endl;
d[i] = (1 - alpha) * d[i - 1] + alpha * d[i]; // 累加在这里实现
}
}
//返回计算得到的 B-样条曲线在参数 u 处的点
return d[p_];
}
下面是写代码时候遇见的问题:
- 数组u的含义是什么? 不同取值的影响?
u就是自变量,求完B样条后得到的是含u的分段函数,我们要画出这个拟合的曲线就要在定义域内带入函数,画出来
U的范围即定义域范围,应在节点向量最大和最小内(否则就会有0,0点出现)
U取的过于稀疏发现拟合的线多折线不平滑;U过于密集发现拟合曲线就经过了几个控制点,大部分控制点没拟合
GPT给我答案:应该是太小时有些基函数十分接近于0,计算机直接近似0处理了。
,
if flag == 1: # 均匀B样条很简单
NodeVector = np.array([np.linspace(0, 1, n + k + 1)]) # 均匀B样条节点向量,首末值定义为 0 和 1
#节点向量 前K个和后K个重复 否则会生成(0,0)的点
NodeVector[0, :k] = 0 # 重复开头的节点
NodeVector[0, n+1:n+k+1] = 1 # 重复结尾的节点
print(NodeVector)
u_min = NodeVector[0, k] # 最小的有效 u 值
u_max = NodeVector[0, n] # 最大的有效 u 值
print(u_min, u_max)
# 设置合适的 u 取值范围和步长
u_values = np.linspace(u_min, u_max, num=200) # 200 可以调整,覆盖整个 u 的范围
for u in u_values:
print(u)
for i in range(n+1):
Bik_u[i, 0] = BaseFunction(i, k, u, NodeVector)
p_u = P.T @ Bik_u
path.append(p_u)
2.网上代码u范围for u in np.arange((k-1) / (n + k + 1), (n + 2) / (n + k + 1),0.005): 表示从k-1个开始,取到n+1,因为范围【0,1】所以除了 (n + k + 1),但实际使用发现 生成曲线首尾要短不是标准开曲线,于是改成上面u_min和max形式,发现尾处短了一截,还是不知道为啥。。。希望大佬解答吧