
本文详解如何利用 sympy 正确建模椭球与平面的交集,推导所得椭圆在三维空间中的几何参数,并重点求解其主轴方向(即特征向量),纠正常见符号代换与方程求解误区。
本文详解如何利用 sympy 正确建模椭球与平面的交集,推导所得椭圆在三维空间中的几何参数,并重点求解其主轴方向(即特征向量),纠正常见符号代换与方程求解误区。
在三维几何分析中,椭球与平面相交通常生成一个椭圆(退化情形除外)。若已知椭球的中心、特征值(半轴平方)和标准正交特征向量(即旋转矩阵),目标是精确获取该交线椭圆在原始坐标系下的主轴方向向量——这并非简单投影,而是需从交线二次型中提取二维椭圆的协方差结构并进行本征分解。
关键问题在于:原代码试图用 solve(eq, (x_coor, y_coor)) 求解含三个符号变量的单一方程,且错误地将 intersection_eq 未参与求解;同时,x_coor, y_coor, z_coor 是 coords 的线性组合,本身不是独立变量,不能直接作为求解目标。正确路径是:
- 构建旋转后的椭球隐式方程(以新坐标 x', y', z' 表达);
- 代入平面约束(如 z = 78),得到关于 x', y' 的二维二次方程;
- 将该方程映射回原始坐标系,提取其对应的对称矩阵;
- 对该二维二次型矩阵执行本征分解,获得椭圆主轴方向(单位向量)及其长度信息。
以下为完整可运行的 SymPy 教程实现:
import numpy as np
from sympy import symbols, Matrix, simplify, sqrt, eye
from sympy.matrices import MatrixBase
# 输入参数(全部转为 SymPy 兼容格式)
centroid = Matrix([313.81153387, 252.73655237, 78.81324428])
eigenvectors = Matrix([[-0.17245704, 0.75883261, 0.62803792],
[-0.82066049, -0.46331271, 0.33445133],
[ 0.54477053, -0.45772742, 0.70264548]])
eigenvalues = Matrix([4.03632232, 3.80325721, 7.21909427])
# 定义局部坐标系符号
x_p, y_p, z_p = symbols('x_p y_p z_p')
coords_p = Matrix([x_p, y_p, z_p])
# 旋转坐标变换:原始坐标 r = Q @ r' + c,故 r' = Q.T @ (r - c)
# 这里我们采用“椭球在自身坐标系中为轴对齐”的标准形式:
# (r')^T @ diag(1/λ_i) @ r' = 1,其中 r' = Q.T @ (r - c)
Q = eigenvectors # 列向量为特征向量 → Q 是从原始到主轴的正交变换矩阵
Lambda_inv = Matrix.diag(*[1/eigenvalues[i] for i in range(3)])
# 构建隐式方程:(r - c)^T @ Q @ Lambda_inv @ Q.T @ (r - c) = 1
r = Matrix(symbols('x y z')) # 原始坐标变量
r_centered = r - centroid
quadratic_form = r_centered.T * Q * Lambda_inv * Q.T * r_centered - 1
# 代入平面约束:z = 78
plane_eq = {r[2]: 78}
ellip_2d_expr = quadratic_form.subs(plane_eq)[0] # 得到关于 x, y 的二次多项式
# 提取二次型系数矩阵 A(2×2),满足 [x,y]·A·[x,y]^T + b·[x,y] + c = 0
# 使用 collect 并匹配系数(更鲁棒于符号展开)
x, y = r[0], r[1]
coeffs = ellip_2d_expr.expand().as_coefficients_dict()
A11 = coeffs.get(x**2, 0)
A22 = coeffs.get(y**2, 0)
A12 = coeffs.get(x*y, 0) / 2 # 因为 x*y 项在二次型中贡献 2*A12*x*y
A = Matrix([[A11, A12],
[A12, A22]])
# 注意:当前 A 对应的是平移后的坐标系(原点仍在全局原点),但椭圆中心一般不在 (0,0)
# 为获得主轴方向,只需对 A 本征分解(方向与平移无关)
eigvals_A, eigvecs_A = A.diagonalize(normalize=True)
# eigvecs_A 的列即为椭圆在 (x,y) 平面上的主轴方向(单位向量)
# 需将其嵌入三维空间(z 分量为 0),再通过 Q 映射回原始坐标系?不——注意:
# A 已在原始坐标系下构造!因此 eigvecs_A[:,i] 是 (x,y,0) 方向,即三维中前两分量,
# 但严格说,该椭圆位于平面 z=78 上,故其主轴是三维向量:(v_x, v_y, 0)
major_axis_3d = Matrix([eigvecs_A[0, 0], eigvecs_A[1, 0], 0]).normalized()
minor_axis_3d = Matrix([eigvecs_A[0, 1], eigvecs_A[1, 1], 0]).normalized()
print("椭圆主轴方向向量(单位化,位于 z=78 平面内):")
print("长轴:", major_axis_3d.evalf())
print("短轴:", minor_axis_3d.evalf())✅ 关键要点与注意事项:
- 勿混淆变量角色:x_p, y_p, z_p 是椭球主轴坐标系下的坐标,而最终交线必须用原始坐标 (x, y, z) 描述;平面代入应在原始坐标中完成。
- 二次型矩阵必须显式构造:solve() 无法直接返回几何方向;必须从隐式方程中解析出对称矩阵 A,再调用 .diagonalize() 或 .eigenvects()。
- 方向向量属于三维平面:所得 major_axis_3d 自动满足 z=0,表示其沿平面内水平延伸;若平面非水平(如 ax+by+cz=d),则需先做坐标变换或使用法向量叉乘构造基底。
- 数值稳定性提示:实际应用中建议使用 nsimplify() 或设定 rational=False 避免符号膨胀;对病态椭球(如扁率极高),应检查 A 是否正定(eigvals_A > 0)。
通过上述流程,你不仅能定位交线椭圆的位置与尺寸,更能精确获取其在三维空间中的朝向——这是医学影像分割、地质断层建模及机器人视觉中姿态估计的核心步骤。










