You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何在Python中为3D数据点拟合二次曲面

刚好之前做过类似的二次曲面拟合任务,来给你拆解下怎么实现这个vertical_distance函数,顺便结合你的场景说清楚逻辑~

先明确二次曲面的通用形式

不管你最终确定哪种二次曲面,通用的表达式可以写成:

$F(x,y,z) = ax² + by² + cz² + dxy + exz + fyz + gx + hy + iz + j = 0$
这里的[a,b,c,d,e,f,g,h,i,j]就是我们要拟合的核心参数,后续的距离计算都基于这个形式。

垂直距离的核心逻辑

点$p_0=(x0,y0,z0)$到曲面的垂直距离,本质是找曲面上的点$p=(x,y,z)$,满足两个关键条件:

  • $p$严格在曲面上:$F(x,y,z)=0$
  • 向量$\overrightarrow{p_0p}$与曲面在$p$处的法向量平行(因为垂直于曲面的方向就是法向量方向)

曲面在$p$处的法向量是$F$的梯度:
$\nabla F = (2ax + dy + ez + g, 2by + dx + fz + h, 2cz + ex + fy + i)$

因为$\overrightarrow{p_0p}$和法向量平行,我们可以引入参数$t$,令:
$x = x0 + t \cdot n_x$
$y = y0 + t \cdot n_y$
$z = z0 + t \cdot n_z$
其中$n=(n_x,n_y,n_z)$是$p$处的法向量。把这三个式子代入曲面方程,解出$t$后,对应的$p$就是曲面上离$p0$最近的垂直点,两点的欧氏距离就是我们要的垂直距离。

Python实现代码

因为代入后得到的是关于$t$的高次方程,没有通用解析解,所以用数值方法求解最方便。下面是完整的实现示例:

首先导入依赖库:

import numpy as np
from scipy.optimize import fsolve

定义二次曲面的方程函数:

def surface_equation(params, x, y, z):
    """计算二次曲面方程的值,params是10个系数的列表"""
    a, b, c, d, e, f, g, h, i, j = params
    return a * x**2 + b * y**2 + c * z**2 + d * x * y + e * x * z + f * y * z + g * x + h * y + i * z + j

定义曲面的梯度(法向量)函数:

def surface_gradient(params, x, y, z):
    """计算曲面在(x,y,z)处的法向量"""
    a, b, c, d, e, f, g, h, i, _ = params
    dx = 2 * a * x + d * y + e * z + g
    dy = 2 * b * y + d * x + f * z + h
    dz = 2 * c * z + e * x + f * y + i
    return np.array([dx, dy, dz])

核心的vertical_distance函数实现:

def vertical_distance(p0, params):
    """
    计算点p0到二次曲面的垂直距离,以及对应的曲面上的点
    :param p0: 三维点,格式为(x0, y0, z0)
    :param params: 二次曲面的10个系数列表
    :return: (垂直距离, 曲面上的对应点)
    """
    x0, y0, z0 = p0

    # 定义关于t的隐式方程:代入曲面方程后等于0
    def solve_for_t(t):
        # 先计算当前t对应的曲面上的点的坐标
        x_candidate = x0 + t * surface_gradient(params, x0 + t, y0 + t, z0 + t)[0]
        y_candidate = y0 + t * surface_gradient(params, x0 + t, y0 + t, z0 + t)[1]
        z_candidate = z0 + t * surface_gradient(params, x0 + t, y0 + t, z0 + t)[2]
        # 返回曲面方程的值,目标是让它等于0
        return surface_equation(params, x_candidate, y_candidate, z_candidate)

    # 用t=0作为初始猜测(也就是p0本身),解方程得到t
    t_opt = fsolve(solve_for_t, 0.0)[0]

    # 计算最终的曲面上的点
    x_surf = x0 + t_opt * surface_gradient(params, x0 + t_opt, y0 + t_opt, z0 + t_opt)[0]
    y_surf = y0 + t_opt * surface_gradient(params, x0 + t_opt, y0 + t_opt, z0 + t_opt)[1]
    z_surf = z0 + t_opt * surface_gradient(params, x0 + t_opt, y0 + t_opt, z0 + t_opt)[2]

    # 计算欧氏距离
    dist = np.sqrt((x0 - x_surf)**2 + (y0 - y_surf)**2 + (z0 - z_surf)**2)
    return dist, (x_surf, y_surf, z_surf)
额外说明
  • 如果之后要做曲面拟合,你可以把所有数据点的垂直距离平方和作为损失函数,用scipy.optimize.minimize来优化参数params
  • 如果你的曲面形式可以简化(比如旋转抛物面、椭球面等),可以减少参数数量,这样解方程和拟合都会更高效
  • 关于误差假设:如果假设数据点有高斯误差,用最小二乘(距离平方和)是合理的;如果是鲁棒性需求,可以用绝对值和作为损失

内容的提问来源于stack exchange,提问作者Annika

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:04:06