离散点曲线曲率计算方法大汇总

引言

在机器人、自动驾驶等领域中,规划与控制常常需要获取路径的曲率信息,而路径曲线的表示的方式一般为离散点的形式,这给曲率的求取带来一定难度。下面给出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()
  • 函数曲线图
    在这里插入图片描述- 曲线曲率图-差分法
    在这里插入图片描述

  • 曲线曲率图-参数方程法
    在这里插入图片描述

  • 曲线曲率图-三点画圆法
    在这里插入图片描述

  • 曲线曲率图-曲线拟合法
    在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值