如何在Matplotlib中实现沿轨迹方向等距偏移的平行复合线型
Matplotlib 任意轨迹等距平行双线实现方案
核心是通过轨迹法向计算+转角交点修正,实现全程等距、偏移方向始终沿轨迹右侧的平行线条,避免固定x/y偏移、默认path_effects静态偏移带来的转角间距不均、方向不匹配问题。
实现逻辑
- 逐段计算原始轨迹的切向向量,顺时针旋转90°得到轨迹前进方向的右侧法向向量,归一化后乘以设定偏移距离,得到每段线段的偏移基准
- 对相邻两段的偏移线段求交点,作为转角处的偏移顶点,替代原始点直接偏移的结果,解决转角处平行间距收窄的问题
- 所有点计算完成后得到和原始轨迹全程垂直等距的平行轨迹,可按需设置实线、虚线等样式
实现代码
import matplotlib.pyplot as plt import numpy as np def generate_right_offset_curve(x, y, offset_dist): """ 生成原始轨迹右侧固定垂直距离的等距平行轨迹 参数: x, y: 原始轨迹的坐标数组 offset_dist: 平行线与原轨迹的垂直距离,单位与轨迹坐标一致 返回: offset_x, offset_y: 等距平行轨迹的坐标数组 """ x = np.asarray(x, dtype=np.float64) y = np.asarray(y, dtype=np.float64) point_num = len(x) offset_x = np.zeros(point_num) offset_y = np.zeros(point_num) # 计算每段线段的切向与右侧法向 seg_dx = np.diff(x) seg_dy = np.diff(y) seg_len = np.hypot(seg_dx, seg_dy) # 切向(dx, dy)顺时针转90°得到右侧法向(dy, -dx),做归一化 norm_nx = seg_dy / seg_len norm_ny = -seg_dx / seg_len # 计算起点偏移位置 offset_x[0] = x[0] + norm_nx[0] * offset_dist offset_y[0] = y[0] + norm_ny[0] * offset_dist # 逐点计算中间转角的交点位置,保证转角等距 for i in range(1, point_num - 1): # 相邻两段各自的偏移线段端点 l1_p1 = (x[i-1] + norm_nx[i-1]*offset_dist, y[i-1] + norm_ny[i-1]*offset_dist) l1_p2 = (x[i] + norm_nx[i-1]*offset_dist, y[i] + norm_ny[i-1]*offset_dist) l2_p1 = (x[i] + norm_nx[i]*offset_dist, y[i] + norm_ny[i]*offset_dist) l2_p2 = (x[i+1] + norm_nx[i]*offset_dist, y[i+1] + norm_ny[i]*offset_dist) # 求解两线段交点作为转角偏移点 x1, y1 = l1_p1 x2, y2 = l1_p2 x3, y3 = l2_p1 x4, y4 = l2_p2 denom = (x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4) t = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3 - x4)) / denom offset_x[i] = x1 + t*(x2 - x1) offset_y[i] = y1 + t*(y2 - y1) # 计算终点偏移位置 offset_x[-1] = x[-1] + norm_nx[-1] * offset_dist offset_y[-1] = y[-1] + norm_ny[-1] * offset_dist return offset_x, offset_y # 用法示例 if __name__ == "__main__": fig, (ax1, ax2) = plt.subplots(1, 2, dpi=100, figsize=(10, 4)) # 测试1:带直角拐点的简单轨迹 X_simple = np.array([0, 1, 1, 0, 0.5]) Z_simple = np.array([1, 1, 0, 0, 0.5]) offset = 0.03 ox_simple, oz_simple = generate_right_offset_curve(X_simple, Z_simple, offset) ax1.plot(X_simple, Z_simple, 'r', linewidth=2) ax1.plot(ox_simple, oz_simple, 'r--', linewidth=2) ax1.set_title('带拐点简单轨迹测试') ax1.set_aspect('equal') # 必须开启等比例坐标轴,否则视觉间距会变形 # 测试2:复杂螺旋曲线轨迹 t = np.linspace(0, 4*np.pi, 200) X_complex = t * np.cos(t) * 0.2 Z_complex = t * np.sin(t) * 0.2 ox_complex, oz_complex = generate_right_offset_curve(X_complex, Z_complex, offset_dist=0.15) ax2.plot(X_complex, Z_complex, 'b', linewidth=2) ax2.plot(ox_complex, oz_complex, 'b--', linewidth=2) ax2.set_title('复杂曲线轨迹测试') ax2.set_aspect('equal') plt.tight_layout() plt.show()
使用注意事项
- 绘制时必须设置
ax.set_aspect('equal')保证x、y轴缩放比例一致,否则会出现法向计算偏差、视觉间距不等的问题 - 该方法对任意折线、采样密度足够的连续曲线都生效,平行间距全程恒定,转角无收窄问题,虚线始终位于轨迹前进方向的右侧
- 绘制闭合轨迹时,只需将轨迹首尾点设置为同一坐标,函数可自动处理闭合处的转角衔接
- 不需要依赖matplotlib的
path_effects静态偏移能力,偏移方向完全随轨迹走向动态计算
内容的提问来源于stack exchange,提问作者TinyTheBrontosaurus
相关产品推荐
相关产品推荐

