【问题标题】:Python - OpenDrive Map - Spiral / Clothoid / Euler Spiral / Cornu Spiral Interpolation using Fresnel IntegralsPython - OpenDrive 地图 - 使用菲涅耳积分的螺旋/回旋/欧拉螺旋/Cornu 螺旋插值
【发布时间】:2018-02-20 11:48:52
【问题描述】:

地图格式OpenDrive,提供(除其他外)道路的几何形状。道路的每一段都可以有不同的几何形状(例如线、弧线、螺旋线、多项式)。提供的道路几何“螺旋”信息如下:

 - s      - relative position of the road segment in respect to the beginning
                                                of the road (not used in here)
 - x      - the "x" position of the starting point of the road segment
 - y      - the "y" position of the starting point of the road segment
 - hdg       - the heading of the starting point of the road segment
 - length      - the length of the road segment
 - curvStart   - the curvature at the start of the road segment
 - curvEnd     - the curvature at the end of the road segment

我的目标是在给定“分辨率”参数的情况下沿螺旋线插入点(例如,分辨率 = 1,每米沿螺旋线插入一个点)。 螺旋几何是这样的,它引入了一个恒定的曲率变化(1/半径),因此它产生了从直线到圆弧的平滑和稳定的过渡,因此车辆上的横向加速度力小于从直线直接转为圆弧(直线曲率 = 0,圆弧曲率 = 常数)。

螺旋总是有一个曲率为 0 的端点(它连接到道路的线段),另一个为常数(例如,0.05 连接到弧线)。根据连接顺序,curvStart 可以等于 0 或常数,curvEnd 也可以等于 0 或常数。它们不能同时等于 0 或常数。

下面的代码是一个函数,它将前面讨论的参数(由格式给出)和分辨率作为参数。

目前,我遇到以下问题:

  • 插入相距 1 米的等距点(检查图 1)
  • 获取点的正确航向(检查图 2)
  • 找到最后 2 个案例的解决方案

从我对如何完成任务的研究中,我找到了一些有用的资源,但没有一个能帮助我获得最终的解决方案:

import numpy as np
from math import cos, sin, pi, radians
from scipy.special import fresnel
import matplotlib.pyplot as plt
%matplotlib inline

def spiralInterpolation(resolution, s, x, y, hdg, length, curvStart, curvEnd):
    points = np.zeros((int(length/resolution), 1))
    points = [i*resolution for i in range(len(points))]
    xx = np.zeros_like(points)
    yy = np.zeros_like(points)
    hh = np.zeros_like(points)
    if curvStart == 0 and curvEnd > 0:
        print("Case 1: curvStart == 0 and curvEnd > 0")
        radius = np.abs(1/curvEnd)
        A_sq = radius*length
        ss, cc = fresnel(np.square(points)/(2*A_sq*np.sqrt(np.pi/2)))
        xx = points*cc
        yy = points*ss
        hh = np.square(points)*2*radius*length
        xx, yy, hh = rotate(xx, yy, hh, hdg)
        xx, yy = translate(xx, yy, x, y)
        xx = np.insert(xx, 0, x, axis=0)
        yy = np.insert(yy, 0, y, axis=0)
        hh = np.insert(hh, 0, hdg, axis=0)

    elif curvStart == 0 and curvEnd < 0:
        print("Case 2: curvStart == 0 and curvEnd < 0")
        radius = np.abs(1/curvEnd)
        A_sq = radius*length
        ss, cc = fresnel(np.square(points)/(2*A_sq*np.sqrt(np.pi/2)))
        xx = points*cc
        yy = points*ss*-1
        hh = np.square(points)*2*radius*length
        xx, yy, hh = rotate(xx, yy, hh, hdg)
        xx, yy = translate(xx, yy, x, y)
        xx = np.insert(xx, 0, x, axis=0)
        yy = np.insert(yy, 0, y, axis=0)
        hh = np.insert(hh, 0, hdg, axis=0)

    elif curvEnd == 0 and curvStart > 0:
        print("Case 3: curvEnd == 0 and curvStart > 0")

    elif curvEnd == 0 and curvStart < 0:
        print("Case 4: curvEnd == 0 and curvStart < 0")

    else:
        print("The curvature parameters differ from the 4 predefined cases. "
              "Change curvStart and/or curvEnd")

    n_stations = int(length/resolution) + 1
    stations = np.zeros((n_stations, 3))
    for i in range(len(xx)):
        stations[i][0] = xx[i]
        stations[i][1] = yy[i]
        stations[i][2] = hh[i]

    return stations

def rotate(x, y, h, angle):
    # This function rotates the x and y vectors around zero
    xx = np.zeros_like(x)
    yy = np.zeros_like(y)
    hh = np.zeros_like(h)
    for i in range(len(x)):
        xx[i] = x[i]*cos(angle) - y[i]*sin(angle)
        yy[i] = x[i]*sin(angle) + y[i]*cos(angle)
        hh[i] = h[i] + angle
    return xx, yy, hh

def translate(x, y, x_delta, y_delta):
    # This function translates the x and y vectors with the delta values
    xx = np.zeros_like(x)
    yy = np.zeros_like(y)
    for i in range(len(x)):
        xx[i] = x[i] + x_delta
        yy[i] = y[i] + y_delta 
    return xx, yy

stations = spiralInterpolation(1, 77, 50, 100, radians(56), 40, 0, 1/20)

x = []
y = []
h = []

for station in stations:
    x.append(station[0])
    y.append(station[1])
    h.append(station[2])

plt.figure(figsize=(20,13))
plt.plot(x, y, '.')
plt.grid(True)
plt.axis('equal')
plt.show()

def get_heading_components(x, y, h, length=1):
    xa = np.zeros_like(x)
    ya = np.zeros_like(y)
    for i in range(len(x)):
        xa[i] = length*cos(h[i])
        ya[i] = length*sin(h[i])
    return xa, ya

xa, ya = get_heading_components(x, y, h)
plt.figure(figsize=(20,13))
plt.quiver(x, y, xa, ya, width=0.005)
plt.grid(True)
plt.axis('equal')
plt.show()

【问题讨论】:

  • 由于 StackOverflow 的格式规则,我无法正确缩进我的代码。请记住,“def rotate()”下面的所有内容都应该没有任何标识

标签: python scipy spiral fresnel


【解决方案1】:

我不确定您当前的代码是否正确。我写了一个简短的脚本来使用相似的参数插入欧拉螺旋,它给出了不同的结果:

import numpy as np
from math import cos, sin, pi, radians, sqrt
from scipy.special import fresnel
import matplotlib.pyplot as plt

def spiral_interp_centre(distance, x, y, hdg, length, curvEnd):
    '''Interpolate for a spiral centred on the origin'''
    # s doesn't seem to be needed...
    theta = hdg                    # Angle of the start of the curve
    Ltot = length                  # Length of curve
    Rend = 1 / curvEnd             # Radius of curvature at end of spiral

    # Rescale, compute and unscale
    a = 1 / sqrt(2 * Ltot * Rend)  # Scale factor
    distance_scaled = distance * a # Distance along normalised spiral
    deltay_scaled, deltax_scaled = fresnel(distance_scaled)
    deltax = deltax_scaled / a
    deltay = deltay_scaled / a

    # deltax and deltay give coordinates for theta=0
    deltax_rot = deltax * cos(theta) - deltay * sin(theta)
    deltay_rot = deltax * sin(theta) + deltay * cos(theta)

    # Spiral is relative to the starting coordinates
    xcoord = x + deltax_rot
    ycoord = y + deltay_rot

    return xcoord, ycoord

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)

# This version
xs = []
ys = []
for n in range(-100, 100+1):
    x, y = spiral_interp_centre(n, 50, 100, radians(56), 40, 1/20.)
    xs.append(x)
    ys.append(y)
ax.plot(xs, ys)

# Your version
from yourspiral import spiralInterpolation
stations = spiralInterpolation(1, 77, 50, 100, radians(56), 40, 0, 1/20.)
ax.plot(stations[:,0], stations[:,1])

ax.legend(['My spiral', 'Your spiral'])
fig.savefig('spiral.png')
plt.show()

这样我就明白了

那么哪个是正确的?

同样,如果曲率在结尾处为零而在开始处不为零,hdg 代表什么?是曲线起点还是终点的角度?您的函数还接受一个未使用的参数s。它应该是相关的吗?

如果您的示例代码显示了螺旋线段之前和之后的线段图,则更容易看出哪个是正确的并了解每个参数的含义。

【讨论】:

  • 您好 Socar,谢谢您的回答。我将测试您提供的解决方案并回复您。从第一眼看,看到以下内容: - 是的,这个函数中没有使用“s”值。我保留它是因为我想保持通用性-您的计算不考虑 4 个“如果”情况(查看我的代码)-不计算标题(这是必需的)。航向表示车辆在道路几何形状上的每个点所面对的方向 - 请记住,我的第一个案例是正确实施的(不包括航向,我不能 100% 确定)
  • 您好,Oscar,现在我可以确认我在上面制作的 cmets,因此,我无法接受您的回答。您的解决方案是对我发布的代码稍作修改,但它不能解决我概述的任何问题。我希望您可以实现上述所有内容,并且我很乐意接受您的代码作为正确答案。但是,请使用我定义的变量名称,并确保输出是一个名为“stations”的数组。当您可以简单地采用我提供的名称时,我不想花时间更改变量名称。
  • 首先,我并没有真正从我只是为你做这件事的角度来处理这个问题。其次,您还没有真正回答我的问题:哪个是正确的?您说到目前为止您的代码是正确的,但我确信它不是 - 如果我们不同意这一点,我将无能为力。
  • 我对“透视”的讨论不感兴趣,所以我会尝试更好地回答您的问题:我认为我的计算正确的原因如下 - 地图是由软件自动创建的将车辆的 gps 位置作为输入,并将这些几何图形拟合到点。当我在第一个“if”循环中插入点时,我的结果与其他几何图形(例如线、弧)的开始和结束位置一致。我对菲涅尔的实现没有完全了解,所以如果你能说出原因,我可以假设你是对的?
  • 嗨,奥斯卡,我按照你的建议做了。你是对的 - 你的实现是正确的。感谢您解决这个问题!
【解决方案2】:

只是发布对奥斯卡答案的更正。有两个错误的部分:

  • 比例因子应为a = 1/sqrt(math.pi * arcLength * Radius),因为scipy.special.fresnel 使用cos(pi*t*t/2)sin(pi*t*t/2)。因此,曲率变为pi*s 而不是s 其中s 是弧长(Wikipedia)。
  • 我删除了spiral_interp_centrelength 参数,因为缩放(在下面的代码cmets 中解释)必须使用所需的弧长。

缩放误差不影响从spiral_interp_centre得到的弧长,但会影响得到曲率的精度。 要验证,请将下面代码中的 scalarmath.pi 更改为 2(奥斯卡答案中使用的值)。弧长(打印在下方)保持不变,但曲率会发生变化(与所需值不同)。

import math
import scipy.special
import matplotlib.pyplot as plt
import numpy as np

def arcLength(XY):
    return np.sum(np.hypot(np.diff(XY[:, 0]), np.diff(XY[:, 1])))

def getAreaOfTriangle(XY, i, j, k):
    xa, ya = XY[i, 0], XY[i, 1]
    xb, yb = XY[j, 0], XY[j, 1]
    xc, yc = XY[k, 0], XY[k, 1]
    return abs((xa * (yb - yc) + xb * (yc - ya) + xc * (ya - yb)) / 2)

def distance(XY, i, j):
    return np.linalg.norm(XY[i, :] - XY[j, :])

def getCurvatureUsingTriangle(XY, i, j, k):
    fAreaOfTriangle = getAreaOfTriangle(XY, i, j, k)
    AB = distance(XY, i, j)
    BC = distance(XY, j, k)
    CA = distance(XY, k, i)
    fKappa = 4 * fAreaOfTriangle / (AB * BC * CA)
    return fKappa

def spiral_interp_centre(arcLength, x_i, y_i, yaw_i, curvEnd, N=300):
    '''
    :param arcLength: Desired length of the spiral
    :param x_i: x-coordinate of initial point
    :param y_i: y-coordinate of initial point
    :param yaw_i: Initial yaw angle in radians
    :param curvEnd: Curvature at the end of the curve.
    :return:
    '''
    # Curvature along the Euler spiral is pi*t where t is the Fresnel integral limit.
    # curvEnd = 1/R
    # s = arcLength
    # t = Fresnel integral limit
    # Scalar a is used to find t such that (1/(a*R) = pi*t) and (a*s = t)
    # ====> 1/(pi*a*R) = a*s
    # ====> a^a*(pi*s*R)
    # ====> a = 1/sqrt(pi*s*R)
    # To achieve a specific curvature at a specific arc length, we must scale
    # the Fresnel integration limit
    scalar = math.pi
    distances = np.linspace(start=0.0, stop=arcLength, num=N)
    R = 1 / curvEnd  # Radius of curvature at end of spiral
    # Rescale, compute and unscale
    a = 1 / math.sqrt(scalar * arcLength * R) # Scale factor
    scaled_distances = a * distances # Distance along normalized spiral
    dy_scaled, dx_scaled = scipy.special.fresnel(scaled_distances)

    dx = dx_scaled / a
    dy = dy_scaled / a

    # Rotate the whole curve by yaw_i
    dx_rot = dx * math.cos(yaw_i) - dy * math.sin(yaw_i)
    dy_rot = dx * math.sin(yaw_i) + dy * math.cos(yaw_i)

    # Translate to (x_i, y_i)
    x = x_i + dx_rot
    y = y_i + dy_rot
    return np.concatenate((x[:, np.newaxis], y[:, np.newaxis]), axis=1)

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.set_aspect('equal')
R = 20.0
for d in range(400, 600, 20):
    XY = spiral_interp_centre(d, 50, 100, math.radians(56), 1/R, N=300)
    ax.plot(XY[:, 0], XY[:, 1])
    print('d={:.3f}, dd={:.3f}, R={:.3f}, RR={:.3f}'.format(d, arcLength(XY), R, 1/getCurvatureUsingTriangle(XY, 299, 298, 297)))
plt.show()

【讨论】:

    猜你喜欢
    • 2010-11-21
    • 2023-03-28
    • 2022-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多