四元数的差、对数、指数、幂以及差值

本文深入探讨四元数的差、对数、指数和幂运算,解释了它们在3D图形与游戏开发中的几何意义。特别是四元数的差值slerp,它用于两个四元数间的平滑差值,解决了欧拉角不能平滑过渡的问题。通过实例和Unity源码展示了四元数在实际应用中的计算和使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概要

对于四元数的学习基本上都是参照《3D数学基础-图形与游戏开发这本书的内容的》对于这本书前面的部分还是很好理解的,但是从四元数的差这里开始,就过于抽象了,不配合实例很难去理解。
因此,这一段被我单独提取出来,在实践中进一步去理解。
差,对数,指数等是定义式,不要试图在四元数的这些操作中找到这些数学符号的本身含义,它只是被定义成这样子的,没有为什么。。

四元数的差

四元数的差表示四元数的两个四元数的角位移,比如ad=b,则d就定义为a和b的差。
d = a − 1 b d=a^{-1}b d=a1b

四元数的对数

这个定义有点玄乎,我们定义 α = θ / 2 \alpha = \theta/2 α=θ/2
则四元数的定义就变成了[cosα xsinα ysinα zsinα]
我们定义其对数为
logq = log([cosα nsinα]) ≡ [0 nα]
是的,这样定义我也觉得很奇怪,但是既然有这个定义,那一定是有理由的,暂且先不管为什么要有这种操作,继续学习。

四元数的指数

指数的定义是和对数的定义严格相反的
设p = [0 nα]
exp p = exp [0,nα] ≡ [cosα nsinα]

四元数求幂

设四元数为q,则有四元数的幂运算 q t q^t qt
不同于上面的运算看似毫无意义,这个运算的定义是有几何意义的, q t q^t qt的含义是绕轴n旋转t* θ \theta θ个角度
更通俗一点,比如说q是绕某轴旋转w°,则 q 2 q^2 q2是旋转2w°, q 1 3 q^{\frac{1}{3}} q31是旋转三分之一w, q − 1 q^{-1}

<think>好的,我现在需要解决用户的问题:如何根据公式$T = R@S@M + \text{OFFSET}$,在Python中解旋转矩阵R、尺度变换矩阵S和偏移量OFFSET,且不使用去中心化的方法。用户提供的输入是N×3的数据矩阵T和M。首先,我得理清楚这个问题的数学背景和可能的解法步骤。 首先,我需要明确每个变量的含义和维度。假设T和M都是N×3的矩阵,每一行代表一个三维点的坐标。那么,变换后的点T是通过旋转R、缩放S作用于M,再加上偏移量OFFSET得到的。这里的R应该是一个3×3的旋转矩阵,S是3×3的尺度矩阵,可能是一个对角矩阵,代表各轴的缩放。OFFSET是一个1×3的行向量,或者是一个N×3的矩阵,但通常偏移量可能是一个固定的向量,每个点都加上同样的偏移。这里可能需要明确OFFSET的结构,用户可能指的是每个点加上相同的偏移,即OFFSET是1×3的向量,在加法时进行广播。 不过,根据公式的结构,T的每个点都是R@S@M_i + OFFSET_i,但这里可能OFFSET是一个整体偏移,即所有点都加上同一个偏移。这时候,公式中的OFFSET可能是一个1×3的行向量,然后加到每个结果行上。例如,若R@S@M的结果是N×3矩阵,那么加上OFFSET(同样是N×3的话,可能每个点有不同偏移),但用户可能希望OFFSET是单个向量。这需要明确,但可能用户假设OFFSET是全局的,即所有点都应用同一个偏移。 接下来,问题转化为如何从给定的T和M中估计R、S和OFFSET。通常,这类问题可以通过最小二乘法来解,因为我们需要找到最优的参数,使得变换后的M尽可能接近T。但问题中的变换涉及旋转矩阵、缩放矩阵以及偏移量的组合,可能存在多个未知数需要同时估计。 首先,我需要分解这个方程。假设每个点的变换是:T_i = R @ S @ M_i^T + OFFSET。这里M_i是第i个点的坐标,作为列向量。所以,M的每一行是点的坐标,所以可能需要转置。或者,如果M是N×3,那么当与R@S相乘时,可能需要考虑矩阵乘法的维度是否正确。例如,R是3×3,S是3×3,那么R@S是3×3。M的每一行是1×3,所以当与R@S相乘时,可能需要将M的行转换为列向量,即M_i^T,得到一个3×1的向量,相乘后的结果是3×1,然后加上OFFSET的转置?或者,整个操作可能需要重新排列矩阵的乘法顺序。 这里可能更准确的是,每个点的变换可以表示为:T_i = (R @ S @ M_i.T) + OFFSET.T。这样的话,M_i是行向量,转置成列向量后,被R@S作用,得到一个新的列向量,然后加上OFFSET的列向量。结果再转置成行向量,作为T_i。但这样处理可能比较麻烦,或者可能需要将整个矩阵的运算重新排列。 另一种可能的理解是,整个变换是将所有点同时进行线性变换,再加上偏移。也就是,将M视为3×N的矩阵(每个点是一列),然后R@S作用于每个点,得到3×N的结果,再加上OFFSET的列向量(假设每个点加同一个偏移)。此时,T应该是3×N的矩阵。但用户给出的T和M是N×3的,所以可能需要调整。例如,可能公式中的M和T都是转置后的形式,即公式中的M是3×N,而用户给出的输入是N×3的,所以需要转置处理。 这可能导致一些维度上的混淆。假设用户提供的M和T都是N×3的矩阵,那么要使得R@S作用于每个点,可能需要将每个点作为列向量。因此,可能需要将M转置为3×N的矩阵,然后R@S是3×3矩阵,相乘后得到3×N的结果,再加上一个3×N的偏移矩阵,其中每一列都是OFFSET的列向量。然后,结果再转置回N×3的矩阵,得到T。但这样的话,公式应该是T = (R @ S @ M.T + OFFSET.T).T。这可能比较复杂,或者用户可能将整个运算视为对每个点的线性变换加上平移,因此每个点的计算是:T_i = M_i @ (R @ S).T + OFFSET。这种情况下,M_i是行向量,乘以R@S的转置(即行向量乘以矩阵),再加上行向量的OFFSET。 或者,可能用户希望的是,整体的变换公式为:T = (M @ (R @ S).T) + OFFSET。其中,M是N×3矩阵,R@S是3×3的矩阵,其转置后的维度是3×3,因此M @ (R@S).T的结果是N×3,再加上OFFSET(同样是N×3,或者是一个1×3的行向量,通过广播加到每一行)。这种情况下,公式可以表示为:每个点经过旋转和缩放后的坐标,再加上平移量。 现在,假设用户的问题中的公式是:T = M @ (R @ S).T + OFFSET。那么,问题转化为如何从已知的T和M中解R、S、OFFSET三个参数。这看起来像是一个仿射变换的问题,但这里R是旋转矩阵(正交矩阵,行列式为1),S是对角矩阵(尺度缩放),而OFFSET是平移向量。 不过,这样的分解是否唯一?可能存在多个解,或者需要通过某种约束条件下的优化来解。例如,旋转矩阵R需要满足R^T R = I,det(R)=1,而S是对角矩阵。此外,可能还需要确定是否允许不同的轴有不同的缩放(即S是否是对角矩阵,而非标量矩阵)。 在这种情况下,可能需要将问题分解为几个步骤: 1. 估计OFFSET。如果先不考虑旋转和缩放,那么OFFSET可能可以通过某些方法确定。例如,假设当M中的点与T中的点经过变换后的异的均值即为OFFSET。但需要进一步分析。 或者,当M和T的对应点之间存在线性变换和平移,通常的解法是先通过去中心化(即减去均值)来消除平移的影响,然后解旋转和缩放。但用户明确指出不使用去中心化的方法,因此必须同时估计平移、旋转和缩放。 这可能增加问题的复杂性,因为平移量OFFSET与旋转和缩放参数之间存在耦合。通常,在类似的问题中(如点云配准),去中心化可以分离平移和旋转/缩放的估计,但这里用户希望不这样做。 因此,可能需要将整个问题建模为一个优化问题,其中需要同时优化R、S、OFFSET,使得T的预测值与实际值的平方最小。不过,这样的优化问题可能具有较高的计算复杂度,尤其是当参数之间存在约束时(如R必须是旋转矩阵,S必须是对角矩阵)。 接下来,我需要考虑如何参数化这些变量。例如,旋转矩阵R可以用欧拉角、四元数或轴角表示,但优化时可能需要转换为矩阵形式。而S是对角矩阵,可以用三个参数(对角线上的元素)表示。OFFSET是三维向量,有三个参数。总共有3(旋转参数)+3(缩放参数)+3(平移参数)=9个参数需要优化,如果旋转参数用欧拉角的话。或者,如果R使用正交矩阵的约束,可能需要更多的处理。 可能的步骤: 1. 将问题转化为最小二乘优化问题,目标是最小化||T - (M @ (R@S).T + OFFSET)||_F^2,其中||·||_F表示Frobenius范数。 2. 参数化R、S和OFFSET。例如,S是对角矩阵,可以用向量s = [s_x, s_y, s_z]表示。旋转矩阵R可以由三个欧拉角参数化(例如,绕x、y、z轴的旋转角度α、β、γ)。OFFSET可以表示为三个平移参数t_x, t_y, t_z。 3. 使用数值优化方法(如Levenberg-Marquardt算法)来最小化目标函数。在Python中,可以使用scipy.optimize中的相关函数,例如scipy.optimize.least_squares。 4. 在优化过程中,必须将约束条件(如R必须是旋转矩阵,S必须是对角矩阵)施加到参数上。例如,当参数化R时,必须确保生成的矩阵是正交的,并且行列式为1。这可能比较复杂,因为直接优化欧拉角可能会遇到万向锁等问题,或者参数化可能需要额外的处理。 或者,可以考虑将旋转矩阵R分解为四元数或其他更稳定的表示方式,但同样需要处理转换问题。 另一个思路是,将R和S合并为一个线性变换矩阵A = R @ S,那么公式变为T = M @ A.T + OFFSET。此时,A是一个3×3的矩阵,可以分解为R(旋转)和S(缩放)。不过,这样的分解是否是唯一的?比如,A可以分解为一个正交矩阵R和一个对称矩阵(这里S是对角矩阵,因此对称),这可能对应于极分解。极分解将矩阵A分解为一个正交矩阵和一个对称正定矩阵的乘积,即A = R U,其中U是正定对称矩阵。在这种情况下,如果S是对称正定的对角矩阵,那么极分解可能可以应用。 假设这个分解存在,那么可以通过以下步骤: 1. 先估计A和OFFSET,使得T ≈ M @ A.T + OFFSET。这一步可以通过线性最小二乘来解,因为对于每个点i,有T_i ≈ M_i A.T + OFFSET。将A和OFFSET作为未知数,可以将问题转化为线性方程组,进而用最小二乘法解。 具体来说,将每个样本的方程展开,得到: 对于每个点i,T_i = M_i A^T + OFFSET. 将A视为一个3x3矩阵,展开为9个变量(a11, a12, a13, a21, ..., a33),OFFSET是3个变量。因此,每个点有三个方程(对应x,y,z三个坐标),总共有3N个方程,12个未知变量(9+3)。这可以通过构建一个大的系数矩阵,用最小二乘法解。 假设我们将问题改写为:T_i - OFFSET = M_i A^T。将A和OFFSET视为未知数,那么这可以转化为一个线性问题,其中对于每个坐标分量: 例如,T的x坐标:T_i,x = (M_i,1 * a11 + M_i,2 * a21 + M_i,3 * a31) + offset_x. 同理,其他坐标类似。这可以视为线性回归问题,其中未知数是a11, a21, a31, ..., a33以及offset_x, offset_y, offset_z。 因此,可以构造设计矩阵X和响应变量Y,然后用线性最小二乘法解系数。 但这样做的问题是,这样得的A矩阵可能无法分解为R和S(旋转和缩放)的乘积。因此,接下来需要将A分解为R和S,其中R是旋转矩阵,S是对角矩阵。然而,这样的分解是否可能? 假设A = R S,其中R是正交矩阵,S是对角矩阵。那么,A的极分解可能无法直接得到这样的分解,因为极分解通常是A = R U,其中U是正定对称矩阵,而这里的S是对角矩阵,所以U必须是正定对称且对角,即S是一个正定对角矩阵。此时,极分解可能可以得到所需的分解。例如,将A分解为旋转矩阵R和对称正定矩阵S,其中S是对角的。 极分解的步骤是:给定矩阵A,计算A = R U,其中R是正交矩阵,U是正定对称矩阵。U可以通过计算 (A^T A)^{1/2},而R = A U^{-1}。此时,如果U是对角矩阵,则S = U。否则,可能需要进一步的对角化处理,但一般情况下,U可能不是对角的,除非A满足特定条件。 因此,这样的分解可能只有在A的极分解得到的U是对角矩阵的情况下才可行。否则,可能需要其他方法。 如果无法保证U是对角的,那么这种方法可能无法得到所需的S矩阵。因此,用户的问题可能需要更复杂的处理,或者在优化过程中同时约束A的结构。 因此,回到最初的思路,如果用户希望直接解R、S、OFFSET,可能需要使用非线性优化方法,同时满足R的正交性约束和S的对角约束。 可能的步骤: 1. 将问题建模为非线性最小二乘问题,优化变量包括旋转参数(如欧拉角)、缩放参数(s_x, s_y, s_z)和偏移量(t_x, t_y, t_z)。 2. 目标函数是预测误的平方和:sum_{i=1}^N ||T_i - (R @ S @ M_i.T + OFFSET)||^2。这里假设M_i是行向量,转置成列向量后与R@S相乘,再加上OFFSET的列向量,再转置为行向量得到预测的T_i。 或者,更准确地说,每个点的计算是:T_i = (R @ S @ M_i.T).T + OFFSET。这等于M_i @ (R @ S).T + OFFSET。因此,整个方程可以写成矩阵形式:T = M @ (R @ S).T + OFFSET。因此,目标函数是||T - (M @ (R@S).T + OFFSET)||_F^2。 3. 参数化: - 旋转矩阵R可以用欧拉角参数化为三个角度α, β, γ。例如,绕Z-Y-X轴的旋转顺序。 - 缩放矩阵S是对角矩阵,参数为s_x, s_y, s_z. - 偏移量OFFSET是三个参数t_x, t_y, t_z. 4. 将这些参数组合为一个参数向量theta = [α, β, γ, s_x, s_y, s_z, t_x, t_y, t_z]。 5. 定义目标函数,该函数将theta转换为R、S、OFFSET,并计算误。 6. 使用非线性最小二乘优化器(如scipy.optimize.least_squares)进行优化,可能需要提供参数的初始猜测。 这种方法的关键在于如何处理旋转矩阵的正交约束。因为如果直接优化欧拉角,生成的矩阵R将自动满足正交性(只要正确实现旋转矩阵的构造)。例如,当使用欧拉角构造旋转矩阵时,每个角对应绕相应轴的旋转矩阵,最终的R是这些旋转矩阵的乘积,结果必然是正交矩阵。 因此,正确参数化R是关键。例如,构造R的函数应该接收欧拉角参数,并生成正确的旋转矩阵。 此外,缩放矩阵S必须是对角的,因此只需要三个参数,构造为diag([s_x, s_y, s_z])即可。 接下来,编写Python代码的大致步骤如下: 1. 定义函数从theta生成R、S、OFFSET: - theta的前三个元素是欧拉角,生成R。 - 接下来的三个元素是缩放系数,生成S。 - 最后三个是偏移量,生成OFFSET。 2. 定义残函数,将每个样本点的预测T_i与实际T_i的差值作为残。 3. 调用优化器,例如scipy.optimize.least_squares,传入残函数和初始参数猜测。 需要注意的问题: - 初始参数的选择对优化结果影响很大。如果初始值离真实值太远,可能会陷入局部最优。 - 欧拉角的参数化可能存在万向锁问题,导致某些旋转无法准确表示。可以考虑使用其他旋转表示方法,如四元数或轴角,但这会增加参数数量或复杂度。 - 缩放矩阵的参数是否允许负数?如果允许,则s_x, s_y, s_z可以为负;否则,可能需要约束它们为正数。这可能需要使用有约束的优化方法,或者对参数进行转换(例如,优化它们的对数,确保正数)。 - OFFSET是否有约束?通常平移量可以是任意实数,所以无需额外约束。 现在,假设用户希望使用Python实现,并且不进行去中心化处理,那么代码的大致结构可能如下: 首先,导入必要的库: import numpy as np from scipy.optimize import least_squares 然后,定义构造R、S、OFFSET的函数: def get_rotation_matrix(alpha, beta, gamma): # 根据欧拉角(例如绕Z-Y-X顺序)生成旋转矩阵 # 这里需要具体实现欧拉角到旋转矩阵的转换 # 示例:绕X轴旋转alpha,绕Y轴旋转beta,绕Z轴旋转gamma Rx = np.array([[1, 0, 0], [0, np.cos(alpha), -np.sin(alpha)], [0, np.sin(alpha), np.cos(alpha)]]) Ry = np.array([[np.cos(beta), 0, np.sin(beta)], [0, 1, 0], [-np.sin(beta), 0, np.cos(beta)]]) Rz = np.array([[np.cos(gamma), -np.sin(gamma), 0], [np.sin(gamma), np.cos(gamma), 0], [0, 0, 1]]) R = Rz @ Ry @ Rx return R def get_scale_matrix(sx, sy, sz): return np.diag([sx, sy, sz]) 然后,定义残函数: def residuals(theta, M, T): alpha, beta, gamma, sx, sy, sz, tx, ty, tz = theta R = get_rotation_matrix(alpha, beta, gamma) S = get_scale_matrix(sx, sy, sz) offset = np.array([tx, ty, tz]) # 计算预测的T: M @ (R@S).T + offset predicted_T = M @ (R @ S).T + offset # 计算残 return (predicted_T - T).flatten() 然后,设置初始参数猜测,例如: # 初始猜测:无旋转,无缩放,无平移 theta0 = np.zeros(9) theta0[3] = 1.0 # sx=1 theta0[4] = 1.0 # sy=1 theta0[5] = 1.0 # sz=1 然后,调用优化器: result = least_squares(residuals, theta0, args=(M, T)) 最后,从结果中提取参数: alpha_opt, beta_opt, gamma_opt, sx_opt, sy_opt, sz_opt, tx_opt, ty_opt, tz_opt = result.x R_opt = get_rotation_matrix(alpha_opt, beta_opt, gamma_opt) S_opt = get_scale_matrix(sx_opt, sy_opt, sz_opt) offset_opt = np.array([tx_opt, ty_opt, tz_opt]) 但需要注意,这种方法的可行性和正确性取决于多个因素: 1. 欧拉角的参数化是否正确。不同的旋转顺序会导致不同的结果,需要确保与数据中的实际旋转顺序一致。 2. 初始猜测的选择是否合理。如果初始值离真实值太远,优化可能无法收敛到正确解。 3. 是否存在多个局部最优解,导致优化结果不准确。 此外,可能需要对优化参数进行适当的缩放,使得优化器能更有效地处理不同量级的参数(例如,旋转角度以弧度为单位,可能数值较小,而平移量可能较大)。 另外,可能还需要考虑约束条件。例如,旋转矩阵的行列式应为1,但通过欧拉角参数化生成的旋转矩阵行列式自然为1。如果使用其他参数化方法(如四元数),可能需要额外的约束。 另外,缩放矩阵的参数是否允许负值?如果允许,则没有问题;否则,需要在优化时对参数进行约束,例如使用有界的优化方法,设置s_x, s_y, s_z的下界为0。但在代码示例中,初始值设为1,可能假设缩放为正。但用户的问题中是否允许负的缩放?例如,镜像变换可能涉及负的缩放因子,但通常缩放是指各轴的正比例变化。 如果需要约束缩放因子为正数,可以在优化时设置bounds参数: 例如,将theta中sx, sy, sz的lower bound设为0: lower_bounds = [-np.inf]*3 + [0, 0, 0] + [-np.inf]*3 upper_bounds = [np.inf]*9 result = least_squares(residuals, theta0, args=(M, T), bounds=(lower_bounds, upper_bounds)) 但需要根据具体问题进行调整。 此外,用户的问题中提到“不使用去中心化”,即不能通过先减去均值的方法消除平移的影响。传统的点云配准方法中,通常先计算两个点云的中心,平移使得中心重合,从而将问题转化为无平移的情况,但这里用户希望不这么做,因此必须将平移作为参数一起估计。 在这种情况下,优化问题可能更难,因为平移参数与旋转、缩放参数之间存在耦合。例如,较大的平移量可能需要较大的旋转或缩放来补偿,导致优化过程更易陷入局部最小值。 另外,是否可能存在解的唯一性?例如,如果存在多个旋转、缩放和平移的组合可以得到相同的变换,那么问题将无法唯一确定参数。因此,需要确保数据足够多,以提供足够的约束条件。 综上,用户的问题需要通过非线性优化同时估计旋转、缩放和平移参数。可能的Python实现如上述代码示例,但需要注意参数化方式、初始猜测、约束条件等。此外,可能需要验证解后的参数是否满足要,例如旋转矩阵的正交性。 最后,测试该方法的正确性,例如,生成带有已知参数的合成数据,看是否能正确恢复参数。</think>根据变换公式$T = R@S@M + \text{OFFSET}$,解旋转矩阵$R$、尺度矩阵$S$和偏移量$\text{OFFSET}$的非线性优化方法如下: ### 数学建模与实现步骤 1. **参数化变量**: - 旋转矩阵$R$用欧拉角$\alpha, \beta, \gamma$表示 - 尺度矩阵$S$用对角线参数$s_x, s_y, s_z$表示 - 偏移量$\text{OFFSET}$用$t_x, t_y, t_z$表示 2. **目标函数**: 最小化残平方和: $$ \min_{\theta} \sum_{i=1}^N ||T_i - (M_i@(R@S)^\top + \text{OFFSET})||^2 $$ 其中$\theta = [\alpha, \beta, \gamma, s_x, s_y, s_z, t_x, t_y, t_z]$ 3. **Python实现**: ```python import numpy as np from scipy.optimize import least_squares def euler_to_rotation(alpha, beta, gamma): """ 欧拉角转旋转矩阵 (Z-Y-X顺序) """ Rx = np.array([[1, 0, 0], [0, np.cos(alpha), -np.sin(alpha)], [0, np.sin(alpha), np.cos(alpha)]]) Ry = np.array([[np.cos(beta), 0, np.sin(beta)], [0, 1, 0], [-np.sin(beta), 0, np.cos(beta)]]) Rz = np.array([[np.cos(gamma), -np.sin(gamma), 0], [np.sin(gamma), np.cos(gamma), 0], [0, 0, 1]]) return Rz @ Ry @ Rx def residuals(theta, M, T): alpha, beta, gamma, sx, sy, sz, tx, ty, tz = theta R = euler_to_rotation(alpha, beta, gamma) S = np.diag([sx, sy, sz]) offset = np.array([tx, ty, tz]) predicted = M @ (R @ S).T + offset return (predicted - T).flatten() def estimate_RS_offset(M, T, init_scale=(1,1,1)): """ 参数估计主函数 """ theta0 = np.zeros(9) theta0[3:6] = init_scale # 初始化缩放参数 result = least_squares(residuals, theta0, args=(M, T)) params = result.x R = euler_to_rotation(*params[:3]) S = np.diag(params[3:6]) offset = params[6:9] return R, S, offset ``` ### 使用示例 ```python # 生成测试数据 true_R = euler_to_rotation(np.pi/4, 0, 0) # 绕X轴旋转45度 true_S = np.diag([2, 1, 0.5]) true_offset = np.array([1, 2, 3]) M = np.random.rand(100, 3) # 原始数据 T = M @ (true_R @ true_S).T + true_offset # 变换后数据 # 参数估计 R_est, S_est, offset_est = estimate_RS_offset(M, T) ``` ### 验证方法 1. **正交性验证**: ```python print("R正交性误:", np.linalg.norm(R_est @ R_est.T - np.eye(3))) ``` 2. **残检验**: ```python pred_T = M @ (R_est @ S_est).T + offset_est print("最大残:", np.max(np.abs(pred_T - T))) ``` ### 注意事项 1. **初始值敏感**:建议根据物理意义初始化缩放参数(如初始设为[1,1,1]) 2. **旋转表示限制**:欧拉角存在万向锁问题,建议配合数据特性选择旋转轴顺序 3. **数值稳定性**:大角度旋转建议使用四元数参数化[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值