将二维点集分离为两条样条曲线以实现拐点检测
解决方案:分离双样条曲线、插值及拐点检测
1. 点集分离(基于K-Means聚类)
针对两条间距不固定的曲线,用K-Means指定2个聚类簇,可自动将点划分到两组,不受间距变化影响。
import numpy as np from sklearn.cluster import KMeans import matplotlib.pyplot as plt # 假设points_on_edges为你的二维点数组(shape: (N,2)) kmeans = KMeans(n_clusters=2, random_state=42, n_init='auto') labels = kmeans.fit_predict(points_on_edges) # 分离两组点 group1 = points_on_edges[labels == 0] group2 = points_on_edges[labels == 1] # 可视化验证分离效果 plt.scatter(group1[:,0], group1[:,1], label='曲线1', c='red') plt.scatter(group2[:,0], group2[:,1], label='曲线2', c='blue') plt.xlabel('y-Koordinate') plt.ylabel('z-Koordinate') plt.legend() plt.grid(True) plt.show() # 区分上下侧曲线(取z均值更小的为下侧) lower_group = group1 if np.mean(group1[:,1]) < np.mean(group2[:,1]) else group2 upper_group = group2 if lower_group is group1 else group1
2. 样条插值(平滑样条)
使用UnivariateSpline生成平滑样条曲线,支持后续求导操作,更适合拐点检测:
from scipy.interpolate import UnivariateSpline # 下侧曲线插值(先按y坐标排序,插值要求x单调) lower_group_sorted = lower_group[np.argsort(lower_group[:,0])] y_lower, z_lower = lower_group_sorted[:,0], lower_group_sorted[:,1] # s为平滑参数,s=0严格过所有点,s越大曲线越平滑,可按需调整 spline_lower = UnivariateSpline(y_lower, z_lower, s=5) # 上侧曲线同理 upper_group_sorted = upper_group[np.argsort(upper_group[:,0])] y_upper, z_upper = upper_group_sorted[:,0], upper_group_sorted[:,1] spline_upper = UnivariateSpline(y_upper, z_upper, s=5) # 生成高密度点可视化 y_new = np.linspace(min(y_lower.min(), y_upper.min()), max(y_lower.max(), y_upper.max()), 500) z_new_lower = spline_lower(y_new) z_new_upper = spline_upper(y_new) plt.scatter(lower_group[:,0], lower_group[:,1], label='下侧原始点', c='orange') plt.plot(y_new, z_new_lower, label='下侧样条曲线', c='darkorange') plt.scatter(upper_group[:,0], upper_group[:,1], label='上侧原始点', c='cyan') plt.plot(y_new, z_new_upper, label='上侧样条曲线', c='darkcyan') plt.xlabel('y-Koordinate') plt.ylabel('z-Koordinate') plt.legend() plt.grid(True) plt.show()
3. 拐点检测(基于二阶导数)
拐点对应曲线二阶导数为0且符号改变,通过样条求导找符合条件的点:
# 求下侧样条的二阶导数 spline_lower_second_deriv = spline_lower.derivative(n=2) # 获取二阶导数的零点(拐点候选) inflection_candidates_y = spline_lower_second_deriv.roots() # 验证零点附近二阶导数符号变化,排除非拐点的极值点 valid_inflection_points = [] for y in inflection_candidates_y: y_left = y - 1e-4 y_right = y + 1e-4 if y_left >= y_lower.min() and y_right <= y_lower.max(): deriv_left = spline_lower_second_deriv(y_left) deriv_right = spline_lower_second_deriv(y_right) if np.sign(deriv_left) != np.sign(deriv_right): valid_inflection_points.append(y) # 转换为(y,z)坐标 inflection_points = np.array([[y, spline_lower(y)] for y in valid_inflection_points]) # 可视化拐点 plt.plot(y_new, z_new_lower, label='下侧样条曲线', c='darkorange') plt.scatter(inflection_points[:,0], inflection_points[:,1], label='拐点', c='red', s=100, marker='x') plt.xlabel('y-Koordinate') plt.ylabel('z-Koordinate') plt.legend() plt.grid(True) plt.show() # 输出拐点坐标 print("下侧曲线拐点坐标:") print(inflection_points)
关键调整说明
- 若K-Means聚类效果不佳,可替换为DBSCAN密度聚类,只需调整
eps和min_samples参数适配点集密度。 - 样条平滑参数
s需根据数据噪声程度调整,噪声大时增大s,需保留细节时设s=0。 - 拐点检测的微小偏移量
1e-4可根据坐标尺度调整,避免边界计算误差。
内容的提问来源于stack exchange,提问作者Fabian
相关产品推荐
相关产品推荐

