You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
847 lines
29 KiB
847 lines
29 KiB
import open3d as o3d |
|
import numpy as np |
|
import copy |
|
import time |
|
from get_lowest_position_of_z_out import get_lowest_position_of_z_out |
|
|
|
# 对外部提供的获取最低z的接口 |
|
def get_lowest_position_of_center_out2(obj_path): |
|
|
|
total_matrix = np.eye(4) |
|
|
|
mesh_obj = o3d.io.read_triangle_mesh(obj_path) |
|
|
|
voxel_size = 3 |
|
|
|
return get_lowest_position_of_center(mesh_obj, obj_path, total_matrix, voxel_size) |
|
|
|
def calculate_rotation_and_center_of_mass(angle_x, angle_y, angle_z, points): |
|
"""计算某一组旋转角度后的重心""" |
|
# 计算绕X轴、Y轴和Z轴的旋转矩阵 |
|
R_x = np.array([ |
|
[1, 0, 0], |
|
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))], |
|
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))] |
|
]) |
|
|
|
R_y = np.array([ |
|
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))], |
|
[0, 1, 0], |
|
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))] |
|
]) |
|
|
|
R_z = np.array([ |
|
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0], |
|
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0], |
|
[0, 0, 1] |
|
]) |
|
|
|
# 综合旋转矩阵 |
|
R = R_z @ R_y @ R_x |
|
|
|
# 执行旋转 |
|
rotated_points = points @ R.T |
|
|
|
# 计算最小z值 |
|
min_z = np.min(rotated_points[:, 2]) |
|
|
|
# 计算平移向量,将最小Z值平移到0 |
|
translation_vector = np.array([0, 0, -min_z]) |
|
rotated_points += translation_vector |
|
|
|
# 计算重心 |
|
center_of_mass = np.mean(rotated_points, axis=0) |
|
|
|
return center_of_mass[2], angle_x, angle_y, angle_z |
|
|
|
def parallel_rotation4(points, angle_step=4): |
|
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)""" |
|
min_center = float('inf') |
|
|
|
for angle_x in range(-45, 45, angle_step): |
|
for angle_y in range(0, 360, angle_step): |
|
center_z, ax, ay, _ = calculate_rotation_and_center_of_mass(angle_x, angle_y, 0, points) |
|
if center_z < min_center: |
|
min_center = center_z |
|
best_angle_x = ax |
|
best_angle_y = ay |
|
|
|
return (best_angle_x, best_angle_y, 0, min_center) |
|
|
|
import numpy as np |
|
from numba import cuda |
|
import math |
|
|
|
# CUDA核函数:计算所有旋转角度下的重心高度 |
|
@cuda.jit |
|
def compute_centers_kernel(points, centers, angle_x_start, angle_x_step, angle_y_start, angle_y_step, num_x, num_y): |
|
i = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x |
|
j = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y |
|
|
|
if i >= num_x or j >= num_y: |
|
return |
|
|
|
# 获取整数角度值 |
|
angle_x = angle_x_start + i * angle_x_step |
|
angle_y = angle_y_start + j * angle_y_step |
|
|
|
rx = math.radians(float(angle_x)) # 使用 float() 进行转换 |
|
ry = math.radians(float(angle_y)) |
|
rz = 0.0 |
|
|
|
# 计算旋转矩阵 |
|
cos_x = math.cos(rx) |
|
sin_x = math.sin(rx) |
|
cos_y = math.cos(ry) |
|
sin_y = math.sin(ry) |
|
cos_z = math.cos(rz) |
|
sin_z = math.sin(rz) |
|
|
|
# 旋转矩阵: R = R_z * R_y * R_x |
|
R00 = cos_z * cos_y |
|
R01 = cos_z * sin_y * sin_x - sin_z * cos_x |
|
R02 = cos_z * sin_y * cos_x + sin_z * sin_x |
|
R10 = sin_z * cos_y |
|
R11 = sin_z * sin_y * sin_x + cos_z * cos_x |
|
R12 = sin_z * sin_y * cos_x - cos_z * sin_x |
|
R20 = -sin_y |
|
R21 = cos_y * sin_x |
|
R22 = cos_y * cos_x |
|
|
|
n = points.shape[0] |
|
min_z = 1e10 |
|
|
|
# 第一遍:计算最小Z值 |
|
for k in range(n): |
|
x = points[k, 0] |
|
y = points[k, 1] |
|
z = points[k, 2] |
|
|
|
x_rot = R00 * x + R01 * y + R02 * z |
|
y_rot = R10 * x + R11 * y + R12 * z |
|
z_rot = R20 * x + R21 * y + R22 * z |
|
|
|
if z_rot < min_z: |
|
min_z = z_rot |
|
|
|
total_z = 0.0 |
|
# 第二遍:平移并计算Z坐标和 |
|
for k in range(n): |
|
x = points[k, 0] |
|
y = points[k, 1] |
|
z = points[k, 2] |
|
|
|
x_rot = R00 * x + R01 * y + R02 * z |
|
y_rot = R10 * x + R11 * y + R12 * z |
|
z_rot = R20 * x + R21 * y + R22 * z |
|
|
|
z_trans = z_rot - min_z |
|
total_z += z_trans |
|
|
|
center_z = total_z / n |
|
centers[i, j] = center_z |
|
|
|
# CUDA版本的并行旋转计算 |
|
def parallel_rotation4_cuda(points, angle_step=4): |
|
angle_x_start = -45 |
|
angle_x_end = 45 |
|
angle_y_start = 0 |
|
angle_y_end = 360 |
|
|
|
num_x = int((angle_x_end - angle_x_start) / angle_step) |
|
num_y = int((angle_y_end - angle_y_start) / angle_step) |
|
|
|
# 将点云数据复制到GPU |
|
d_points = cuda.to_device(points.astype(np.float32)) |
|
d_centers = cuda.device_array((num_x, num_y), dtype=np.float32) |
|
|
|
# 配置线程块和网格 |
|
threadsperblock = (16, 16) |
|
blockspergrid_x = (num_x + threadsperblock[0] - 1) // threadsperblock[0] |
|
blockspergrid_y = (num_y + threadsperblock[1] - 1) // threadsperblock[1] |
|
blockspergrid = (blockspergrid_x, blockspergrid_y) |
|
|
|
# 启动核函数 |
|
compute_centers_kernel[blockspergrid, threadsperblock]( |
|
d_points, d_centers, angle_x_start, angle_step, angle_y_start, angle_step, num_x, num_y |
|
) |
|
|
|
# 将结果复制回主机 |
|
centers = d_centers.copy_to_host() |
|
|
|
# 找到最小重心值的索引 |
|
min_index = np.argmin(centers) |
|
i = min_index // num_y |
|
j = min_index % num_y |
|
|
|
best_angle_x = angle_x_start + i * angle_step |
|
best_angle_y = angle_y_start + j * angle_step |
|
min_center = centers[i, j] |
|
|
|
return best_angle_x, best_angle_y, 0, min_center |
|
|
|
def read_mesh(obj_path, simple=True): |
|
mesh_obj = o3d.io.read_triangle_mesh(obj_path) |
|
return mesh_obj |
|
|
|
def compute_mesh_center2(vertices): |
|
""" |
|
计算网格质心 |
|
|
|
参数: |
|
vertices: 顶点坐标数组,形状为(N, 3)的NumPy数组或列表 |
|
|
|
返回: |
|
centroid: 质心坐标的NumPy数组 [x, y, z] |
|
""" |
|
if len(vertices) == 0: |
|
raise ValueError("顶点数组不能为空") |
|
|
|
n = len(vertices) # 顶点数量 |
|
# 初始化坐标累加器 |
|
sum_x, sum_y, sum_z = 0.0, 0.0, 0.0 |
|
|
|
start_time1 = time.time() |
|
# 遍历所有顶点累加坐标值 |
|
for vertex in vertices: |
|
sum_x += vertex[0] |
|
sum_y += vertex[1] |
|
sum_z += vertex[2] |
|
print("compute_mesh_center1 time", time.time()-start_time1) |
|
|
|
# 计算各坐标轴的平均值 |
|
centroid = np.array([sum_x / n, sum_y / n, sum_z / n]) |
|
return centroid |
|
|
|
def compute_mesh_center(vertices): |
|
""" |
|
计算网格质心(优化版) |
|
|
|
参数: |
|
vertices: 顶点坐标数组,形状为(N, 3)的NumPy数组或列表 |
|
|
|
返回: |
|
centroid: 质心坐标的NumPy数组 [x, y, z] |
|
""" |
|
if len(vertices) == 0: |
|
raise ValueError("顶点数组不能为空") |
|
|
|
# 确保vertices是NumPy数组 |
|
vertices_np = np.asarray(vertices) |
|
|
|
# 使用NumPy的mean函数直接计算均值(向量化操作) |
|
centroid = np.mean(vertices_np, axis=0) |
|
|
|
return centroid |
|
|
|
def get_lowest_position_of_center_ext3(mesh_obj, obj_path, voxel_size = 3): |
|
|
|
best_angle_x, best_angle_y, best_angle_z, z_mean_min, pcd_transformed= get_lowest_position_of_center2(mesh_obj, obj_path, voxel_size) |
|
|
|
return best_angle_x, best_angle_y, best_angle_z, z_mean_min |
|
|
|
|
|
def get_lowest_position_of_center_ext2(mesh_obj, obj_path, total_matrix, voxel_size): |
|
|
|
# total_matrix, z_mean_min = get_lowest_position_of_center(obj_path, total_matrix, voxel_size) |
|
temp_matrix, z_mean_min = get_lowest_position_of_center(mesh_obj, obj_path, np.eye(4), voxel_size) |
|
# print("temp_matrix=",temp_matrix,voxel_size,mesh_obj) |
|
|
|
total_matrix = temp_matrix @ total_matrix |
|
|
|
return total_matrix, z_mean_min |
|
|
|
def get_lowest_position_of_center_ext(obj_path, total_matrix): |
|
|
|
temp_matrix, z_max = get_lowest_position_of_z_out(obj_path) |
|
|
|
total_matrix = temp_matrix @ total_matrix |
|
|
|
return total_matrix, z_max |
|
|
|
def down_sample(pcd, voxel_size, farthest_sample = False): |
|
original_num = len(pcd.points) |
|
target_samples = 1500 # 1000 |
|
num_samples = min(target_samples, original_num) |
|
|
|
# 第一步:使用体素下采样快速减少点数量 |
|
# voxel_size = 3 |
|
if farthest_sample: |
|
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples) |
|
else: |
|
pcd_voxel = pcd.voxel_down_sample(voxel_size) |
|
down_num = len(pcd_voxel.points) |
|
# print(f"original_num={original_num}, down_num={down_num}") |
|
|
|
# 第二步:仅在必要时进行最远点下采样 |
|
if len(pcd_voxel.points) > target_samples and False: |
|
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples) |
|
else: |
|
pcd_downsampled = pcd_voxel |
|
|
|
return pcd_downsampled |
|
|
|
def get_lowest_position_of_center(mesh_obj, obj_path, total_matrix, voxel_size): |
|
|
|
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}") |
|
start_time1 = time.time() |
|
|
|
vertices = np.asarray(mesh_obj.vertices) |
|
|
|
# 确保网格有顶点 |
|
if len(vertices) == 0: |
|
# raise ValueError(f"Mesh has no vertices: {obj_path}") |
|
print(f"Warning: Mesh has no vertices: {mesh_obj}") |
|
return None |
|
|
|
pcd = o3d.geometry.PointCloud() |
|
pcd.points = o3d.utility.Vector3dVector(vertices) |
|
|
|
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices)) |
|
|
|
# 对点云进行下采样(体素网格法) |
|
#""" |
|
pcd_downsampled = down_sample(pcd, voxel_size) |
|
pcd_downsampled.paint_uniform_color([0, 0, 1]) |
|
|
|
if len(np.asarray(pcd_downsampled.points)) <= 0: |
|
bbox = pcd.get_axis_aligned_bounding_box() |
|
volume = bbox.volume() |
|
|
|
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}") |
|
|
|
# 处理体积为零的情况 |
|
if volume <= 0: |
|
# 计算点云的实际范围 |
|
points = np.asarray(pcd.points) |
|
if len(points) > 0: |
|
min_bound = np.min(points, axis=0) |
|
max_bound = np.max(points, axis=0) |
|
extent = max_bound - min_bound |
|
|
|
# 确保最小维度至少为0.01 |
|
min_dimension = max(0.01, np.min(extent)) |
|
volume = min_dimension ** 3 |
|
else: |
|
volume = 1.0 # 最后的安全回退 |
|
|
|
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}") |
|
|
|
# 安全计算密度 - 防止除零错误 |
|
if len(pcd.points) > 0 and volume > 0: |
|
original_density = len(pcd.points) / volume |
|
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33))) |
|
else: |
|
# 当点数为0或体积为0时使用默认体素大小 |
|
voxel_size = 1.0 # 默认值 |
|
|
|
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}") |
|
|
|
pcd_downsampled = down_sample(pcd, voxel_size) |
|
pcd_downsampled.paint_uniform_color([0, 0, 1]) |
|
|
|
original_num = len(pcd.points) |
|
target_samples = 1000 |
|
num_samples = min(target_samples, original_num) |
|
|
|
# print("get_lowest_position_of_center1 time", time.time()-start_time1) |
|
start_time2 = time.time() |
|
# 确保下采样后有点云 |
|
if len(np.asarray(pcd_downsampled.points)) == 0: |
|
# 使用原始点云作为后备 |
|
pcd_downsampled = pcd |
|
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points") |
|
|
|
points = np.asarray(pcd_downsampled.points) |
|
|
|
# 初始化最小重心Y的值 |
|
min_center_of_mass_y = float('inf') |
|
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0 |
|
best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation4(points, angle_step=3) |
|
|
|
# 使用最佳角度进行旋转并平移obj |
|
pcd_transformed = copy.deepcopy(mesh_obj) |
|
|
|
|
|
# 最佳角度旋转 |
|
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x)) |
|
pcd_transformed.rotate(R_x) |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y)) |
|
pcd_transformed.rotate(R_y) |
|
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z)) |
|
pcd_transformed.rotate(R_z) |
|
|
|
T_x = np.eye(4) |
|
T_x[:3, :3] = R_x |
|
center_point = compute_mesh_center(mesh_obj.vertices) |
|
T_center_to_origin = np.eye(4) |
|
T_center_to_origin[:3, 3] = -center_point |
|
T_origin_to_center = np.eye(4) |
|
T_origin_to_center[:3, 3] = center_point |
|
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin |
|
total_matrix = T_rot_center @ total_matrix |
|
|
|
T_y = np.eye(4) |
|
T_y[:3, :3] = R_y |
|
center_point = compute_mesh_center(mesh_obj.vertices) |
|
T_center_to_origin = np.eye(4) |
|
T_center_to_origin[:3, 3] = -center_point |
|
T_origin_to_center = np.eye(4) |
|
T_origin_to_center[:3, 3] = center_point |
|
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin |
|
total_matrix = T_rot_center @ total_matrix |
|
|
|
T_z = np.eye(4) |
|
T_z[:3, :3] = R_z |
|
center_point = compute_mesh_center(mesh_obj.vertices) |
|
T_center_to_origin = np.eye(4) |
|
T_center_to_origin[:3, 3] = -center_point |
|
T_origin_to_center = np.eye(4) |
|
T_origin_to_center[:3, 3] = center_point |
|
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin |
|
total_matrix = T_rot_center @ total_matrix |
|
|
|
#试着旋转180,让脸朝上 |
|
|
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
T_trans1 = np.eye(4) |
|
T_trans1[:3, 3] = translation_vector |
|
total_matrix = T_trans1 @ total_matrix |
|
|
|
# 计算 z 坐标均值 |
|
vertices = np.asarray(pcd_transformed.vertices) |
|
z_mean1 = np.mean(vertices[:, 2]) |
|
z_max1 = np.max(vertices[:, 2]) |
|
|
|
angle_rad = np.pi |
|
#print("旋转前质心:", pcd_transformed.get_center()) |
|
#print("旋转前点示例:", np.asarray(pcd_transformed.vertices)[:3]) |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad) |
|
centroid = pcd_transformed.get_center() |
|
pcd_transformed.translate(-center_point) |
|
pcd_transformed.rotate(R_y, center=(0, 0, 0)) |
|
pcd_transformed.translate(center_point) |
|
|
|
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
# center_point = aabb.get_center() |
|
center_point = compute_mesh_center(mesh_obj.vertices) |
|
# 构建绕中心点旋转的变换矩阵[3](@ref) |
|
T_center_to_origin = np.eye(4) |
|
T_center_to_origin[:3, 3] = -center_point |
|
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad) |
|
T_rotate = np.eye(4) |
|
T_rotate[:3, :3] = R_y180 |
|
T_origin_to_center = np.eye(4) |
|
T_origin_to_center[:3, 3] = center_point |
|
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin |
|
total_matrix = T_rot_center @ total_matrix |
|
|
|
#print("旋转后质心:", pcd_transformed.get_center()) |
|
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3]) |
|
|
|
# |
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
max_z = np.max(vertices[:, 2]) |
|
# print("min_z1", min_z, obj_path) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),]) |
|
# print("translation_vector1",translation_vector) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
T_trans2 = np.eye(4) |
|
T_trans2[:3, 3] = translation_vector |
|
translation = total_matrix[:3, 3] |
|
# print("translation_vector2",translation_vector) |
|
# print(1,translation) |
|
|
|
total_matrix = T_trans2 @ total_matrix |
|
translation = total_matrix[:3, 3] |
|
# print(2,translation) |
|
|
|
# 计算 z 坐标均值 |
|
vertices = np.asarray(pcd_transformed.vertices) |
|
z_mean2 = np.mean(vertices[:, 2]) |
|
z_max2 = np.max(vertices[:, 2]) |
|
|
|
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}") |
|
|
|
if (z_mean2 > z_mean1): |
|
# if (z_max2 > z_max1): |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad) |
|
centroid = pcd_transformed.get_center() |
|
|
|
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
# center_point = aabb.get_center() |
|
center_point = compute_mesh_center(mesh_obj.vertices) |
|
|
|
pcd_transformed.translate(-center_point) |
|
pcd_transformed.rotate(R_y, center=(0, 0, 0)) |
|
pcd_transformed.translate(center_point) |
|
|
|
T_center_to_origin = np.eye(4) |
|
T_center_to_origin[:3, 3] = -center_point |
|
T_origin_to_center = np.eye(4) |
|
T_origin_to_center[:3, 3] = center_point |
|
# 构建反向旋转矩阵 |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad) |
|
T_rotate_inv = np.eye(4) |
|
T_rotate_inv[:3, :3] = R_y |
|
# 完整的反向绕中心旋转矩阵 |
|
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin |
|
total_matrix = T_rot_center_inv @ total_matrix |
|
|
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
# print("min_z2", min_z, obj_path) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
T_trans3 = np.eye(4) |
|
T_trans3[:3, 3] = translation_vector |
|
total_matrix = T_trans3 @ total_matrix |
|
|
|
# z_mean_min = min(z_mean1, z_mean2) |
|
z_max_min = min(z_max1, z_max2) |
|
|
|
# print("get_lowest_position_of_center2 time", time.time()-start_time2) |
|
return total_matrix, z_max_min |
|
|
|
import requests |
|
import json |
|
import re |
|
|
|
def is_valid_float_string(s): |
|
# 匹配科学计数法或普通小数,允许开头和末尾的空格 |
|
pattern = r'^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$' |
|
return bool(re.match(pattern, s.strip())) |
|
|
|
def safe_convert_to_float(s): |
|
""" |
|
尝试将字符串安全转换为float,处理一些不完整情况。 |
|
""" |
|
s = s.strip().lower() # 去除空格,统一为小写 |
|
|
|
# 处理空字符串或完全非数字的情况 |
|
if not s or s in ['na', 'nan', 'inf', 'null', 'none']: |
|
return None # 或者可以根据需要返回 float('nan') |
|
|
|
# 检查是否为有效的浮点数字符串 |
|
if is_valid_float_string(s): |
|
return float(s) |
|
|
|
# 处理类似 '0.00000000e' 的情况(缺少指数) |
|
if s.endswith('e'): |
|
# 尝试添加指数 '0' -> 'e0' |
|
try: |
|
return float(s + '0') |
|
except ValueError: |
|
pass # 如果添加'e0'后仍然失败,则继续下面的异常处理 |
|
|
|
# 更激进的清理:移除非数字、小数点、负号和指数e以外的所有字符 |
|
# 注意:这可能破坏有特定格式的字符串,慎用 |
|
cleaned_s = re.sub(r'[^\d\.eE-]', '', s) |
|
try: |
|
return float(cleaned_s) |
|
except ValueError: |
|
pass |
|
|
|
# 如果所有尝试都失败,返回None或抛出异常 |
|
return None |
|
|
|
def string_to_matrix(data_string): |
|
""" |
|
将字符串转换为NumPy浮点数矩阵,并处理可能的转换错误。 |
|
""" |
|
# 分割字符串 |
|
lines = data_string.strip().split(';') |
|
matrix = [] |
|
|
|
for line in lines: |
|
num_list = line.split() |
|
float_row = [] |
|
for num in num_list: |
|
# 使用安全转换函数 |
|
value = safe_convert_to_float(num) |
|
if value is None: |
|
# 处理转换失败,例如记录日志、使用NaN代替 |
|
print(f"警告: 无法转换字符串 '{num}',将其替换为NaN。") |
|
value = np.nan # 用NaN标记缺失或无效值 |
|
float_row.append(value) |
|
matrix.append(float_row) |
|
|
|
return np.array(matrix) |
|
|
|
import ast |
|
|
|
def get_lowest_position_of_center_net(printId, total_matrix): |
|
print("get_lowest_position_of_center_net", printId) |
|
|
|
url = f"https://mp.api.suwa3d.com/api/printOrder/infoByPrintId?printId={printId}" |
|
res = requests.get(url) |
|
|
|
datas = res.json()["data"]["layout"] |
|
print("datas=", datas) |
|
|
|
homo_matrix_str = datas.get("homo_matrix") |
|
print("homo_matrix_str=", homo_matrix_str) |
|
|
|
# 1. 去除字符串首尾的方括号 |
|
str_cleaned = homo_matrix_str.strip('[]') |
|
# 2. 按行分割字符串 |
|
rows = str_cleaned.split('\n') |
|
|
|
# 3. 修复:处理每行中的逗号问题 |
|
matrix_list = [] |
|
for row in rows: |
|
if row.strip() == '': |
|
continue |
|
|
|
# 去除行首尾的方括号和空格 |
|
row_cleaned = row.strip(' []') |
|
# 按逗号分割,但过滤掉空字符串 |
|
elements = [elem.strip() for elem in row_cleaned.split(',') if elem.strip() != ''] |
|
|
|
# 进一步清理每个元素:去除可能残留的逗号和方括号 |
|
cleaned_elements = [] |
|
for elem in elements: |
|
# 去除元素中可能存在的逗号、方括号和空格 |
|
elem_cleaned = elem.strip(' ,[]') |
|
if elem_cleaned != '': |
|
cleaned_elements.append(elem_cleaned) |
|
|
|
if cleaned_elements: # 只添加非空行 |
|
matrix_list.append(cleaned_elements) |
|
|
|
print("matrix_list=", matrix_list) |
|
|
|
# 4. 安全地转换为浮点数数组(带错误处理) |
|
try: |
|
reconstructed_matrix = np.array(matrix_list, dtype=float) |
|
except ValueError as e: |
|
print(f"转换矩阵时出错: {e}") |
|
print("尝试逐个元素转换...") |
|
|
|
# 逐个元素转换,便于定位问题元素 |
|
float_matrix = [] |
|
for i, row in enumerate(matrix_list): |
|
float_row = [] |
|
for j, elem in enumerate(row): |
|
try: |
|
# 再次清理元素并转换 |
|
cleaned_elem = elem.strip(' ,') |
|
float_val = float(cleaned_elem) |
|
float_row.append(float_val) |
|
except ValueError as ve: |
|
print(f"无法转换的元素: 行{i}, 列{j}, 值'{elem}', 错误: {ve}") |
|
# 可以选择设置为0或NaN,或者抛出异常 |
|
float_row.append(0.0) # 或者 np.nan |
|
float_matrix.append(float_row) |
|
|
|
reconstructed_matrix = np.array(float_matrix, dtype=float) |
|
|
|
layout_z = datas.get("layout_z", 0) |
|
print("layout_z", layout_z) |
|
|
|
reconstructed_matrix = reconstructed_matrix @ total_matrix |
|
print("reconstructed_matrix=", reconstructed_matrix) |
|
|
|
return reconstructed_matrix, layout_z |
|
|
|
def get_lowest_position_of_center2(mesh_obj, obj_path, voxel_size = 3): |
|
|
|
# mesh_obj = read_mesh(obj_path) |
|
|
|
vertices = np.asarray(mesh_obj.vertices) |
|
|
|
# 确保网格有顶点 |
|
if len(vertices) == 0: |
|
print(f"Warning: Mesh has no vertices: {obj_path}") |
|
return None |
|
|
|
pcd = o3d.geometry.PointCloud() |
|
pcd.points = o3d.utility.Vector3dVector(vertices) |
|
|
|
# 对点云进行下采样(体素网格法 |
|
pcd_downsampled = down_sample(pcd, voxel_size) |
|
pcd_downsampled.paint_uniform_color([0, 0, 1]) |
|
|
|
if len(np.asarray(pcd_downsampled.points)) <= 0: |
|
bbox = pcd.get_axis_aligned_bounding_box() |
|
volume = bbox.volume() |
|
|
|
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}") |
|
|
|
# 处理体积为零的情况 |
|
if volume <= 0: |
|
# 计算点云的实际范围 |
|
points = np.asarray(pcd.points) |
|
if len(points) > 0: |
|
min_bound = np.min(points, axis=0) |
|
max_bound = np.max(points, axis=0) |
|
extent = max_bound - min_bound |
|
|
|
# 确保最小维度至少为0.01 |
|
min_dimension = max(0.01, np.min(extent)) |
|
volume = min_dimension ** 3 |
|
else: |
|
volume = 1.0 # 最后的安全回退 |
|
|
|
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}") |
|
|
|
# 安全计算密度 - 防止除零错误 |
|
if len(pcd.points) > 0 and volume > 0: |
|
original_density = len(pcd.points) / volume |
|
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33))) |
|
else: |
|
# 当点数为0或体积为0时使用默认体素大小 |
|
voxel_size = 1.0 # 默认值 |
|
|
|
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}") |
|
|
|
pcd_downsampled = down_sample(pcd, voxel_size) |
|
pcd_downsampled.paint_uniform_color([0, 0, 1]) |
|
|
|
# 确保下采样后有点云 |
|
if len(np.asarray(pcd_downsampled.points)) == 0: |
|
# 使用原始点云作为后备 |
|
pcd_downsampled = pcd |
|
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points") |
|
|
|
points = np.asarray(pcd_downsampled.points) |
|
|
|
# 初始化最小重心Y的值 |
|
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0 |
|
# 旋转并计算最优角度:绕X、Y、Z轴进行每度的旋转 |
|
best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation4(points, angle_step=3) |
|
# print("best_angle1", best_angle_x, best_angle_y, best_angle_z) |
|
|
|
# 使用最佳角度进行旋转并平移obj |
|
pcd_transformed = copy.deepcopy(mesh_obj) |
|
|
|
# 最佳角度旋转 |
|
""" |
|
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x)) |
|
pcd_transformed.rotate(R_x) |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y)) |
|
pcd_transformed.rotate(R_y) |
|
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z)) |
|
pcd_transformed.rotate(R_z) |
|
#""" |
|
|
|
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
center_point = aabb.get_center() |
|
|
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
# 计算 z 坐标均值 |
|
vertices = np.asarray(pcd_transformed.vertices) |
|
z_mean1 = np.mean(vertices[:, 2]) |
|
|
|
angle_rad = np.pi |
|
|
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad) |
|
|
|
pcd_transformed.translate(-center_point) |
|
pcd_transformed.rotate(R_y, center=(0, 0, 0)) |
|
pcd_transformed.translate(center_point) |
|
best_angle_y += angle_rad / np.pi * 180 |
|
|
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
# 计算 z 坐标均值 |
|
vertices = np.asarray(pcd_transformed.vertices) |
|
z_mean2 = np.mean(vertices[:, 2]) |
|
|
|
# print("z_mean",z_mean1,z_mean2,len(pcd_transformed.vertices),obj_path) |
|
|
|
if z_mean2 > z_mean1: |
|
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad) |
|
|
|
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
center_point = aabb.get_center() |
|
|
|
pcd_transformed.translate(-center_point) |
|
pcd_transformed.rotate(R_y, center=(0, 0, 0)) |
|
pcd_transformed.translate(center_point) |
|
best_angle_y += -angle_rad / np.pi * 180 |
|
|
|
vertices = np.asarray(pcd_transformed.vertices) |
|
# 计算平移向量,将最小Y值平移到0 |
|
min_z = np.min(vertices[:, 2]) |
|
translation_vector = np.array([0,0,-min_z,]) |
|
pcd_transformed.translate(translation_vector) |
|
|
|
z_mean_min = min(z_mean1, z_mean2) |
|
|
|
angle_z_delta = arrange_box_correctly(pcd_transformed, voxel_size) |
|
best_angle_z += angle_z_delta |
|
# print("angle_z_delta", angle_z_delta, best_angle_z) |
|
|
|
return best_angle_x, best_angle_y, best_angle_z, z_mean_min, pcd_transformed |
|
|
|
def arrange_box_correctly(obj_transformed, voxel_size): |
|
|
|
vertices = np.asarray(obj_transformed.vertices) |
|
pcd = o3d.geometry.PointCloud() |
|
pcd.points = o3d.utility.Vector3dVector(vertices) |
|
|
|
# 降采样与特征计算 |
|
pcd_downsampled = down_sample(pcd, voxel_size) |
|
|
|
original_num = len(pcd.points) |
|
target_samples = 1000 |
|
num_samples = min(target_samples, original_num) |
|
|
|
points = np.asarray(pcd_downsampled.points) |
|
cov = np.cov(points.T) |
|
|
|
center = obj_transformed.get_center() |
|
|
|
# 特征分解与方向约束(关键修改点) |
|
eigen_vals, eigen_vecs = np.linalg.eigh(cov) |
|
max_axis = eigen_vecs[:, np.argmax(eigen_vals)] |
|
|
|
# 强制主方向向量X分量为正(指向右侧) |
|
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0): |
|
max_axis = -max_axis |
|
|
|
target_dir = np.array([1, 0]) # 目标方向为X正轴 |
|
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2]) |
|
dot_product = np.dot(current_dir, target_dir) |
|
|
|
# print("dot_product", dot_product) |
|
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9) |
|
max_axis = -max_axis # 强制翻转方向 |
|
|
|
# 计算旋转角度 |
|
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi) |
|
|
|
if max_axis[0] <= 0 and max_axis[1] <= 0: |
|
angle_z += np.pi |
|
|
|
# print("max_axis2", max_axis, -angle_z, np.rad2deg(-angle_z)) |
|
|
|
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z]) |
|
|
|
T = np.eye(4) |
|
T[:3, :3] = R |
|
T[:3, 3] = center - R.dot(center) # 保持中心不变 |
|
obj_transformed.transform(T) |
|
|
|
return np.rad2deg(-angle_z)
|
|
|