视觉SLAM实践入门——(16)使用SVD求解ICP

点云配准ICP算法推导,SVD分解 迭代最近点(Iterative Closest Point, ICP)算法可称为点云配准算法的先驱和鼻祖,该算法于上世纪90年代提出。通过学习ICP的设计原理可以深刻的了解点云配准这个数学问题及纯数学形式的解决思路。在学习的过程中我发现网上很少有从零基础讲解ICP算法的(可能默认看这类算法的都有很好的数学功底吧),所以我想写一篇关于ICP算法的详细推导博客,希望通过一篇文章帮大家彻底搞懂ICP的原理。 阅读详情

在理论部分已经总结了使用SVD求解的步骤(视觉SLAM理论入门——(9)视觉里程计之特征点法—ICP_floatinglong的博客-优快云博客):

 

1、求点对的去质心坐标

2、估计旋转矩阵 R

3、计算平移向量 t

书中的问题

这部分书上源码有点不好理解(实际上书中并没有指明参考系,假定以相机1为参考系):

1、求得的 R 不是相机1到相机2的旋转矩阵, 而是它的逆(R^T)

2、求得的 t 不是相机1到相机2的平移向量,而是它的相反数( -t )

结论 — 按照书中的源码,求解时应该是以相机2作为参考系,求相机2到相机1的位姿(前面2d-2d、3d-2d均是以相机1作为参考),本文所附代码以相机1为参考,求解相机1到相机2的位姿

以相机1为参考进行运动估计:

1、对于旋转矩阵,有两种解释

① 按照书中对 w 的定义以及 对 R 的求解公式(认为这是正确的),要以相机1为参考进行求解,需要将下面代码点对的位置交换

W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x, q2[i].y, q2[i].z).transpose();

② 按照比较自然的定义方法, w 取书本定义的 w 的转置,那么求解 R 的时候,也应该取书本中公式的转置,那么按照上面代码对 w 的定义,求解的便是相机1到相机2的旋转矩阵

2、对于平移向量,同样是点对的问题,如果要求相机1到相机2的平移向量,下面代码的计算方法应该改为 t^* = p_2 - R * p_1

  Eigen::Vector3d t_ = Eigen::Vector3d(p1.x, p1.y, p1.z) - R_ * Eigen::Vector3d(p2.x, p2.y, p2.z);

下面贴上我的代码,定义方式与源码有点不一样,大致过程是一样的

#include <iostream>
using namespace std;

#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
using namespace cv;

#include <Eigen/Core>
#include <Eigen/SVD>
#include <Eigen/Dense>


//提取ORB特征并匹配
void findAndMatchOrbFeature(const Mat &img1, const Mat &img2, 
	vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, 
	vector<DMatch> &matches)
{	
	Ptr<FeatureDetector> detector = ORB::create();
	Ptr<DescriptorExtractor> desc = ORB::create();
	Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
	
	//检测角点
	detector->detect(img1, kp1);
	detector->detect(img2, kp2);
	
	//计算描述子
	Mat desc1, desc2;	
	desc->compute(img1, kp1, desc1);
	desc->compute(img2, kp2, desc2);
	
	//匹配点对
	vector<DMatch> allMatches;
	matcher->match(desc1, desc2, allMatches);
	
	//检测误匹配
	double minDist = 10000;
	for(auto m : allMatches)
		if(m.distance < minDist)	minDist = m.distance;
	
	double useDist = max(2*minDist, 30.0);
	for(auto m : allMatches)	
		if(m.distance <= useDist)
			matches.push_back(m);	
}

//将像素坐标转化为归一化坐标
Point2d pixelToNor(const Point2d &p, const Mat & k)
{
	//像素坐标 = k * 归一化坐标
	//xp = fx * xn + cx
	return Point2d((p.x - k.at<double>(0, 2)) / k.at<double>(0, 0), (p.y - k.at<double>(1, 2)) / k.at<double>(1, 1));
}

void poseEstimate3d3d(const vector<Point3d> &p1s3d, const vector<Point3d> &p2s3d, 
	Mat &R, Mat &t)
{
/***********************
    1、求去质心坐标
	2、SVD分解求R
	3、求t
************************/

	Point3d p1(0, 0, 0), p2(0, 0, 0);
	//求质心p1、p2
	for(int i = 0; i < (int)p1s3d.size(); i++)
	{
		p1 += p1s3d[i];
		p2 += p2s3d[i];
	}
	p1 = p1 / (int)(p1s3d.size());
	p2 = p2 / (int)(p2s3d.size());
	
	//使用去质心坐标,求 w
	Eigen::Matrix3d w = Eigen::Matrix3d::Zero();
	for(int i = 0; i < (int)p1s3d.size(); i++)
	{		
		Point3d p_3d = p1s3d[i] - p1;	
		Point3d p3d = p2s3d[i] - p2;
		
		Eigen::Vector3d p_V3d(p_3d.x, p_3d.y, p_3d.z), pV3d(p3d.x, p3d.y, p3d.z);
		w += p_V3d*pV3d.transpose();
	}
	
	//SVD分解求 R ,按照这里 w 的定义方式,R = VU^T
	JacobiSVD<Eigen::Matrix3d> svd(w, ComputeFullU | ComputeFullV );
	Eigen::Matrix3d U = svd.matrixU();
	Eigen::Matrix3d V = svd.matrixV();
	
	Eigen::Matrix3d R_ = V*U.transpose();
		  
	if(R_.determinant() < 0)	//保证det > 0
		R_ = -R_;

	//根据 R ,求t
	t = (Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));	
		
	//重构Mat格式的 R,t
	R = (Mat_<double>(3, 3) << R_(0, 0), R_(0, 1), R_(0, 2),
								R_(1, 0), R_(1, 1), R_(1, 2),
								R_(2, 0), R_(2, 1), R_(2, 2));
	
	Eigen::Vector3d p_(p1.x, p1.y, p1.z),  p(p2.x, p2.y, p2.z);
	Eigen::Vector3d t_ = p - R_*p_;
}

int main(int argc, char **argv)
{
	if(argc != 5)
	{
		cout << "error!" << endl;
		return 0;
	}

	Mat img1 = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	Mat img2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);

	//提取ORB特征、匹配点对
	vector<KeyPoint> kp1, kp2;
	vector<DMatch> matches;
	findAndMatchOrbFeature(img1, img2, kp1, kp2, matches);
	
		
	//读取深度图,获得3d点对,每个像素占据16字节(单通道)
	Mat img3 = imread(argv[3], CV_LOAD_IMAGE_UNCHANGED);
	Mat img4 = imread(argv[4], CV_LOAD_IMAGE_UNCHANGED);	
	Mat k = (Mat_<double>(3, 3) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
	vector<Point3d> p1s3d, p2s3d;
	for(auto m : matches)
	{
		Point2d p12d = kp1[m.queryIdx].pt;
		unsigned short pixel1 = img3.ptr<unsigned short>((int)p12d.y)[(int)p12d.x];
		Point2d p22d = kp2[m.trainIdx].pt;
		unsigned short pixel2 = img4.ptr<unsigned short>((int)p22d.y)[(int)p22d.x];
		
		if(pixel1 == 0 || pixel2 == 0)	continue;
		
		double depth = pixel1 / 5000.0;		
		Point2d pNor2d = pixelToNor(p12d, k);		
		Point3d p3d(pNor2d.x*depth, pNor2d.y*depth, depth);
		p1s3d.push_back(p3d);
		
		depth = pixel2 / 5000.0;
		pNor2d = pixelToNor(p22d, k);		
		p3d = Point3d(pNor2d.x*depth, pNor2d.y*depth, depth);
		p2s3d.push_back(p3d);		
	}
	
	//ICP估计
	Mat R, t;
	poseEstimate3d3d(p1s3d, p2s3d, R, t);
	cout << "R :\n" << R << endl << "t : " << t.t() << endl;;
	
	return 0;
}

ICP的不同解法:SVD分解,高斯牛顿法。代码实现 ICP (Iterative Closest Point,最近迭代)是点云配准中的一个很重要的算法。假设有两个点云P和Q,想要找到一个变换T,使得P=TQ,那么就需要使用到这个算法。 1.最近邻查找 这一步是为Q中的点Qi找到Q中相对应的点。 这个要分情况讨论:比如在RGBD-slam中,特征点的匹配已经为我们找到了P、Q中点的对应关系,那么这一步其实是已经完成了。但是对于两坨点云,没有点和点的对应关系,我们只好先做这样一个假设:离Qi最近的P中的点Pi就是它对应的点,显然这个是不准确的,但是我们可以通过 阅读详情

相关推荐

[学习笔记-SLAM篇]视觉SLAM十四讲ch7

前面的内容基本都是基础内容,从第7讲开始变为实践应用,其实就是具体在SLAM中是如何完成各线程内容的。 按理说是实践部分,但还是坚持不实践呢。 注: 1)学习视频:【高翔】视觉SLAM十四讲。 视觉SLAM十四讲第7讲7.1 理论部分7.2 实践部分 第7讲 7.1 理论部分 7-8讲均是视觉里程计的内容,主要区别在于前端原理不同,分别对应特征点法和直接法。 1) 前面我们得到了运动方程(由SE(3)SE(3)SE(3)描述【讲3】,李代数优化【讲4】)和观测方程(由针孔模型给定【讲5】),其中涉及到

warningm_dm的博客 1844

计算机视觉与OpenCV算法实战(C++/Python双语教学)

课程目的:OpenCV是应用非常广泛的开源视觉处理库,在图像处理、计算机视觉和自动驾驶中有着非常重要的作用。课程设计特色:(课程当前为第一期)1、C++与Python双语教学Python语言是在计算机视觉中应用最多的一种语言,在工作中,深度学习模型的训练基本上都是使用Python语言编写的训练代码。OpenCV在这个过程中用于图像的预处理(例如图像读取、数据增强)和后处理,还可以用于显示处理的结果,功能强大,使用方便。但是在功能的部署的时候,不管是部署在服务端还是PC端,开发语言基本上用的是C++,所以如何有效的使用OpenCV进行模型或者功能的部署尤为重要。C++语言应用的好坏,在面试中可以看出一个面试者的工程实践能力的强弱,两种语言的开发掌握好了可以使工作如虎添翼。2、全模块讲解我出版了一本图书《学习OpenCV4:基于Python的算法实战》,虽然这本书是写的基于Python的算法实战,但是实际上这本书有详细的介绍算法的C++接口,还有一些C++方向的案例,是以Python为主。图书出版的时候就想双语写作,只是限于篇幅没有成行。本课程不仅采用双语教学,更是对C++的每个模块都做讲解,我们知道,很多的书其实只讲imgproc,如果你翻开一本书图像的形态学运算和图像滤波都是作为独立章节讲解的,那么这本书基本上就可以确定是只是讲解了imgproc模块,但是其他的模块在工作中也有很重要的作用。例如:core模块定义了C++的基本数据结构和基本运算(如四则运算);highgui模块是可视化与交互的模块;feature2d是特征点与特征匹配相关算法所在的模块;ml是机器学习相关的模块;dnn是深度学习相关的模块,可以使用OpenCV进行深度学习模型的部署。这些是很多的书和课程都不会讲的。3、讲解细致本课程会从环境搭建开始讲解,环境搭建尤为重要。从我多年的授课经验总结来看,如果只是给了代码,很多的入门用户环境问题处理不好的话,后面的学习很难进行下去,甚至会丧失学习的信心。4、会讲解C++和Python的开发语法问题是入门用户的一大难关,特别是C++语言。大学只是教授了C语言相关的内容,C++很多同学只懂一点皮毛,所以写代码步履维艰,我们在讲解代码的过程中会顺带讲解C++和Python的内容。我们还会讲解编译相关知识,还有库的装载与链接,这些是学校里不会教的,目前也几乎没有课程讲解。5、讲师经验丰富我讲解过C++和OpenCV的多个课程,广受学员好评。我出版过两本图书《深度学习计算机视觉实战》和《学习OpenCV4》,两本书都是细致入微的讲解,主要针对的就是初学者,所以能够很好的处理课程的难易程度。6、讲义准备充分讲义准备的充分细致,标识清楚明确,重点和疑难点突出。

《视觉SLAM十四讲精品总结》8:VO—— 3D-3D:ICP+SVD+BA

一、ICP ICP(Iterative Closest Point)迭代最近点,指代匹配好的两组点云间运动估计问题。 ICP算法是对于X中的每一个点用当前的R和 t在Y中找最近的点(比如用欧式距离),然后这两个点就成了一对了,每个点都有了对应的映射点,用每一对的坐标列一个方程,就得到一系列的方程。 重复迭代运行上述过程,直到均方差误差小于某个阀值。  二、RANSAC  (RANdo...

try_again_later的博客 2936

SVD奇异值分解求解位姿变换矩阵

一、SVD奇异值分解理论推导 SVD奇异值分解求解旋转矩阵和平移矩阵的理论推导可以参考如下博文: 1. SLAM常见问题(四):求解ICP,利用SVD分解得到旋转矩阵 2. 机器学习中的矩阵方法04:SVD 分解 3. SVD分解求变换矩阵(C++版) 4. 利用SVD求得两个对应点集合的旋转矩阵R和转移矩阵t的数学推导 5. 计算两个对应点集之间的旋转矩阵R和转移矩阵T 二、求解注意事项 很多情况下通过SVD分解得到的矩阵并不是最终旋转矩阵的结果,而是反射矩阵,因此需要在最后得到结果后进行处理,下面简单介

TimeRiverForever的博客 4667

视觉SLAM十四讲学习笔记——第七讲 视觉里程计(2)

视觉SLAM的前端也就是视觉里程计实际上要解决的问题是根据匹配的特征点估计相机运动。根据不同的已知条件,选择不同的方法。 1.针对单目相机2D-2D:对极几何 对于单目相机,前后两幅图像之间存在着对极几何约束,我们掌握的信息只有两幅图像中特征点的图像坐标,因此相机运动估计问题变为以下两步: (1)根据匹配点坐标估计本质矩阵E:因为本质矩阵E仅与旋转矩阵R和平移向量t有关,估计本质矩阵的方法包括传统的八点法和RANSAC(随机抽样一致性算法)及其改进算法MASC...

weixin_61294345的博客 2338

SLAM5-视觉里程计的算法

一、视觉里程计的定义 视觉里程计主要是根据相邻图像信息粗略估计出相机的运动,为后端提供初始值,常用的算法有特征点法和直接法 特征点:特征点=关键点+描述子。关键点是指像素坐标,描述子指坐标周围像素的信息。有很多特征点,SIFT特征,SURF特征,ORB特征 SIFT特征:尺度不变特征变换(Scale-invariant feature transform):大致方法是首先搜索所有尺度下图像的位置,通过高斯微分函数来识别潜在的对于尺度和旋转不变的兴趣点,然后在候选位置通过拟合精细的模型来确定位置和尺度,再基于

Summer_star_summer的博客 1664

svd求解点云旋转矩阵方法_【苦逼科研】用SVD求解ICP问题的完整证明

在SLAM和无人驾驶研究过程中,我们经常会求解ICP问题(3D点云之间的匹配问题或者说传感器外参的标定问题),简单地说,就是我们有两组已知的点 和 ,我们需要找到一组参数 来代表(衡量)这两组点之间“最有可能的旋转平移变换关系”,一般来说这组参数 需要使变换前后的误差平方和达到最小,写成标准的数学问题就是: 一种常用的求解ICP问题的方法是SVD(矩阵奇异值分解),SLAM神书《视觉SLA...

weixin_39728909的博客 441

视觉SLAM笔记(39) 求解 ICP

SVD 方法和非线性优化方法求解 ICP

氢键H-H 1万+

视觉SLAM14讲学习笔记-ch7

本博客主要记录学习高翔老师视觉SLAM14讲的总结笔记,若有总结不对的地方,欢迎大家与我讨论。

shuaibidebei的博客 1791

【第21话:定位建图】SLAM点云配准之3D-3D ICP(Iterative Closest Point)方法详解

本文详细介绍了SLAM中点云配准的核心算法ICP(Iterative Closest Point)。ICP通过迭代优化刚体变换(旋转和平移)来对齐点云数据,包含四个关键步骤:最近点搜索、最优变换计算、点云变换和收敛判断。文章推导了ICP的数学原理,分析其优缺点,并指出其在SLAM中的典型应用场景(如帧间配准、局部优化和回环检测)。尽管ICP对初始位置敏感且仅适用于刚性变换,但其高效性使其成为SLAM系统的关键组件。理解ICP有助于掌握点云配准的基本原理和实现方法。

一碗白开水一 1086

视觉SLAM入门 -- 学习笔记 - Part5

2 ORB 特征点 原图见1.png 文件和2.png。 2.1 ORB 提取 ORB 即Oriented FAST 简称。它实际上是FAST 特征再加上一个旋转量。本习题将使用OpenCV 自带的FAST 提取算法,但是你要完成旋转部分的计算。旋转的计算过程描述如下: 在一个小图像块中,先计算质心。质心是指以图像块灰度值作为权重的中心。 在一个小的图像块B 中,定义图像块的矩为: 通过矩可以找到图像块的质心: 连接图像块的几何中心O 与质心C,得到一个方向向量OC⃗\vec{OC}OC,于

~勿据散客~的博客 824

【SLAM】G2OBA优化及ICP的学习实践

1.G2O 在之前的章节我已经讲过了G2O(General Graph Optimization)的相关原理以及应用,这次的BA优化问题呢,我们也可以转化为一个G2O来求解。 G2O(General Graph Optimization)———— 通用图优化。 G2O(General Graph Optimization)的核里带有种类多样的求解器,而它的顶点、边的类型也是多种多样。我们可以自己定义顶点和边。总的来说,如果一个优化问题能够表达成图(顶点与边),那么这个问题就可以用G2O(General G

heroybc的博客 1621

ETL第八章无人售货机零售项目实践

ETL实战项目——无人售货机

m0_52058217的博客 4417

SLAM系列——视觉里程计(特征法)

特征点法 特征点

xiaohu的博客 3280

MIT6.824-lab2D-2022(日志压缩详细讲解)

从paper中去讲解,并实现关于lab2D的代码。

幸平的博客 6035

ADS学习笔记 2. 低噪声放大器设计

ADS学习笔记 2. 低噪声放大器设计。设计了一个中心频率433MHz,增益18.206dB,NF=0.134dB,S11带宽较好的低噪声放大器

u014087103的博客 9506

《视觉 SLAM 十四讲》V2 第 7 讲 视觉里程计1 【如何根据图像 估计 相机运动】【特征点法】

github源码链接V2 文章目录第 7 讲 视觉里程计17.1 特征点法7.1.1 特征点7.1.2 ORB 特征FAST 关键点 ⟹\Longrightarrow⟹ Oriented FASTBRIEF 描述子7.1.3 特征匹配7.2 实践 【Code】本讲 CMakeLists.txt7.2.1 使用 OpenCV 进行 ORB 的特征匹配 【Code】7.2.2 手写 ORB 特征估计 相机运动【相机位姿 估计】 3种情形 【对极几何、ICP、PnP】7.3 2D-2D: 对极几何 单

weixin_46034116的博客 1439

NSX-T 4.1部署搭建

NSX-T4.1 部署搭建

weixin_58250887的博客 564

[slam14讲] 第七讲视觉里程计

@[TOC]视觉里程计 理论 特征点提取与匹配 对极约束 三角测距 PnP ICP 实践 特征点提取 特征点匹配 对极约束(2d-2d) 三角化测距 PnP(3d-2d) EPnP求解位姿 BA优化 ➜ build git:(master) ✗ ./pose_estimation_3d2d ../1.png ../2.png ../1_depth.png [ INFO:0] Initialize OpenCL runtime... -- Max dist : 95.000000 -- Min d

自动驾驶行业学习、交流 538

RK3588 Android12 添加HDMI和喇叭声音通道切换选项

rk3588 android12系统默认接hdmi out时,声音会切换到hdmi out输出,主板声卡喇叭输出会没有声音输出。现在添加一个选项设置hdmi out时,声音可以保持主板声卡喇叭输出。

Android系统开发笔记 4162

120、ISP 驱动架构解析:从 V4L2 请求到 ISP 硬件的配置下发流程

本文以实际案例解析ISP驱动架构的V4L2到硬件配置下发流程。作者通过一个因内存共享导致画面闪烁的bug案例,详细剖析了用户空间参数通过V4L2结构体传递、驱动层拆包与硬件寄存器映射、时序同步机制等关键环节。特别强调深拷贝、dirty flag增量更新、shadow register等实现要点,并给出多路并发隔离、寄存器写入失败等典型问题的排查方法。文章建议开发者深入理解参数到寄存器的转换过程,推荐通过debugfs实时监控寄存器状态,避免过度依赖自动调试工具。全文以实践为导向,对ISP驱动开发具有直接指导

sundong_81的博客 159

视觉SLAM学习——视觉里程计VO

视觉SLAM学习——视觉里程计VO 本文为个人学习视觉slam之后的一些片面的理解,希望能够对大家有所帮助 特征点估计 ORB特征检测 这里仅仅介绍ORB特征点的提取的算法过程: 1、FAST特征点提取(设提取n个角点): 1.1、选取像素p,获得其亮度为Ip 1.2、设置阈值T(如:Ip的 20%) 1.3、以p为中心选取半径为3的圆上的16个像素点 1.4、预测试像素:检测索引为1、5、9、1...

qq_37702859的博客 1126

2026年中兴微电子数字IC设计笔试题带答案

这篇考试试卷涵盖了数字IC设计的关键知识点,主要包含四个部分:单选题(18分)考查RTL仿真、低功耗设计、跨时钟域处理等基础知识;多选题(15分)涉及低功耗技术、异步FIFO设计和工具链使用;代码设计题(25分)要求实现两级同步器和时钟门控电路;简答题(42分)重点考察异步FIFO原理、ASIC设计流程、FPGA时钟处理和时序违例修复方法。试题注重理论与实践结合,既包含概念性知识也强调实际工程能力,全面检验学生对数字IC前端设计核心技术的掌握程度。

Open 118

【SLAM十四讲】第七讲

总结: 特征点 对极几何,用于2D到2D的位姿估计,比如单目初始化中 PNP问题,3D到2D,单目SLAM初始化以后的运行过程就是3D-2D,采用线性解法(DLT)和BA两种。 ICP,3D到3D,一般用于点云匹配过程,但是在RGBD-SLAM中因为可以获取点的3D空间位置(相对于当前相机系的),所以可以。但是相比于激光中的点云匹配,这里是已知点的对应匹配信息的,所以 整个问题变简单了。采用线性解法(SVD)和BA两种。 ...

weixin_42696356的博客 827

Nuxt - 项目打包部署及上线到服务器流程(SSR 服务端渲染)

Nuxt-项目打包部署及上线到服务器流程,详细介绍Nuxt项目上线部署,服务端渲染(ssr)项目及nuxt项目部署过程,nuxt项目打包上线,Nuxt项目的部署教程,NuxtJS项目部署方案,Nuxt如何部署到线上,nuxt项目部署篇,Nuxt项目搭建到Nuxt项目部署,Nuxt项目从开始到部署,详细教程。服务器部署nuxt项目,nuxt服务端部署上线,在服务器上部署nuxt项目,NuxtJS项目——命令和部署。......

🏆 前端领域优质创作者 1万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 视觉SLAM理论入门——(9)视觉里程计之特征点法—ICP
下一篇: 视觉SLAM实践入门——(17)使用非线性优化方法求解ICP
floatinglong
博客等级 码龄9年 90粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值