坐标转换 四参数/七参数/正形变换 ∈ C# 编程笔记

更新日期:2020/4/7

【注1】其中的代码也许并不完整,您可以作为伪码参看,或者您可以去我主博客逛逛,也许有意外之喜!
【注2】此篇博客是 C# 编程笔记 的子博客。

上一篇:-- 回到 主博客 下一篇:曲线道路坐标计算

2、平面坐标转换 四参数/正形变换

最近(2020/4/2)总结四参数坐标转换的时候,发现了一个问题,就是在之前我大二的时候编写平面四参数坐标转换直接用到了一个公式:
在这里插入图片描述
看之前的实习报告也没有说明公式的由来,然后我就开始查阅资料,终于有了点苗头,在这里记录一下。
从最原始开始…
在这里插入图片描述
如上图所示,(x,y)是源坐标,(X,Y)是目标坐标,注意,这里旋转角是负的(顺正逆负),转换公式为:
在这里插入图片描述
为了得到这四个参数,我们有两种方法:一种是只知道两对点,用直接参数法求解;另一种是知道不只两对点,我们可以先用两对点求近似值,然后用迭代,前后两次迭代结果小于限差就跳出循环。

2.1 直接法

在这里插入图片描述

2.2 平差法

利用两个点(最好是可以控制全局的两个点)用上面的直接求参的方法求得近似值后,再用下面的公式利用源坐标(x,y)计算出目标坐标的近似值(X’,Y’)。不妨将刚才求出来的近似值记作:
在这里插入图片描述
将真值记作:
在这里插入图片描述
在这里插入图片描述
毕竟我们用的是所求参数的近似值,所以必然会存在偏差,但是这个偏差又非常之微小,所以我们可以在
在这里插入图片描述
处进行泰勒展开;要想继续进行下去,您首先需要了解泰勒展开:
在这里插入图片描述
代码实现:
注【1】:如果您直接复制粘贴这些代码的话也许会发现会缺失函数。这是正常情况,因为有些函数是我编写的类里面的,都粘贴下来的话会十分繁杂。所以您可以当做伪码来看。
注【2】:用此方法也许会出现永远跳不出来循环的情况(我用实验数据尝试过,没有成功)我觉得原因是:1)我设置的限差太过苛刻;2)所用数据本身测量误差很大。

		/*此函数用于求象限角
         */ 
        public static double arctan(double dx, double dy)
        {
            double angle = 0;
            if(dx==0)
            {
                if(dy>0) angle=Math.PI*0.5;
                else     angle=Math.PI*1.5;
                return angle;
            }
            angle = Math.Atan2(Math.Abs(dy), Math.Abs(dx));
            if (dx < 0)
            {
                if (dy < 0) angle = Math.PI + angle;
                else        angle = Math.PI - angle;
            }
            else if (dy < 0) angle = 2 * Math.PI - angle;
            return angle;
        }
        /*此函数用于求四参数的近似值,
         * 输入:po=源坐标,pt=目标坐标
         * 输出:
         * 0 尺度因子
         * 1 旋转角度
         * 2 x平移参数
         * 3 y平移参数
         */ 
        public static double[] Get_Four_JS(Point po1, Point po2, Point pt1, Point pt2)
        {
            double[] JS = new double[4];
            double dx = po1.x - po2.x, dy = po1.y - po2.y;
            double dX = pt1.x - pt2.x, dY = pt1.y - pt2.y;
            double s = Math.Sqrt(dx * dx + dy * dy);
            double S = Math.Sqrt(dX * dX + dY * dY);
            double A = arctan(dX, dY);
            double a = arctan(dx, dy);
            JS[0] = s / S;
            JS[1] = A - a;
            JS[2] = pt1.x - po1.x;
            JS[3] = pt1.y - po1.y;
            return JS;
        }
        /* 此函数用于求取平面四参数 用到坐标重心化
          * 输入:po是源坐标,pt是目标坐标
          * 输出:resultMat是结果矩阵数组
          *     第一个矩阵 X      是所求的四参数
          *     第二个矩阵 V      是改正数,可以算内符合精度
          *     第三个矩阵 Sigma2 是验后单位权中误差的平方
          */
        public static Matrix[] Get_Four(Point[] po1, Point[] pt1)
        {
            int n = po1.Length;
            int r = 2 * n - 4;//多余观测数
            Point[] po = Point.ZXH(po1);//坐标重心化
            Point[] pt = Point.ZXH(pt1);
            double[] JS = Get_Four_JS(po[0], po[n - 1], pt[0], pt[n - 1]);//求近似
            double Dx = JS[2] ,
                Dy = JS[3],
                m = JS[0],
                Sita = JS[1];
            Matrix[] resultMat = new Matrix[3];
            Matrix B = new Matrix(2 * n, 4); ;
            Matrix P = new Matrix(2 * n); P.MakeUnitMatrix(2 * n);//默认权矩阵为单位阵
            Matrix L = new Matrix(2 * n, 1);
            Matrix X1 = new Matrix(4, 1);//参数
            while (true)
            {
                for (int i = 0; i < n; i++)
                {//根据近似参数求观测值
                    double a = Math.Cos(Sita) * po[i].x + Math.Sin(Sita) * po[i].y,
                        b = -Math.Sin(Sita) * po[i].x + Math.Cos(Sita) * po[i].y,
                        X = Dx + a * m,
                        Y = Dy - b * m;
                    B[2 * i, 0] = 1;
                    B[2 * i, 1] = 0;
                    B[2 * i, 2] = a;
                    B[2 * i, 3] = b * m;
                    B[2 * i + 1, 0] = 0;
                    B[2 * i + 1, 1] = 1;
                    B[2 * i + 1, 2] = b;
                    B[2 * i + 1, 3] = -a * m;
                    L[2 * i, 0] = pt[i].x - X;
                    L[2 * i + 1, 0] = pt[i].y - Y;
                }
                Matrix Nbb = B.Transpose() * P * B;
                Nbb.InvertGaussJordan();
                X1 = Nbb * B.Transpose() * P * L;
                X1.Eps = 1e-6;
                if (X1.Equals(0))
                    break;
                Dx += X1[0, 0];
                Dy += X1[1, 0];
                m += X1[2, 0];
                Sita += X1[3, 0];
            }           
            Matrix V = B * X1 - L;
            Matrix Sigma2 = V.Transpose() * P * V;//验后单位权中误差
            Sigma2[0, 0] /= r;

            resultMat[0] = X1;
            resultMat[1] = V;
            resultMat[2] = Sigma2;
            return resultMat;
        }
2.3 正形变换法

正形变换的公式就比较复杂了,好像还考虑到了投影变形等因素,具体的原理目前还不是特别明晰。先把公式列出来:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现:

		/*此函数用于求d的n次方,因为用Math.Pow()太累赘
         */ 
        public static double P(double d, double n)
        {
            return Math.Pow(d, n);
        }
		/* 此函数用于求取平面四参数 未用到坐标重心化,实际上是舍去了正形变换的高阶项
          * 输入:po是源坐标,pt是目标坐标,可选参数n 取前n(>=2)点平差
          * 输出:resultMat是结果矩阵数组
          *     第一个矩阵 X      是所求的四参数
          *     第二个矩阵 V      是改正数,可以算内符合精度
          *     第三个矩阵 Sigma2 是验后单位权中误差的平方
          */
        public static Matrix[] Get_Four1(Point[] po, Point[] pt,int n=0)
        {
            if(n==0) n = po.Length;
            int r = 2 * n - 4;//多余观测数
            Matrix[] resultMat = new Matrix[3];
            Matrix B = new Matrix(2 * n, 4); ;
            Matrix P = new Matrix(2 * n); P.MakeUnitMatrix(2 * n);//默认权矩阵为单位阵
            Matrix L = new Matrix(2 * n, 1);
            Matrix X = new Matrix(4, 1);//参数
            for (int i = 0; i < n; i++)
            {
                double x = po[i].x, y = po[i].y;
                double x1 = pt[i].x, y1 = pt[i].y;
                B[2 * i, 0] = 1;
                B[2 * i, 1] = 0;
                B[2 * i, 2] = x;
                B[2 * i, 3] = -y;
                B[2 * i + 1, 0] = 0;
                B[2 * i + 1, 1] = 1;
                B[2 * i + 1, 2] = y;
                B[2 * i + 1, 3] = x;
                L[2 * i, 0] = x1;
                L[2 * i + 1, 0] = y1;
            }
            
            Matrix Nbb = B.Transpose() * P * B;
            Nbb.InvertGaussJordan();
            X = Nbb * B.Transpose() * P * L;
            Matrix V = B * X - L;
            Matrix Sigma2 = V.Transpose() * P * V;//验后单位权中误差
            if (r != 0) Sigma2[0, 0] /= r;
            else Sigma2[0, 0] = 0;

            resultMat[0] = X;
            resultMat[1] = V;
            resultMat[2] = Sigma2;
            return resultMat;
        }
        /* 此函数用正形变换求取平面四参数 用到坐标重心化
          * 输入:po是源坐标,pt是目标坐标
          * 输出:resultMat是结果矩阵数组
          *     第一个矩阵 X      是所求的四参数
          *     第二个矩阵 V      是改正数,可以算内符合精度
          *     第三个矩阵 Sigma2 是验后单位权中误差的平方
         *      第四个矩阵 ZX     是源/目重心[po.x,po,y,pt.x,pt.y]
          */
        public static Matrix[] Get_Four_ZX(Point[] po1, Point[] pt1)
        {
            int n = pt1.Length;
            Point[] po = Point.ZXH(po1);//坐标重心化
            Point[] pt = Point.ZXH(pt1);
            int r = 2 * n - 10;//冗余度
            Matrix[] resultMat = new Matrix[4];

            Matrix[] JS = Get_Four1(po, pt, 2); Matrix C = JS[0];
            double p0 = C[0, 0],//前四个参数的近似值
                q0 = C[1, 0],
                p1 = C[2, 0],
                q1 = C[3, 0];
            Matrix B = new Matrix(2 * n, 10);
            Matrix L = new Matrix(2 * n, 1);
            for (int i = 0; i < n; i++)
            {
                double x = po[i].x, y = po[i].y;
                double x1 = pt[i].x, y1 = pt[i].y;
                B[2 * i, 0] = 1;
                B[2 * i, 1] = 0;
                B[2 * i, 2] = x;
                B[2 * i, 3] = -y;
                B[2 * i, 4] = P(x, 2) - P(y, 2);
                B[2 * i, 5] = -2 * x * y;
                B[2 * i, 6] = P(x, 3) - 3 * x * P(y, 2);
                B[2 * i, 7] = P(y, 3) - 3 * P(x, 2) * y;
                B[2 * i, 8] = P(x, 4) - 6 * P(x, 2) * P(y, 2) + P(y, 4);
                B[2 * i, 9] = 4 * x * P(y, 3) - 4 * P(x, 3) * y;
                B[2 * i + 1, 0] = 0;
                B[2 * i + 1, 1] = 1;
                B[2 * i + 1, 2] = y;
                B[2 * i + 1, 3] = x;
                B[2 * i + 1, 4] = 2 * x * y;
                B[2 * i + 1, 5] = P(x, 2) - P(y, 2);
                B[2 * i + 1, 6] = 3 * P(x, 2) * y - P(y, 3);
                B[2 * i + 1, 7] = P(x, 3) - 3 * x * P(y, 2);
                B[2 * i + 1, 8] = 4 * P(x, 3) * y - 4 * x * P(y, 3);
                B[2 * i + 1, 9] = P(x, 4) - 6 * P(x, 2) * P(y, 2) + P(y, 4);
                L[2 * i, 0] = x1 - p0 - x * p1 + y * q1;
                L[2 * i + 1, 0] = y1 - q0 - y * p1 - x * q1;
            }
            Matrix Nbb = B.Transpose() * B;
            Nbb.InvertGaussJordan();
            Matrix X = Nbb * B.Transpose() * L;
            Matrix Xp = X;
            while (true)
            {
                p0 += X[0, 0];
                q0 += X[1, 0];
                p1 += X[2, 0];
                q1 += X[3, 0];
                for (int i = 0; i < n; i++)
                {
                    double x = po[i].x, y = po[i].y;
                    double x1 = pt[i].x, y1 = pt[i].y;
                    L[2 * i, 0] = x1 - p0 - x * p1 + y * q1;
                    L[2 * i + 1, 0] = y1 - q0 - y * p1 - x * q1;
                }
                X = Nbb * B.Transpose() * L;
                X.Eps = 1e-6;
                if (X.Equals(Xp)) break;
                Xp = X;
            }
            Matrix V = B * X - L;
            X[0,0]+=p0;
            X[1,0]+=q0;
            X[2,0]+=p1;
            X[3,0]+=q1;
            Matrix ZX=new Matrix(4,1);//重心矩阵
            ZX[0,0]=po[n].x; ZX[0,1]=po[n].y;
            ZX[0,2]=pt[n].x; ZX[0,3]=pt[n].y;

            Matrix Sigma2 = V.Transpose() * V;//延后单位权中误差
            if (r != 0) Sigma2[0, 0] /= r;
            else Sigma2[0, 0] = 0;
            resultMat[0] = X;
            resultMat[1] = V;
            resultMat[2] = Sigma2;
            resultMat[3] = ZX;
            return resultMat;
        }

从源坐标转换为目标坐标的代码:

         /* 此函数用于平面坐标转换,对应函数为 Get_Four_ZX
          * 输入:po是源坐标; M 是用正形参数求取函数得到的矩阵数组
          * 输出:pt是目标坐标
          */
        public static Point[] zbTrans_ZX(Point[] po, Matrix[] M)
        {
            int n=po.Length;
            Point[] pt = new Point[n];
            Matrix X = M[0];
            Matrix ZX=M[3];
            Matrix B = new Matrix(2, 10);
            Matrix Pt;
            Point po_ZX = new Point(ZX[0, 0], ZX[1, 0]);
            Point pt_ZX = new Point(ZX[2, 0], ZX[3, 0]);
            for (int j = 0; j < n; j++)
            {
                int i = 0;
                double x = po[j].x - po_ZX.x, y = po[j].y - po_ZX.y;
                B[2 * i, 0] = 1;
                B[2 * i, 1] = 0;
                B[2 * i, 2] = x;
                B[2 * i, 3] = -y;
                B[2 * i, 4] = P(x, 2) - P(y, 2);
                B[2 * i, 5] = -2 * x * y;
                B[2 * i, 6] = P(x, 3) - 3 * x * P(y, 2);
                B[2 * i, 7] = P(y, 3) - 3 * P(x, 2) * y;
                B[2 * i, 8] = P(x, 4) - 6 * P(x, 2) * P(y, 2) + P(y, 4);
                B[2 * i, 9] = 4 * x * P(y, 3) - 4 * P(x, 3) * y;
                B[2 * i + 1, 0] = 0;
                B[2 * i + 1, 1] = 1;
                B[2 * i + 1, 2] = y;
                B[2 * i + 1, 3] = x;
                B[2 * i + 1, 4] = 2 * x * y;
                B[2 * i + 1, 5] = P(x, 2) - P(y, 2);
                B[2 * i + 1, 6] = 3 * P(x, 2) * y - P(y, 3);
                B[2 * i + 1, 7] = P(x, 3) - 3 * x * P(y, 2);
                B[2 * i + 1, 8] = 4 * P(x, 3) * y - 4 * x * P(y, 3);
                B[2 * i + 1, 9] = P(x, 4) - 6 * P(x, 2) * P(y, 2) + P(y, 4);
                Pt = B * X;
                pt[j] = new Point(Pt[0, 0]+pt_ZX.x, Pt[1, 0]+pt_ZX.y);
            }
            return pt;
        }
附 简化版正形变换法 代码

至此,终于搞清楚了刚开始的公式由来------简化版的正形变换
其实现代码上面有,但为了以后方便复制,下面再列一遍:

         /* 此函数用于求取平面四参数 未用到坐标重心化,实际上是舍去了正形变换的高阶项
          * 输入:po是源坐标,pt是目标坐标,可选参数n 取前n(>=2)点平差
          * 输出:resultMat是结果矩阵数组
          *     第一个矩阵 X      是所求的四参数
          *     第二个矩阵 V      是改正数,可以算内符合精度
          *     第三个矩阵 Sigma2 是验后单位权中误差的平方
          */
        public static Matrix[] Get_Four1(Point[] po, Point[] pt,int n=0)
        {
            if(n==0) n = po.Length;
            int r = 2 * n - 4;//多余观测数
            Matrix[] resultMat = new Matrix[3];
            Matrix B = new Matrix(2 * n, 4); ;
            Matrix P = new Matrix(2 * n); P.MakeUnitMatrix(2 * n);//默认权矩阵为单位阵
            Matrix L = new Matrix(2 * n, 1);
            Matrix X = new Matrix(4, 1);//参数
            for (int i = 0; i < n; i++)
            {
                double x = po[i].x, y = po[i].y;
                double x1 = pt[i].x, y1 = pt[i].y;
                B[2 * i, 0] = 1;
                B[2 * i, 1] = 0;
                B[2 * i, 2] = x;
                B[2 * i, 3] = -y;
                B[2 * i + 1, 0] = 0;
                B[2 * i + 1, 1] = 1;
                B[2 * i + 1, 2] = y;
                B[2 * i + 1, 3] = x;
                L[2 * i, 0] = x1;
                L[2 * i + 1, 0] = y1;
            }
            
            Matrix Nbb = B.Transpose() * P * B;
            Nbb.InvertGaussJordan();
            X = Nbb * B.Transpose() * P * L;
            Matrix V = B * X - L;
            Matrix Sigma2 = V.Transpose() * P * V;//验后单位权中误差
            if (r != 0) Sigma2[0, 0] /= r;
            else Sigma2[0, 0] = 0;

            resultMat[0] = X;
            resultMat[1] = V;
            resultMat[2] = Sigma2;
            return resultMat;
        }       

从源坐标转换为目标坐标的代码:

         /* 此函数用于四参数坐标转换,对应函数为 Get_Four1
          * 输入:po是源坐标; M 是用四参数求取函数得到的矩阵数组
          * 输出:pt是目标坐标
          */
        public static Point[] zbTrans4(Point[] po, Matrix[] M)
        {
            int n=po.Length;
            Point[] pt = new Point[n];
            Matrix X = M[0];
            Matrix B = new Matrix(2, 4);
            Matrix Pt;
            for (int i = 0; i < n; i++)
            {
                double x = po[i].x, y = po[i].y;
                B[0, 0] = 1;
                B[0, 1] = 0;
                B[0, 2] = x;
                B[0, 3] = -y;
                B[1, 0] = 0;
                B[1, 1] = 1;
                B[1, 2] = y;
                B[1, 3] = x;
                Pt = B * X;
                pt[i] = new Point(Pt[0, 0], Pt[1, 0]);
            }
            return pt;
        }

总结:若精度要求高则用正形变换,精度要求低就用简化版的正形变换或直接法。(aiyamaya,累死我了2020/4/2 22:35)

2.4 七参数坐标转换

七参数坐标用于求取空间坐标系1到空间坐标系2之间的转换关系,具体的公式如下所示
在这里插入图片描述
公式中XYZ 是目标坐标系中的坐标,xyz 是源坐标系中的坐标,DX DY DZ 是三个平移参量,RX RY RZ 是三个旋转参数,DK 是尺度因子。
注意:上述公式只适用于小角度转换,好像是叫布尔沙模型。实际上,理论上严格推导出来的公式不是如此,此公式进行了一定的化简,所求参数也并不是真正的旋转角。由于时间有限,这里暂且不推导,以后有时间在来完善。
接下来平差就开始表演:
上述方程视为观测方程,误差方程如下:

在这里插入图片描述
在这里插入图片描述为待求转换参量。
代码实现:

		 /* 此函数用于求取空间面七参数 未用到坐标重心化
          * 输入:po是源坐标,pt是目标坐标
          * 输出:resultMat是结果矩阵数组
          *     第一个矩阵 X      是所求的七参数
          *     第二个矩阵 V      是改正数,可以算内符合精度
          *     第三个矩阵 Sigma2 是验后单位权中误差的平方
          */
        public static Matrix[] Get_Seven(Point[] po, Point[] pt)
        {
            int n = po.Length;
            int r = 3 * n - 7;//多余观测数
            Matrix[] resultMat = new Matrix[3];
            Matrix B = new Matrix(3 * n, 7);
            Matrix L = new Matrix(3 * n, 1);
            for (int i = 0; i < n; i++)
            {
                B[3 * i, 0] = 1;
                B[3 * i, 1] = 0;
                B[3 * i, 2] = 0;
                B[3 * i, 3] = 0;
                B[3 * i, 4] = -po[i].z;
                B[3 * i, 5] = po[i].y;
                B[3 * i, 6] = po[i].x;
                B[3 * i + 1, 0] = 0;
                B[3 * i + 1, 1] = 1;
                B[3 * i + 1, 2] = 0;
                B[3 * i + 1, 3] = po[i].z;
                B[3 * i + 1, 4] = 0;
                B[3 * i + 1, 5] = -po[i].x;
                B[3 * i + 1, 6] = po[i].y;
                B[3 * i + 2, 0] = 0;
                B[3 * i + 2, 1] = 0;
                B[3 * i + 2, 2] = 1;
                B[3 * i + 2, 3] = -po[i].y;
                B[3 * i + 2, 4] = po[i].x;
                B[3 * i + 2, 5] = 0;
                B[3 * i + 2, 6] = po[i].z;
                L[3 * i, 0] = pt[i].x;
                L[3 * i + 1, 0] = pt[i].y;
                L[3 * i + 2, 0] = pt[i].z;
            }
            Matrix Nbb = B.Transpose() * B;
            Nbb.InvertGaussJordan();
            Matrix X = Nbb * B.Transpose() * L;
            Matrix V = B * X - L;
            Matrix Sigma2 = V.Transpose() * V;//验后单位权中误差
            resultMat[0] = X;
            resultMat[1] = V;
            resultMat[2] = Sigma2;
            return resultMat;            
        }

从源坐标转换为目标坐标的代码:

		 /* 此函数用于七参数坐标转换,对应函数为 Get_Seven
          * 输入:po是源坐标; M 是用七参数求取函数得到的矩阵数组
          * 输出:pt是目标坐标
          */
        public static Point[] zbTrans7(Point[] po, Matrix[] M)
        {
            int n=po.Length;
            Point[] pt = new Point[n];
            Matrix X = M[0];
            Matrix B = new Matrix(3, 7);
            Matrix Pt;
            for (int i = 0; i < n; i++)
            {
                B[0, 0] = 1;
                B[0, 1] = 0;
                B[0, 2] = 0;
                B[0, 3] = 0;
                B[0, 4] = -po[i].z;
                B[0, 5] = po[i].y;
                B[0, 6] = po[i].x;
                B[0 + 1, 0] = 0;
                B[0 + 1, 1] = 1;
                B[1, 2] = 0;
                B[1, 3] = po[i].z;
                B[1, 4] = 0;
                B[1, 5] = -po[i].x;
                B[1, 6] = po[i].y;
                B[2, 0] = 0;
                B[2, 1] = 0;
                B[2, 2] = 1;
                B[2, 3] = -po[i].y;
                B[2, 4] = po[i].x;
                B[2, 5] = 0;
                B[2, 6] = po[i].z;
                Pt = B * X;
                pt[i] = new Point(Pt[0, 0], Pt[1, 0], Pt[2, 0]);
            }
            return pt;
        }

其所依附的类有:

点类:https://blog.csdn.net/Gou_Hailong/article/details/88989274
矩阵类:https://blog.csdn.net/Gou_Hailong/article/details/98451032

上一篇:-- 回到 主博客 下一篇:曲线道路坐标计算

【注1】其中的代码也许并不完整,您可以作为伪码参看,或者您可以去我主博客逛逛,也许有意外之喜!
【注2】此篇博客是 C# 编程笔记 的子博客。
  • 9
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流浪猪头拯救地球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值