123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- #!/usr/bin/env python3
- import rospy
- import numpy as np
- import open3d as o3d
- from scipy.spatial.transform import Rotation as R
- import os
- from sensor_msgs.msg import PointCloud2
- from sensor_msgs.msg import PointField
- folder_path = rospy.get_param("folder_path")
- file_path = os.path.join(folder_path, 'pointcloud.txt')
- file_path_points = os.path.join(folder_path, 'points.txt')
- file_path_down = os.path.join(folder_path, 'pointcloud_down.pcd')
- file_path_octomap = os.path.join(folder_path, 'pointcloud_octomap.pcd')
- #chen############################################################################################
- def cull_pointcloud(data,culling_radius):
- data_retained,data_culled = [],[]
- for i in range(data.shape[0]): # 遍历每一个点 P
- for j in range(len(vectors)): # 遍历每一个向量 u
- A = start_points[j] - culling_radius * (vectors[j] / np.linalg.norm(vectors[j]))
- B = end_points[j] + culling_radius * (vectors[j] / np.linalg.norm(vectors[j]))
- aaa = (np.linalg.norm(data[i] - A) + np.linalg.norm(data[i] - B)) <= (2 * culling_radius + np.linalg.norm(B - A))
- distance = np.linalg.norm(np.cross(vectors[j], data[i]-start_points[j])) / np.linalg.norm(vectors[j])
- # 如果有任何一个距离小于等于10,则标记为不满足条件
- if distance <= culling_radius and aaa:
- # if aaa:
- data_culled.append(data[i])
- break
- # 如果该点对所有向量的距离都大于10,则保留该点
- elif j == len(vectors)-1:
- data_retained.append(data[i])
- data_retained = np.array(data_retained)
- data_culled = np.array(data_culled)
- return data_retained,data_culled
- def visual_data(data):
- # 可视化
- pt_cloud_circular = o3d.geometry.PointCloud()
- pt_cloud_circular.points = o3d.utility.Vector3dVector(data)
- o3d.visualization.draw_geometries([pt_cloud_circular])
- ################################点云降采样################################
- # 从文件中读取焊缝起点和终点,并计算两点构成的向量
- def read_and_calculate_vectors(file_path):
- with open(file_path, 'r') as file:
- # 逐行读取文件内容
- lines = file.readlines()
- start_points,end_points,vectors = [],[],[]
- for line in lines:
- # 去除行尾的换行符并按'/'分割每一行
- points_str = line.strip().split('/')
-
- # 确保每一行都正确地分为两部分
- if len(points_str) == 2:
- point1_str = points_str[0].split(',')
- point2_str = points_str[1].split(',')
-
- # 转换字符串为浮点数列表,构造三维点
- point1 = [float(coord) for coord in point1_str]
- point2 = [float(coord) for coord in point2_str]
- start_points.append(point1)
- end_points.append(point2)
- # 计算向量:向量 = 点2 - 点1
- vector = [p2 - p1 for p1, p2 in zip(point1, point2)]
-
- vectors.append(vector)
-
- return start_points,end_points,vectors
- # 读取文件
- def load_point_cloud_from_binary_txt(file_path):
- with open(file_path, 'rb') as f:
- binary_data = f.read()
- # 将二进制数据转换为 NumPy 数组
- point_cloud = np.frombuffer(binary_data, dtype=np.float64).reshape(-1, 3)
- # 将数组转换为可写状态
- point_cloud = np.copy(point_cloud)
- return point_cloud
- def open_file(file_path):
- if '.txt' in file_path:
- pcd = o3d.io.read_point_cloud(file_path, format='xyz')
- else:
- pcd = o3d.io.read_point_cloud(file_path)
- return pcd
- def save_file(content, file_path, write_ascii=True):
- if '.txt' in file_path:
- np.savetxt(file_path, np.asarray(content.points), fmt='%f %f %f')
- else:
- o3d.io.write_point_cloud(file_path, content, write_ascii=write_ascii)
- def voxel_down_sample(pcd, voxel_size):
- """
- 体素方法点云降采样
- :param pcd: 点云数据
- :param voxel_size: 体素降采样的网格长度
- :return: 降采样点云数据
- """
- return pcd.voxel_down_sample(voxel_size=voxel_size)
- def add_rgb_to_pcd(pcd):
- # 为点云中的每个点指定RGB颜色,这里我们使用红色作为示例
- # RGB值范围为[0, 1],红色可以表示为(1, 0, 0)
- pcd = np.asarray(pcd.points).shape[0]
- colors = np.repeat([[1, 0, 0]], pcd, axis=0) # 创建一个全是红色的颜色数组
- # 将颜色信息添加到点云对象中
- pcd.colors = o3d.utility.Vector3dVector(colors)
- return pcd
- # 降采样
- # pcd = open_file(file_path)
- pcd = o3d.geometry.PointCloud()
- pcd.points = o3d.utility.Vector3dVector(load_point_cloud_from_binary_txt(file_path))
- pcd = pcd.voxel_down_sample(voxel_size = 4)
- save_file(pcd,file_path_down)
- ################################去掉焊缝周围的点云#################################
- # 计算焊缝向量
- start_points,end_points,vectors = np.array(read_and_calculate_vectors(file_path_points))
- # 使用pcread直接读取点云数据
- ptCloud = o3d.io.read_point_cloud(file_path_down)
- data = np.asarray(ptCloud.points)
- culling_radius1 = 20
- culling_radius2 = float(rospy.get_param('culling_radius'))
- # culling_radius2 = 6
- data_retained2,data_culled2 =cull_pointcloud(data,culling_radius2)
- # rospy.loginfo('Point cloud culling is complete')
- #分批剔除
- # data_retained1,data_culled1 =cull_pointcloud(data,culling_radius1)
- # # visual_data(data_culled1)
- # data_retained2,data_culled2 =cull_pointcloud(data_culled1,culling_radius2)
- # visual_data(data_retained2)
- # visual_data(data_culled2)
- ################################变换单位并保存################################
- # 将点云数据的小数点向前移动三位(即乘以1000)
- # data_scaled1 = np.array(data_retained1) / 1000
- data_scaled2 = np.array(data_retained2) / 1000
- # 创建一个新的pointCloud对象来存储缩放后的数据
- ptCloud_scaled1 = o3d.geometry.PointCloud()
- # ptCloud_scaled1.points = o3d.utility.Vector3dVector(data_scaled1)
- ptCloud_scaled2 = o3d.geometry.PointCloud()
- ptCloud_scaled2.points = o3d.utility.Vector3dVector(data_scaled2)
- # 将缩放后的点云数据写入新的 PCD 文件
- # o3d.io.write_point_cloud(file_path_octomap, ptCloud_scaled1, write_ascii=True)
- # 读取保存的点云数据
- # pcd = o3d.io.read_point_cloud(file_path_octomap)
- #####################################################################################################################################################################
- def build_pointcloud2_msg(points):
- msg = PointCloud2()
- msg.header.stamp = rospy.Time(0)
- msg.header.frame_id = "base_link"
- if len(points.shape) == 3:
- msg.height = points.shape[1]
- msg.width = points.shape[0]
- else:
- msg.height = 1
- msg.width = len(points)
- msg.fields = [
- PointField('x', 0, PointField.FLOAT32, 1),
- PointField('y', 4, PointField.FLOAT32, 1),
- PointField('z', 8, PointField.FLOAT32, 1)]
- msg.is_bigendian = False
- msg.point_step = 12
- msg.row_step = msg.point_step * points.shape[0]
- msg.is_dense = False
- msg.data = np.asarray(points, np.float32).tobytes()
- return msg
- def talker():
- pointcloud_topic = rospy.get_param('pointcloud_topic')
- pub1 = rospy.Publisher("/pointcloud/output", PointCloud2, queue_size=10)
- # pub2 = rospy.Publisher("/pointcloud/output2", PointCloud2, queue_size=10)
- rospy.init_node('publish_pointcloud', anonymous=True)
- rate = rospy.Rate(10)
-
- points1 = np.asarray(ptCloud_scaled1.points)
- points2 = np.asarray(ptCloud_scaled2.points)
- # msg1 = build_pointcloud2_msg(points1)
- msg2 = build_pointcloud2_msg(points2)
-
- while not rospy.is_shutdown():
- #机械臂完毕,退出点云发布
- sign_pointcloud = str(rospy.get_param("sign_pointcloud"))
- if sign_pointcloud == "1":
- rospy.set_param("sign_pointcloud",0)
- break
- pub1.publish(msg2)
- # pub2.publish(msg2)
- # print("pointcloud has published,sign_pointcloud ={}".format(sign_pointcloud))
- rate.sleep()
- if __name__ == '__main__':
- talker()
|