引言
在机器人、自动驾驶等领域中,规划与控制常常需要获取路径的曲率信息,而路径曲线的表示的方式一般为离散点的形式,这给曲率的求取带来一定难度。下面给出4种根据离散点曲线求曲率的方法。
方法
差分法
# 差分法
def calculate_curvature_difference(data, interval=1):
# interval代表计算曲率时选取点的间隔,例如当需要计算data[i]的曲率时,选取的点为
# data[i-2*interval],data[i-interval],data[i],data[i+interval],data[i+2*interval]这5个点来计算
# 通过此操作可以缓解离散点不平滑的情况,后面的函数同理
curvature_list = []
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < 2 * interval:
curvature_list.append(0)
elif i > len(data) - 2*interval - 1:
curvature_list.append(0)
# 获取间隔为interval的5个点(二阶差分需要5个点)
else:
start, end = i - 2 * interval, i + 2 * interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
# 差分法计算曲率
# 一阶差分
dx = np.gradient(x)
dy = np.gradient(y)
# 二阶差分
d2x = np.gradient(dx)
d2y = np.gradient(dy)
# 计算曲率, 并处理分母为0的情况
denominator = (dx**2 + dy**2)**1.5
curvature = np.where(denominator == 0, 0, (dx * d2y - d2x * dy) / denominator)
# 只有中间点的曲率是准确计算的,所以只取中间点的曲率
curvature_list.append(curvature[2])
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:2*interval] = [curvature_list[2*interval]] * (2*interval)
curvature_list[-2*interval:] = [curvature_list[-2*interval-1]] * (2*interval)
return curvature_list
注意:
1.差分法需要二阶差分,所以计算一个点的较为精确曲率需要周围5个点的信息。
2.差分法对于噪声较大,不平滑的离散点的曲率计算效果较差。
3.当相邻点之间的距离较小时,或者点的噪声比较大时,可以间隔选取点来计算曲率,可以提高精度。间隔个数通过interval
参数设置。
三点参数方程法
# 三点参数方程法
def calculate_curvature_parameter(data,interval=1):
# interval代表计算曲率时选取点的间隔,例如当需要计算data[i]的曲率时,选取的点为
# data[i-interval],data[i],data[i+interval]这3个点来计算
# 通过此操作可以缓解离散点不平滑的情况,后面的函数同理
curvature_list=[]
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < interval:
curvature_list.append(0)
elif i > len(data) - interval - 1:
curvature_list.append(0)
# 获取间隔为interval的3个点(二阶差分需要3个点)
else:
start, end = i - interval, i + interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
t_a = norm([x[1]-x[0], y[1]-y[0]])
t_b = norm([x[2]-x[1], y[2]-y[1]])
M = np.array([
[1, -t_a, t_a**2],
[1, 0, 0 ],
[1, t_b, t_b**2]
])
# Add a small regularization term to the diagonal to prevent singularity
M += np.eye(3) * 1e-6
a = np.matmul(inv(M), x)
b = np.matmul(inv(M), y)
curvature = 2*(a[2]*b[1]-b[2]*a[1])/(a[1]**2.+b[1]**2.)**(1.5)
curvature_list.append(curvature)
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:interval] = [curvature_list[interval]] * (interval)
curvature_list[-interval:] = [curvature_list[-interval-1]] * (interval)
return curvature_list
注意:
1.三点参数方程法需要周围3个点的信息。
2.三点参数方程法对于噪声较大,不平滑的离散点的曲率计算效果较差。
3.当相邻点之间的距离较小时,或者点的噪声比较大时,可以间隔选取点来计算曲率,可以提高精度。间隔个数通过interval
参数设置。
三点画圆法
# 三点画圆法
def calculate_curvature_circle(data,interval=1):
# interval代表计算曲率时选取点的间隔,例如当需要计算data[i]的曲率时,选取的点为
# data[i-interval],data[i],data[i+interval]这3个点来计算
# 通过此操作可以缓解离散点不平滑的情况,后面的函数同理
curvature_list=[]
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < interval:
curvature_list.append(0)
elif i > len(data) - interval - 1:
curvature_list.append(0)
# 获取间隔为interval的3个点(二阶差分需要3个点)
else:
start, end = i - interval, i + interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
# 计算圆心
x1,x2,x3=x
y1,y2,y3=y
denominator = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))
x_center = ((x1**2 + y1**2) * (y2 - y3) + (x2**2 + y2**2) * (y3 - y1) + (x3**2 + y3**2) * (y1 - y2)) / denominator
y_center = ((x1**2 + y1**2) * (x3 - x2) + (x2**2 + y2**2) * (x1 - x3) + (x3**2 + y3**2) * (x2 - x1)) / denominator
# 计算半径
r = np.sqrt((x1 - x_center)**2 + (y1 - y_center)**2)
# 计算曲率
curvature = 1 / r
# 判断曲率的正负
vector1 = (x2 - x1, y2 - y1)
vector2 = (x3 - x2, y3 - y2)
cross_product = vector1[0] * vector2[1] - vector1[1] * vector2[0]
if cross_product > 0:
curvature *= 1 # 曲率为负
else:
curvature *= -1 # 曲率为正
curvature_list.append(curvature)
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:interval] = [curvature_list[interval]] * (interval)
curvature_list[-interval:] = [curvature_list[-interval-1]] * (interval)
return curvature_list
注意:
1.三点画圆法需要周围3个点的信息。
2.三点画圆法对于噪声较大,不平滑的离散点的曲率计算效果较差。
3.当相邻点之间的距离较小时,或者点的噪声比较大时,可以间隔选取点来计算曲率,可以提高精度。间隔个数通过interval
参数设置。
曲线拟合法
# 曲线拟合法
def Calculate_curvature_spline(data):
x=data[:,0]
y=data[:,1]
# Cubic Spline 拟合
cs = CubicSpline(x, y)
# 求导
y_prime = cs(x, 1)
y_double_prime = cs(x, 2)
# 计算曲率
curvature_list = (y_double_prime) / np.sqrt(1 + y_prime**2)**3
return curvature_list
注意:
1.本方法采用了三次样条曲线进行拟合,也可以采用其他方式进行拟合。
2.该方法对于离散点存在误差、不平滑的情况时具有很好的效果,相对于其他方法,该方法的综合效果最好。
3.该方法存在一个致命的弱点,当离散点的x坐标非单调时则无法采用此方法,因为函数要求x必须是单调的,否则不能成为函数。
完整代码与测试结果
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import CubicSpline
from numpy.linalg import norm, inv
# 差分法
def calculate_curvature_difference(data, interval=1):
curvature_list = []
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < 2 * interval:
curvature_list.append(0)
elif i > len(data) - 2*interval - 1:
curvature_list.append(0)
# 获取间隔为interval的5个点(二阶差分需要5个点)
else:
start, end = i - 2 * interval, i + 2 * interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
# 差分法计算曲率
# 一阶差分
dx = np.gradient(x)
dy = np.gradient(y)
# 二阶差分
d2x = np.gradient(dx)
d2y = np.gradient(dy)
# 计算曲率, 并处理分母为0的情况
denominator = (dx**2 + dy**2)**1.5
curvature = np.where(denominator == 0, 0, (dx * d2y - d2x * dy) / denominator)
# 只有中间点的曲率是准确计算的,所以只取中间点的曲率
curvature_list.append(curvature[2])
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:2*interval] = [curvature_list[2*interval]] * (2*interval)
curvature_list[-2*interval:] = [curvature_list[-2*interval-1]] * (2*interval)
return curvature_list
# 三点参数方程法
def calculate_curvature_parameter(data,interval=1):
curvature_list=[]
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < interval:
curvature_list.append(0)
elif i > len(data) - interval - 1:
curvature_list.append(0)
# 获取间隔为interval的3个点(二阶差分需要3个点)
else:
start, end = i - interval, i + interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
t_a = norm([x[1]-x[0], y[1]-y[0]])
t_b = norm([x[2]-x[1], y[2]-y[1]])
M = np.array([
[1, -t_a, t_a**2],
[1, 0, 0 ],
[1, t_b, t_b**2]
])
# Add a small regularization term to the diagonal to prevent singularity
M += np.eye(3) * 1e-6
a = np.matmul(inv(M), x)
b = np.matmul(inv(M), y)
curvature = 2*(a[2]*b[1]-b[2]*a[1])/(a[1]**2.+b[1]**2.)**(1.5)
curvature_list.append(curvature)
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:interval] = [curvature_list[interval]] * (interval)
curvature_list[-interval:] = [curvature_list[-interval-1]] * (interval)
return curvature_list
# 三点画圆法
def calculate_curvature_circle(data,interval=1):
curvature_list=[]
for i in range(len(data)):
# 初始化头尾的曲率均为0
if i < interval:
curvature_list.append(0)
elif i > len(data) - interval - 1:
curvature_list.append(0)
# 获取间隔为interval的3个点(二阶差分需要3个点)
else:
start, end = i - interval, i + interval + 1
x, y = data[start:end:interval, 0], data[start:end:interval, 1]
# 计算圆心
x1,x2,x3=x
y1,y2,y3=y
denominator = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))
x_center = ((x1**2 + y1**2) * (y2 - y3) + (x2**2 + y2**2) * (y3 - y1) + (x3**2 + y3**2) * (y1 - y2)) / denominator
y_center = ((x1**2 + y1**2) * (x3 - x2) + (x2**2 + y2**2) * (x1 - x3) + (x3**2 + y3**2) * (x2 - x1)) / denominator
# 计算半径
r = np.sqrt((x1 - x_center)**2 + (y1 - y_center)**2)
# 计算曲率
curvature = 1 / r
# 判断曲率的正负
vector1 = (x2 - x1, y2 - y1)
vector2 = (x3 - x2, y3 - y2)
cross_product = vector1[0] * vector2[1] - vector1[1] * vector2[0]
if cross_product > 0:
curvature *= 1 # 曲率为负
else:
curvature *= -1 # 曲率为正
curvature_list.append(curvature)
# 将头尾数据的曲率设置为最近可计算点的曲率
curvature_list[0:interval] = [curvature_list[interval]] * (interval)
curvature_list[-interval:] = [curvature_list[-interval-1]] * (interval)
return curvature_list
# 曲线拟合法
def Calculate_curvature_spline(data):
x=data[:,0]
y=data[:,1]
# Cubic Spline 拟合
cs = CubicSpline(x, y)
# 求导
y_prime = cs(x, 1)
y_double_prime = cs(x, 2)
# 计算曲率
curvature_list = (y_double_prime) / np.sqrt(1 + y_prime**2)**3
return curvature_list
x=np.linspace(0,2*np.pi,10000)
y=np.sin(x)
data=np.array([x,y]).T
#curvature=calculate_curvature_difference(data,1)
#curvature=calculate_curvature_parameter(data,4)# 这块需要设置为4效果才好,因为点的间距很小
#curvature=calculate_curvature_circle(data,1)
curvature=Calculate_curvature_spline(data)
plt.figure(1)
plt.plot(x,y)
plt.show()
plt.figure(2)
plt.plot(curvature)
plt.show()
-
函数曲线图
- 曲线曲率图-差分法
-
曲线曲率图-参数方程法
-
曲线曲率图-三点画圆法
-
曲线曲率图-曲线拟合法