一、distances.h 整体概述
这个头文件定义了PCL中常用的距离计算函数,主要包括:
- 线段相关距离计算 (lineToLineSegment, sqrPointToLineDistance)
- 最大距离段查找 (getMaxSegment)
- 欧氏距离计算 (euclideanDistance, squaredEuclideanDistance)
这些函数是PCL许多高级算法的基础,如分割、配准、特征提取等。
二、线到线的最短线段计算
2.1 函数原型
PCL_EXPORTS void
lineToLineSegment (const Eigen::VectorXf &line_a,
const Eigen::VectorXf &line_b,
Eigen::Vector4f &pt1_seg,
Eigen::Vector4f &pt2_seg);
2.2 功能说明
计算两条三维直线之间的最短线段,返回该线段的两个端点。
参数解析:
line_a: 第一条直线的系数(点+方向),格式为[px, py, pz, dx, dy, dz]line_b: 第二条直线的系数(点+方向),格式相同pt1_seg: 输出的线段第一个端点(在line_a上)pt2_seg: 输出的线段第二个端点(在line_b上)
2.3 数学原理
两条空间直线的最短距离问题:
- 直线A: P = P₀ + t·d₀
- 直线B: Q = Q₀ + s·d₁
- 最短距离的线段垂直于两条直线
2.4 完整使用示例
#include <pcl/common/distances.h>
#include <Eigen/Core>
#include <iostream>
int main()
{
// 定义第一条直线: 过点(0,0,0), 方向为(1,0,0)
Eigen::VectorXf line_a(6);
line_a << 0.0f, 0.0f, 0.0f, // 直线上的点
1.0f, 0.0f, 0.0f; // 方向向量
// 定义第二条直线: 过点(0,1,1), 方向为(0,1,0)
Eigen::VectorXf line_b(6);
line_b << 0.0f, 1.0f, 1.0f, // 直线上的点
0.0f, 1.0f, 0.0f; // 方向向量
// 存储结果的端点
Eigen::Vector4f pt1_seg, pt2_seg;
// 计算最短线段
pcl::lineToLineSegment(line_a, line_b, pt1_seg, pt2_seg);
std::cout << "最短线段端点1 (在line_a上): "
<< pt1_seg.transpose() << std::endl;
std::cout << "最短线段端点2 (在line_b上): "
<< pt2_seg.transpose() << std::endl;
// 计算最短距离
float min_distance = (pt1_seg - pt2_seg).norm();
std::cout << "两直线间的最短距离: " << min_distance << std::endl;
return 0;
}
2.5 在PCL中的应用场景
- 直线拟合后的距离评估: 在RANSAC直线拟合中评估两个候选直线模型
- 线特征匹配: 比较两个点云中提取的线特征
- 机器人路径规划: 计算两条轨迹之间的最小间隙
三、点到线的距离计算
3.1 第一个重载版本
double inline
sqrPointToLineDistance (const Eigen::Vector4f &pt,
const Eigen::Vector4f &line_pt,
const Eigen::Vector4f &line_dir)
功能: 计算点到直线的平方距离(自动计算方向向量的模)
数学公式:
D² = ||line_dir × (line_pt - pt)||² / ||line_dir||²
参数说明:
pt: 待测点(确保pt[3] = 0或1)line_pt: 直线上的一点(重要: line_pt[3]必须为0)line_dir: 直线的方向向量
3.2 第二个重载版本(优化版)
double inline
sqrPointToLineDistance (const Eigen::Vector4f &pt,
const Eigen::Vector4f &line_pt,
const Eigen::Vector4f &line_dir,
const double sqr_length)
功能: 计算点到直线的平方距离(使用预计算的方向向量平方模)
优势: 当需要计算多个点到同一直线的距离时,可预先计算sqr_length = line_dir.squaredNorm(),避免重复计算
参数说明:
- 前三个参数同上
sqr_length: 直线方向向量的平方模(预计算值)
3.3 两个重载的对比使用
#include <pcl/common/distances.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <iostream>
#include <chrono>
int main()
{
// 定义一条直线: 过原点,沿X轴方向
Eigen::Vector4f line_pt(0.0f, 0.0f, 0.0f, 0.0f); // 注意第4个分量为0
Eigen::Vector4f line_dir(1.0f, 0.0f, 0.0f, 0.0f);
// 定义测试点
Eigen::Vector4f test_pt(2.0f, 3.0f, 4.0f, 0.0f);
// === 方法1: 基本版本 ===
double dist_sqr_1 = pcl::sqrPointToLineDistance(test_pt, line_pt, line_dir);
std::cout << "点到直线的平方距离 (方法1): " << dist_sqr_1 << std::endl;
std::cout << "点到直线的距离: " << std::sqrt(dist_sqr_1) << std::endl;
// === 方法2: 优化版本(多点计算时使用) ===
// 模拟1000个点到同一直线的距离计算
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
cloud->width = 1000;
cloud->height = 1;
cloud->points.resize(1000);
// 填充随机点
for (auto& pt : cloud->points) {
pt.x = rand() % 100;
pt.y = rand() % 100;
pt.z = rand() % 100;
}
// 预计算方向向量的平方模
double sqr_length = line_dir.squaredNorm();
// 性能对比
auto start1 = std::chrono::high_resolution_clock::now();
for (const auto& pt : cloud->points) {
Eigen::Vector4f point = pt.getVector4fMap();
double d = pcl::sqrPointToLineDistance(point, line_pt, line_dir);
}
auto end1 = std::chrono::high_resolution_clock::now();
auto start2 = std::chrono::high_resolution_clock::now();
for (const auto& pt : cloud->points) {
Eigen::Vector4f point = pt.getVector4fMap();
double d = pcl::sqrPointToLineDistance(point, line_pt, line_dir, sqr_length);
}
auto end2 = std::chrono::high_resolution_clock::now();
std::cout << "\n性能对比 (1000个点):" << std::endl;
std::cout << "方法1耗时: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end1-start1).count()
<< " μs" << std::endl;
std::cout << "方法2耗时: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end2-start2).count()
<< " μs" << std::endl;
return 0;
}
3.4 注意事项与常见错误
⚠️ 重要提醒:
// ❌ 错误用法: line_pt的第4个分量不为0
Eigen::Vector4f line_pt(1.0f, 2.0f, 3.0f, 1.0f); // 错误!
// ✅ 正确用法
Eigen::Vector4f line_pt(1.0f, 2.0f, 3.0f, 0.0f); // 正确
代码注释明确说明:make sure that line_pt[3] = 0 as there are no internal checks!
3.5 在PCL中的应用
这两个函数广泛用于:
- RANSAC平面/直线拟合
// 在pcl::SACSegmentation中评估点到模型的距离
// 伪代码示例
for (each point in inlier_candidates) {
double dist = sqrPointToLineDistance(point, model_line_pt, model_line_dir);
if (dist < threshold_squared)
inliers.push_back(point);
}
- 边缘检测
// 判断点是否在边缘线附近
if (sqrPointToLineDistance(query_pt, edge_line_pt, edge_dir) < tolerance)
is_near_edge = true;
- 点云投影到直线
// 计算最近点位置
Eigen::Vector4f projection = line_pt +
((pt - line_pt).dot(line_dir) / line_dir.squaredNorm()) * line_dir;
四、最大线段查找函数 (getMaxSegment)
这是一组模板函数,用于在点云中找到距离最远的两个点(即点云的"直径")。
4.1 第一个重载版本(处理完整点云)
template <typename PointT> double inline
getMaxSegment (const pcl::PointCloud<PointT> &cloud,
PointT &pmin, PointT &pmax)
功能: 在整个点云中找到距离最远的两个点
参数说明:
cloud: 输入点云pmin: 输出最大线段的一个端点pmax: 输出最大线段的另一个端点- 返回值: 最大线段的长度(欧氏距离)
4.2 第二个重载版本(处理索引子集)
template <typename PointT> double inline
getMaxSegment (const pcl::PointCloud<PointT> &cloud,
const Indices &indices,
PointT &pmin, PointT &pmax)
功能: 在点云的指定索引子集中找到距离最远的两个点
参数说明:
cloud: 输入点云indices: 要处理的点的索引列表pmin,pmax: 输出端点- 返回值: 最大线段的长度
4.3 算法原理分析
// 伪代码展示算法逻辑
max_dist = 最小值
for i in 点集:
for j in 点集(从i开始):
dist = distance(点i, 点j)的平方
if dist > max_dist:
max_dist = dist
记录i和j的索引
return sqrt(max_dist) // 返回实际距离而非平方距离
时间复杂度: O(n²),其中n是点的数量 空间复杂度: O(1)
4.4 完整使用示例
#include <pcl/common/distances.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <iostream>
// 示例1: 处理完整点云
void example_full_cloud()
{
std::cout << "=== 示例1: 完整点云的最大线段 ===" << std::endl;
// 创建点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 添加一些测试点
cloud->points.push_back(pcl::PointXYZ(0.0f, 0.0f, 0.0f));
cloud->points.push_back(pcl::PointXYZ(1.0f, 0.0f, 0.0f));
cloud->points.push_back(pcl::PointXYZ(0.0f, 1.0f, 0.0f));
cloud->points.push_back(pcl::PointXYZ(5.0f, 5.0f, 5.0f)); // 最远点
cloud->points.push_back(pcl::PointXYZ(-3.0f, -3.0f, -3.0f)); // 另一个最远点
cloud->points.push_back(pcl::PointXYZ(0.5f, 0.5f, 0.5f));
cloud->width = cloud->points.size();
cloud->height = 1;
cloud->is_dense = true;
// 查找最大线段
pcl::PointXYZ pmin, pmax;
double max_length = pcl::getMaxSegment(*cloud, pmin, pmax);
std::cout << "最大线段长度: " << max_length << std::endl;
std::cout << "端点1: (" << pmin.x << ", " << pmin.y << ", " << pmin.z << ")" << std::endl;
std::cout << "端点2: (" << pmax.x << ", " << pmax.y << ", " << pmax.z << ")" << std::endl;
// 验证计算
float dx = pmax.x - pmin.x;
float dy = pmax.y - pmin.y;
float dz = pmax.z - pmin.z;
double verify_dist = std::sqrt(dx*dx + dy*dy + dz*dz);
std::cout << "验证距离: " << verify_dist << std::endl;
}
// 示例2: 使用索引处理点云子集
void example_with_indices()
{
std::cout << "\n=== 示例2: 使用索引的最大线段 ===" << std::endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 创建一个较大的点云
for (int i = 0; i < 20; ++i) {
cloud->points.push_back(pcl::PointXYZ(i * 0.1f, i * 0.1f, 0.0f));
}
cloud->width = cloud->points.size();
cloud->height = 1;
// 只处理部分点(索引5到15)
pcl::Indices indices;
for (int i = 5; i < 15; ++i) {
indices.push_back(i);
}
pcl::PointXYZ pmin, pmax;
double max_length = pcl::getMaxSegment(*cloud, indices, pmin, pmax);
std::cout << "子集中的最大线段长度: " << max_length << std::endl;
std::cout << "端点1: (" << pmin.x << ", " << pmin.y << ", " << pmin.z << ")" << std::endl;
std::cout << "端点2: (" << pmax.x << ", " << pmax.y << ", " << pmax.z << ")" << std::endl;
}
// 示例3: 实际应用 - 计算点云包围盒的对角线
void example_bounding_box_diagonal()
{
std::cout << "\n=== 示例3: 包围盒对角线估计 ===" << std::endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 模拟一个立方体点云
for (float x = 0; x <= 10; x += 1.0f) {
for (float y = 0; y <= 10; y += 1.0f) {
for (float z = 0; z <= 10; z += 1.0f) {
cloud->points.push_back(pcl::PointXYZ(x, y, z));
}
}
}
cloud->width = cloud->points.size();
cloud->height = 1;
pcl::PointXYZ pmin, pmax;
double diagonal = pcl::getMaxSegment(*cloud, pmin, pmax);
std::cout << "点云数量: " << cloud->points.size() << std::endl;
std::cout << "包围盒对角线长度: " << diagonal << std::endl;
std::cout << "理论对角线长度 (√(10²+10²+10²)): "
<< std::sqrt(10*10 + 10*10 + 10*10) << std::endl;
}
int main()
{
example_full_cloud();
example_with_indices();
example_bounding_box_diagonal();
return 0;
}
4.5 特殊情况处理
代码中有一个重要的边界情况处理:
const auto token = std::numeric_limits<std::size_t>::max();
std::size_t i_min = token, i_max = token;
// ... 搜索过程 ...
if (i_min == token || i_max == token)
return (max_dist = std::numeric_limits<double>::min());
这段代码处理什么情况?
- 空点云或只有一个点的情况
- 所有点重合的极端情况
- 返回最小double值作为错误标志
4.6 性能优化技巧
虽然算法是O(n²),但有一些优化策略:
#include <pcl/common/distances.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
// 优化策略1: 下采样后再计算
void optimized_getMaxSegment()
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
// 假设有大量点
// ... 填充cloud ...
// 先下采样减少点数
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(0.01f, 0.01f, 0.01f); // 1cm体素
vg.filter(*cloud_filtered);
// 在下采样后的点云上计算
pcl::PointXYZ pmin, pmax;
double max_length = pcl::getMaxSegment(*cloud_filtered, pmin, pmax);
std::cout << "原始点数: " << cloud->size() << std::endl;
std::cout << "下采样后: " << cloud_filtered->size() << std::endl;
std::cout << "最大线段: " << max_length << std::endl;
}
// 优化策略2: 使用凸包
// 最大线段必然在凸包的顶点之间
void convex_hull_optimization()
{
// 提示: 可以先计算凸包,然后只在凸包顶点上执行getMaxSegment
// 这样可以大幅减少需要比较的点数
std::cout << "提示: 结合pcl::ConvexHull可进一步优化" << std::endl;
}
4.7 在PCL中的实际应用
应用场景1: 自动缩放/归一化
// 将点云归一化到单位立方体
void normalize_point_cloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
pcl::PointXYZ pmin, pmax;
double max_extent = pcl::getMaxSegment(*cloud, pmin, pmax);
// 计算中心点
float cx = (pmin.x + pmax.x) / 2.0f;
float cy = (pmin.y + pmax.y) / 2.0f;
float cz = (pmin.z + pmax.z) / 2.0f;
// 归一化
for (auto& pt : cloud->points) {
pt.x = (pt.x - cx) / max_extent;
pt.y = (pt.y - cy) / max_extent;
pt.z = (pt.z - cz) / max_extent;
}
}
应用场景2: 估计采样分辨率
// 根据点云大小自动设置采样参数
void auto_sampling_resolution(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
pcl::PointXYZ pmin, pmax;
double extent = pcl::getMaxSegment(*cloud, pmin, pmax);
// 根据点云大小设置体素大小(例如1/100的extent)
float voxel_size = extent / 100.0f;
std::cout << "建议的体素大小: " << voxel_size << std::endl;
}
应用场景3: 初始配准估计
// ICP配准前的粗略尺度估计
void estimate_scale_for_registration(
pcl::PointCloud<pcl::PointXYZ>::Ptr source,
pcl::PointCloud<pcl::PointXYZ>::Ptr target)
{
pcl::PointXYZ s_min, s_max, t_min, t_max;
double source_extent = pcl::getMaxSegment(*source, s_min, s_max);
double target_extent = pcl::getMaxSegment(*target, t_min, t_max);
double scale_ratio = target_extent / source_extent;
std::cout << "源点云和目标点云的尺度比: " << scale_ratio << std::endl;
// 可用于调整ICP的距离阈值
double icp_threshold = source_extent * 0.01; // 1%的extent
}
4.8 与其他PCL函数的配合使用
#include <pcl/common/distances.h>
#include <pcl/common/centroid.h>
#include <pcl/common/transforms.h>
// 综合示例: 点云预处理流程
void preprocessing_pipeline(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud)
{
// 1. 计算质心
Eigen::Vector4f centroid;
pcl::compute3DCentroid(*cloud, centroid);
// 2. 计算最大范围
pcl::PointXYZ pmin, pmax;
double max_extent = pcl::getMaxSegment(*cloud, pmin, pmax);
// 3. 中心化并归一化
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.translation() << -centroid[0], -centroid[1], -centroid[2];
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_transformed(new pcl::PointCloud<pcl::PointXYZ>);
pcl::transformPointCloud(*cloud, *cloud_transformed, transform);
// 缩放到单位尺度
for (auto& pt : cloud_transformed->points) {
pt.x /= max_extent;
pt.y /= max_extent;
pt.z /= max_extent;
}
std::cout << "点云已中心化并归一化到单位尺度" << std::endl;
}
五、欧氏距离函数
5.1 平方欧氏距离 (squaredEuclideanDistance)
5.1.1 通用版本(3D点)
template<typename PointType1, typename PointType2> inline float
squaredEuclideanDistance (const PointType1& p1, const PointType2& p2)
{
float diff_x = p2.x - p1.x, diff_y = p2.y - p1.y, diff_z = p2.z - p1.z;
return (diff_x*diff_x + diff_y*diff_y + diff_z*diff_z);
}
特点:
- 模板函数,支持不同点类型之间的距离计算
- 计算3D点的平方距离
- 返回float类型
5.1.2 特化版本(2D点 - PointXY)
template<> inline float
squaredEuclideanDistance (const PointXY& p1, const PointXY& p2)
{
float diff_x = p2.x - p1.x, diff_y = p2.y - p1.y;
return (diff_x*diff_x + diff_y*diff_y);
}
特点:
- 专门为2D点(PointXY)优化的版本
- 只计算x和y方向的距离
- 模板特化(template specialization)
5.2 欧氏距离 (euclideanDistance)
template<typename PointType1, typename PointType2> inline float
euclideanDistance (const PointType1& p1, const PointType2& p2)
{
return (std::sqrt (squaredEuclideanDistance (p1, p2)));
}
特点:
- 基于squaredEuclideanDistance实现
- 返回真实的欧氏距离(开方后)
- 性能略低于平方距离(多了sqrt运算)
5.3 完整使用示例
#include <pcl/common/distances.h>
#include <pcl/point_types.h>
#include <iostream>
#include <vector>
// 示例1: 基本距离计算
void basic_distance_calculation()
{
std::cout << "=== 示例1: 基本距离计算 ===" << std::endl;
// 3D点距离
pcl::PointXYZ p1(1.0f, 2.0f, 3.0f);
pcl::PointXYZ p2(4.0f, 6.0f, 8.0f);
float dist_sqr = pcl::squaredEuclideanDistance(p1, p2);
float dist = pcl::euclideanDistance(p1, p2);
std::cout << "点1: (" << p1.x << ", " << p1.y << ", " << p1.z << ")" << std::endl;
std::cout << "点2: (" << p2.x << ", " << p2.y << ", " << p2.z << ")" << std::endl;
std::cout << "平方距离: " << dist_sqr << std::endl;
std::cout << "欧氏距离: " << dist << std::endl;
// 验证
float dx = p2.x - p1.x;
float dy = p2.y - p1.y;
float dz = p2.z - p1.z;
float verify = std::sqrt(dx*dx + dy*dy + dz*dz);
std::cout << "验证: " << verify << std::endl;
}
// 示例2: 2D点距离(PointXY特化版本)
void point_xy_distance()
{
std::cout << "\n=== 示例2: 2D点距离 ===" << std::endl;
pcl::PointXY p1, p2;
p1.x = 0.0f; p1.y = 0.0f;
p2.x = 3.0f; p2.y = 4.0f;
float dist_sqr = pcl::squaredEuclideanDistance(p1, p2);
float dist = pcl::euclideanDistance(p1, p2);
std::cout << "2D点1: (" << p1.x << ", " << p1.y << ")" << std::endl;
std::cout << "2D点2: (" << p2.x << ", " << p2.y << ")" << std::endl;
std::cout << "平方距离: " << dist_sqr << std::endl;
std::cout << "欧氏距离: " << dist << " (应该是5.0)" << std::endl;
}
// 示例3: 不同点类型之间的距离
void mixed_point_types()
{
std::cout << "\n=== 示例3: 混合点类型 ===" << std::endl;
pcl::PointXYZ p1(1.0f, 2.0f, 3.0f);
pcl::PointXYZRGB p2;
p2.x = 4.0f; p2.y = 5.0f; p2.z = 6.0f;
p2.r = 255; p2.g = 0; p2.b = 0; // 颜色信息不影响距离计算
// 模板函数可以处理不同类型的点
float dist = pcl::euclideanDistance(p1, p2);
std::cout << "PointXYZ 和 PointXYZRGB 之间的距离: " << dist << std::endl;
}
// 示例4: 性能比较 - 何时使用平方距离
void performance_comparison()
{
std::cout << "\n=== 示例4: 性能考虑 ===" << std::endl;
pcl::PointXYZ p1(0.0f, 0.0f, 0.0f);
// 场景: 找到距离阈值内的点
float threshold = 5.0f;
float threshold_sqr = threshold * threshold; // 25.0
std::vector<pcl::PointXYZ> points;
for (int i = 0; i < 10; ++i) {
points.push_back(pcl::PointXYZ(i * 1.0f, i * 0.5f, i * 0.3f));
}
// ✅ 高效方式: 使用平方距离
std::cout << "使用平方距离判断(推荐):" << std::endl;
for (size_t i = 0; i < points.size(); ++i) {
float dist_sqr = pcl::squaredEuclideanDistance(p1, points[i]);
if (dist_sqr < threshold_sqr) {
std::cout << " 点" << i << " 在阈值内, 平方距离=" << dist_sqr << std::endl;
}
}
// ❌ 低效方式: 使用欧氏距离(多余的sqrt)
std::cout << "使用欧氏距离判断(不推荐):" << std::endl;
for (size_t i = 0; i < points.size(); ++i) {
float dist = pcl::euclideanDistance(p1, points[i]);
if (dist < threshold) {
std::cout << " 点" << i << " 在阈值内, 距离=" << dist << std::endl;
}
}
}
int main()
{
basic_distance_calculation();
point_xy_distance();
mixed_point_types();
performance_comparison();
return 0;
}
5.4 何时使用平方距离 vs 欧氏距离
使用平方距离的场景(推荐):
// 1. 距离比较(不需要真实距离值)
if (pcl::squaredEuclideanDistance(p1, p2) < pcl::squaredEuclideanDistance(p1, p3)) {
// p2离p1更近
}
// 2. 阈值判断
float threshold = 0.1f;
float threshold_sqr = threshold * threshold;
if (pcl::squaredEuclideanDistance(query, candidate) < threshold_sqr) {
// 在阈值内
}
// 3. 最近邻搜索
float min_dist_sqr = std::numeric_limits<float>::max();
int nearest_idx = -1;
for (size_t i = 0; i < cloud->size(); ++i) {
float d = pcl::squaredEuclideanDistance(query, cloud->points[i]);
if (d < min_dist_sqr) {
min_dist_sqr = d;
nearest_idx = i;
}
}
使用欧氏距离的场景:
// 1. 需要报告真实距离值
float actual_distance = pcl::euclideanDistance(p1, p2);
std::cout << "两点相距 " << actual_distance << " 米" << std::endl;
// 2. 需要距离的数学运算(如加权)
float weight = 1.0f / pcl::euclideanDistance(query, neighbor);
// 3. 可视化或用户界面显示

1904

被折叠的 条评论
为什么被折叠?



