第一段代码
从杯子的点云(即点的集合)创建一个碰撞网格几何体。下面是步骤和代码示例:
1. 从 X
, Y
, Z
坐标中提取杯子的顶点数据
2. 使用 collisionMesh
函数将顶点转换为碰撞网格
示例代码:
cupHeight = 0.2;
cupRadius = 0.05;
cupPosition = [-0.5, 0.5, cupHeight/2];
% 创建用于视觉化杯子的点
[X,Y,Z] = cylinder(cupRadius*linspace(0,1,50).^0.125);
% 调整 Z 坐标的比例,使其符合杯子的高度
Z = cupHeight*Z - cupHeight/2;%杯子的中点被放置在Z=0的位置。这样,杯子在Z方向上的范围变成了从-0.1到0.1
% 将杯子的位置平移到指定位置
X = X + cupPosition(1);
Y = Y + cupPosition(2);
Z = Z + cupPosition(3);
% 创建点的数组,用于碰撞网格
vertices = [X(:), Y(:), Z(:)];
% 使用 collisionMesh 函数将点转换为碰撞网格
cupMesh = collisionMesh(vertices);
% 可视化碰撞网格
show(cupMesh);
title('Cup Collision Mesh');
代码解释:
-
提取顶点数据:我们将
X
,Y
,Z
数据点展开为一个N×3
的顶点数组vertices
,其中N
是点的数量。 -
创建碰撞网格:使用
collisionMesh(vertices)
函数将这些顶点转换为凸网格碰撞几何体。 -
可视化:使用
show(cupMesh)
来显示生成的碰撞网格。
vertices = [X(:), Y(:), Z(:)];
将 X
, Y
, Z
三个矩阵中的数据重新组织成一个表示三维空间中顶点(或点)坐标的数组(矩阵)。
1. 矩阵展平 (Flattening):
X(:)
,Y(:)
,Z(:)
通过(:)
运算符将各自的矩阵展平成一个列向量。这意味着,矩阵中的所有元素会按照列优先顺序被排列成一列。- 如果
X
、Y
、Z
的尺寸是m x n
,那么X(:)
、Y(:)
、Z(:)
的尺寸将变成mn x 1
的列向量。
2. 组合成顶点数组:
[X(:), Y(:), Z(:)]
将展平后的X
、Y
、Z
列向量按列拼接在一起,形成一个mn x 3
的矩阵。- 这个矩阵中的每一行代表一个顶点的三维坐标
(x, y, z)
。其中,第i
行的元素分别是X(i)
、Y(i)
、Z(i)
对应的坐标值。
3. 用途:
- 这些顶点可以用于创建三维物体的网格结构,或者进行碰撞检测等计算。本例提供的代码中,
vertices
就表示杯子表面的所有点的坐标,通过这些顶点可以构建杯子的三维形状。
代码示例:
clear; clc; close all;
% 假设 X, Y, Z 是 50 x 2 的矩阵,表示生成圆柱体表面的点
X = [0.1, 0.2; 0.3, 0.4];
Y = [0.5, 0.6; 0.7, 0.8];
Z = [0.9, 1.0; 1.1, 1.2];
% 展平并组合成顶点坐标矩阵
vertices = [X(:), Y(:), Z(:)];
disp(vertices);
% 结果 vertices 将会是:
% vertices =
% 0.1 0.5 0.9
% 0.3 0.7 1.1
% 0.2 0.6 1.0
% 0.4 0.8 1.2
在这个例子中,每一行代表一个点的 (x, y, z)
坐标,这些点可以用于表示一个三维物体的几何形状,例如杯子的表面。
第二段代码
- 使用
surf2patch
函数将曲面数据转换为网格数据(顶点和面)。 - 使用
collisionMesh
函数创建碰撞网格。 - 可视化碰撞体以验证结果。
代码示例:
% 清除环境
clear; clc; close all;
% 定义杯子的参数
cupHeight = 0.2; % 杯子高度
cupRadius = 0.05; % 杯子半径
cupPosition = [-0.5, 0.5, cupHeight/2]; % 杯子的位置
% 创建用于视觉化杯子的点
theta = linspace(0, 2*pi, 21); % 角度分布
r = cupRadius * (linspace(0, 1, 50).^0.125); % 半径分布,创造出杯子形状
[Theta, R] = meshgrid(theta, r);
X = R .* cos(Theta);
Y = R .* sin(Theta);
Z = linspace(-cupHeight/2, cupHeight/2, 50)';
Z = repmat(Z, 1, size(X,2));
% 平移杯子到指定位置
X = X + cupPosition(1);
Y = Y + cupPosition(2);
Z = Z + cupPosition(3);
% 将曲面数据转换为网格数据(顶点和面)
[F,V] = surf2patch(X,Y,Z,'triangles');
% 创建碰撞网格
cupMesh = collisionMesh(V);
% 可视化碰撞网格
figure;
show(cupMesh);
title('杯子碰撞体');
xlabel('X');
ylabel('Y');
zlabel('Z');
axis equal;
grid on;
代码解释:
-
定义杯子参数:
cupHeight
:定义杯子的高度。cupRadius
:定义杯子的半径。cupPosition
:定义杯子在空间中的位置。
-
创建杯子的曲面数据:
- 使用参数化的方法生成杯子的表面,其中半径随高度变化,可以通过调整指数(此处为
0.125
)控制杯子的形状。 - 使用
meshgrid
生成二维网格,然后计算对应的 X、Y、Z 坐标。
- 使用参数化的方法生成杯子的表面,其中半径随高度变化,可以通过调整指数(此处为
-
转换为网格数据:
- 使用
surf2patch
函数将曲面数据转换为网格数据,得到顶点 (V
) 和面 (F
)。 - 指定
'triangles'
参数以确保生成的面是三角形,这对于碰撞检测更稳定。
- 使用
-
创建碰撞网格:
- 使用
collisionMesh
函数并传入顶点数据V
,创建杯子的碰撞体。 - 注意:
collisionMesh
需要一个凸包,因此如果杯子的形状是非凸的,可能需要进一步处理。
- 使用
-
可视化碰撞网格:
- 使用
show
函数可视化创建的碰撞体,以验证结果是否符合预期。 - 设置标题和坐标轴标签,并使用
axis equal
保持比例一致。
- 使用
Z = repmat(Z, 1, size(X,2));
通过复制和扩展矩阵 Z
,使其与矩阵 X
在列数上匹配。
-
repmat
函数:repmat
是 MATLAB 中用于复制和重复数组的函数,其语法为B = repmat(A, m, n)
,其中A
是原始矩阵,m
和n
分别表示在行和列方向上复制的次数。repmat
返回一个新的矩阵B
,其大小为[m*size(A,1), n*size(A,2)]
。
-
解释
Z = repmat(Z, 1, size(X,2));
:
Z
是一个列向量或矩阵,最初可能是一个m x 1
的矩阵。size(X,2)
返回矩阵X
的列数。如果X
是一个m x n
的矩阵,那么size(X,2)
就是n
。repmat(Z, 1, size(X,2))
的作用是将Z
沿列方向复制n
次(即size(X,2)
次),从而生成一个新的矩阵,该矩阵的大小为m x n
。
- 示例:
假设 Z
是一个 50x1
的列向量,表示某些数据的高度值。X
是一个 50x21
的矩阵,表示某个曲面(例如圆柱体表面)的 X
坐标。
Z = linspace(-0.1, 0.1, 50)'; % Z 是一个 50x1 的列向量
X = rand(50, 21); % X 是一个 50x21 的矩阵
执行 Z = repmat(Z, 1, size(X,2));
后,Z
将变成一个 50x21
的矩阵,其中原来的 Z
列向量被重复复制了 21
次(size(X,2)
的值),使得 Z
的列数与 X
匹配。
- 为什么要这样做:
在许多情况下,为了在计算或绘图时对矩阵进行操作,需要两个矩阵的大小相匹配(即它们的行数和列数相同)。通过 repmat
将 Z
的列数扩展到与 X
一致,可以在后续的操作中,将 Z
矩阵与 X
矩阵一起使用,比如在 surf
、mesh
或 plot3
等函数中用于三维绘图。
注意事项:
-
凸性要求:
collisionMesh
函数要求输入的网格是凸的。如果杯子的形状存在凹陷,可能需要使用凸包算法(例如convhull
)处理顶点数据,确保生成的碰撞体是凸的。% 如果需要生成凸包 K = convhull(V); cupMesh = collisionMesh(V(K,:));
-
性能考虑: 如果顶点数量较多,可能会影响碰撞检测的性能。可以通过减少采样点或简化模型来提升性能。
-
进一步应用: 创建的碰撞体可以用于机器人运动规划、物理仿真等领域,结合 MATLAB 的其他工具箱实现复杂的应用。
区别说明:
这两段代码的目的是生成一个杯子的三维模型并为其创建碰撞网格,不过它们在实现方式上有一些关键区别。
第一段代码的特点:
-
使用
cylinder
函数:- 第一段代码使用了 MATLAB 的
cylinder
函数来生成杯子的表面点。cylinder
函数生成了一个大小为50x21
的矩阵,这对应于 50 个高度切片,每个切片上有 21 个点。这个方法相对简单,并且直接利用 MATLAB 的内置函数来生成圆柱体表面。
- 第一段代码使用了 MATLAB 的
-
vertices
的生成:- 通过
[X(:), Y(:), Z(:)]
生成vertices
,这将X
、Y
、Z
矩阵展平成列向量并组合成一个1050x3
的矩阵,其中每一行是一个顶点的(x, y, z)
坐标。这个vertices
矩阵可以用于进一步的碰撞检测。
- 通过
-
碰撞网格创建:
- 使用
collisionMesh(vertices)
创建碰撞网格。vertices
是一个展平后的顶点矩阵。
- 使用
第二段代码的特点:
-
手动创建圆柱体表面:
- 第二段代码通过
linspace
和meshgrid
手动生成了杯子的表面点。它通过参数化的角度theta
和半径r
来生成X
和Y
坐标,同时通过linspace
生成Z
坐标。 Z
坐标使用repmat
扩展,使其与X
和Y
的列数匹配。结果是X
和Y
矩阵的大小为50x21
,Z
也扩展为50x21
。
- 第二段代码通过
-
使用
surf2patch
生成网格:- 该段代码使用
surf2patch
函数将生成的曲面数据 (X
,Y
,Z
) 转换为面片 (F
) 和顶点 (V
) 数据。V
是一个n x 3
的矩阵,其中n
是所有顶点的数量。
- 该段代码使用
-
碰撞网格创建:
- 使用
collisionMesh(V)
来生成碰撞网格。这里的V
是从surf2patch
中得到的顶点数据,包含杯子表面的所有顶点。
- 使用
关键区别:
-
生成表面点的方式:
- 第一段代码使用
cylinder
生成圆柱体,这是一种更简单和直接的方式。 - 第二段代码手动创建表面点,通过
meshgrid
和linspace
生成的表面点更加灵活,但代码相对复杂。
- 第一段代码使用
-
网格数据的生成:
- 第一段代码没有明确生成面片数据,而是直接将展平后的顶点用于碰撞网格的创建。
- 第二段代码生成了面片数据 (
F
) 和顶点数据 (V
),提供了更细致的几何信息,适合更复杂的形状和处理。
-
结果差异:
- 尽管两段代码的目标相同,生成的顶点数据 (
vertices
和V
) 可能会由于计算和生成方法的不同而略有差异。第二段代码通过手动控制生成的细节,可能会在浮点精度上略有不同。
- 尽管两段代码的目标相同,生成的顶点数据 (
顶点比较的意义:
比较 V1
和 V2
(以及 vertices
)的目的是检查这两种生成方法是否在实际生成的几何形状上有差异。由于浮点数精度的原因,即使是理论上应该相等的点,在计算机中也可能会有细微的差异,这就是为什么需要使用容差进行比较的原因。
clear; clc; close all;
cupHeight = 0.2;
cupRadius = 0.05;
cupPosition = [-0.5, 0.5, cupHeight/2];
% 创建用于视觉化杯子的点
[X,Y,Z] = cylinder(cupRadius*linspace(0,1,50).^0.125);
% 调整 Z 坐标的比例,使其符合杯子的高度
Z = cupHeight*Z - cupHeight/2;%杯子的中点被放置在Z=0的位置。这样,杯子在Z方向上的范围变成了从-0.1到0.1
% 将杯子的位置平移到指定位置
X = X + cupPosition(1);
Y = Y + cupPosition(2);
Z = Z + cupPosition(3);
% 创建点的数组,用于碰撞网格
vertices = [X(:), Y(:), Z(:)];%矩阵中的所有元素会按照列优先顺序被排列成一列
[~,V1] = surf2patch(X,Y,Z,'triangles');
% isEqual1 = (vertices == V1);
% if isEqual1
% disp('V1 和 vertices 的点是相同的。');
% else
% disp('V1 和 vertices 的点不同。');
% end
% 确认维度是否相同
if isequal(size(V1), size(vertices))
% 逐元素比较,考虑浮点数误差
tolerance = 1e-10; % 设置容差值
difference = abs(V1 - vertices); % 计算差异
isEqual1 = all(difference(:) < tolerance); % 判断所有差异是否都在容差范围内
if isEqual1
disp('V1 和 vertices 的点是相同的。');
else
disp('V1 和 vertices 的点不同。');
end
else
disp('V1 和 vertices 的维度不同,因此它们不相同。');
end
% 创建用于视觉化杯子的点
theta = linspace(0, 2*pi, 21); % 角度分布
r = cupRadius * (linspace(0, 1, 50).^0.125); % 半径分布,创造出杯子形状
[Theta, R] = meshgrid(theta, r);
X2 = R .* cos(Theta);
Y2 = R .* sin(Theta);
Z2 = linspace(-cupHeight/2, cupHeight/2, 50)';
Z2 = repmat(Z2, 1, size(X2,2));
% 平移杯子到指定位置
X2 = X2 + cupPosition(1);
Y2 = Y2 + cupPosition(2);
Z2 = Z2 + cupPosition(3);
% 将曲面数据转换为网格数据(顶点和面)
[F2,V2] = surf2patch(X2,Y2,Z2,'triangles');
% isEqual2 = (V2 == V1);% 在浮点数比较时使用容差范围,而不是直接比较两个数值是否完全相等
% if isEqual2
% disp('V1 和 V2 的点是相同的。');
% else
% disp('V1 和 V2 的点不同。');
% end
% 确认维度是否相同
if isequal(size(V1), size(V2))
% 逐元素比较,考虑浮点数误差
tolerance = 1e-10; % 设置容差值
difference = abs(V1 - V2); % 计算差异
isEqual = all(difference(:) < tolerance); % 判断所有差异是否都在容差范围内
if isEqual
disp('V1 和 V2 的点是相同的。');
else
disp('V1 和 V2 的点不同。');
end
else
disp('V1 和 V2 的维度不同,因此它们不相同。');
end
if isequal(size(V2), size(vertices))
% 逐元素比较,考虑浮点数误差
tolerance = 1e-10; % 设置容差值
difference = abs(V2 - vertices); % 计算差异
isEqual1 = all(difference(:) < tolerance); % 判断所有差异是否都在容差范围内
if isEqual1
disp('V2 和 vertices 的点是相同的。');
else
disp('V2 和 vertices 的点不同。');
end
else
disp('V2 和 vertices 的维度不同,因此它们不相同。');
end
a = 0.1 + 0.2;
b = 0.3;
isEqual_test = (a == b) % 直接比较,可能不准确
difference = abs(a - b) < 1e-10 % 使用容差比较
通过上述解释,可以理解这两段代码虽然在最终视觉效果上可能非常相似,但在实现细节和生成数据的精确度上存在不同。
collisionMesh
是 MATLAB 中用来创建凸网格碰撞几何体的工具。
它可以根据一组三维顶点创建碰撞几何体,这在机器人学、仿真以及碰撞检测等应用中非常有用。
创建方式
语法
MSH = collisionMesh(Vertices)
MSH = collisionMesh(___,Pose=pose)
描述
-
MSH = collisionMesh(Vertices):从一组三维顶点列表
Vertices
创建一个凸网格碰撞几何体。顶点相对于指定的坐标系(通常是碰撞几何体的坐标系)给定。默认情况下,碰撞几何体的坐标系与世界坐标系重合。 -
MSH = collisionMesh(___,Pose=pose):设置网格的姿态
Pose
,相对于世界坐标系。Pose
属性可以在创建碰撞几何体时指定。
在 MATLAB 文档中,___
是一个占位符,表示前面语法中的输入参数。也就是说,MSH = collisionMesh(___,Pose=pose)
这一语法中的 ___
表示你可以在此处插入前面介绍过的必需参数或可选参数,然后再添加 Pose=pose
参数来设置网格的姿态。
具体来说,这里的 ___
可以是你创建 collisionMesh
对象时所需的其他参数。例如,最基本的形式是:
MSH = collisionMesh(Vertices)
在这个基础上,如果你还想指定姿态 (Pose
),就可以这样写:
MSH = collisionMesh(Vertices, Pose=pose)
在这个语法中:
Vertices
是你指定的顶点数据,用于定义网格的几何形状。Pose=pose
用于设置网格在世界坐标系中的姿态。
所以,___
代表的是前面提到的 Vertices
这个参数,表示你必须首先指定网格的顶点,然后才可以选择性地指定 Pose
。
属性
-
Vertices:网格的顶点,指定为一个 N×3 的数组,其中 N 是顶点的数量。每一行表示三维空间中的一个点的坐标。注意,有些点可能在构建的凸网格内部。
数据类型:
double
-
Pose:碰撞几何体相对于世界坐标系的姿态,指定为一个 4×4 的齐次矩阵或
se3
对象。可以在创建几何体之后更改姿态。数据类型:
single
|double
对象函数
- show:显示碰撞几何体。
- fitCollisionCapsule:在碰撞几何体周围拟合一个碰撞胶囊体。
示例
-
创建并可视化网格碰撞几何体
- 创建一个包含十个随机选择的单位球面上点的数组。为了确保结果可重复,将随机种子设置为默认值。
rng default n = 10; pts = zeros(n,3); for k = 1:n ph = 2*pi*rand(1); th = pi*rand(1); pts(k,:) = [cos(th)*sin(ph) sin(th)*sin(ph) cos(ph)]; end
- 从数组创建凸网格碰撞几何体并可视化。
m = collisionMesh(pts); show(m)
-
使用更多的点生成网格
- 创建一个包含1000个随机选择的单位球面上点的数组,并可视化生成的网格碰撞几何体。
n = 1000; pts2 = zeros(n,3); for k = 1:n ph = 2*pi*rand(1); th = pi*rand(1); pts2(k,:) = [cos(th)*sin(ph) sin(th)*sin(ph) cos(ph)]; end m2 = collisionMesh(pts2); show(m2)
-
创建包含立方体顶点的数组
- 创建一个立方体的八个角点的数组,并将这些点添加到先前的点数组中。生成并可视化新的网格碰撞几何体。
cubeCorners = [-2 -2 -2 ; -2 2 -2 ; 2 -2 -2 ; 2 2 -2 ;... -2 -2 2 ; -2 2 2 ; 2 -2 2 ; 2 2 2]; pts3 = [pts2;cubeCorners]; m3 = collisionMesh(pts3); show(m3)