.ply点云文件转化为.dae并成功在gazebo中加载(有颜色)

1. 点云转化为.dae

因为gazebo并不支持直接加载.ply文件,我们首先转化.ply文件为.dae。并且.dae是携带颜色信息的。如何转化参考这里。How to import PLY/PCD to Gazebo world for simulation? · Issue #13 · ethz-asl/voxblox_ground_truth · GitHub

注意:生成的.dae和_txt.png文件要放在同一个文件夹下,以后要移动也一直放在同一个文件夹下。否则会不显示颜色,这应该是因为.dae已经定义了两个文件的相对位置。

2. 导入.dae到gazebo中

注意这里我们在terminal中使用代码导入。也可以使用python或者c++在脚本中导入。因为我们的项目需要代码和仿真中的模型频繁交互,动态的增加和删除模型,所以我们使用这种方法。即使不需要频繁交互,这种方法也没有问题。

2.1. 通过.world导入

首先写一个.world文件,在文件中包含生成的.dae.如下所示。注意: .world文件需要与生成在plant.dae 和 meshlab中导出的 _tex.png在同一个文件夹下, 否则会找不到模型文件。这种方法是脱离ROS的,如果使用ROS,那么这个路径的定义方法不同,类似这样。<uri>model://tomato/meshes/tomato5.dae</uri>

<?xml version="1.0"?>
<sdf version="1.4">
  <world name="default">
    <include>
      <uri>model://ground_plane</uri>
    </include>
    <include>
      <uri>model://sun</uri>
    </include>
    <model name="my_mesh">
      <pose>0 0 0  0 0 0</pose>
      <static>true</static>
      <link name="body">
        <visual name="visual">
          <geometry>
            <mesh><uri>file://plant0.dae</uri></mesh>
          </geometry>
        </visual>
      </link>
    </model>
  </world>
</sdf>

然后在terminal中运行

gazebo 'full/path/of/world/file.world' 

2.1 通过.sdf导入。

个人更喜欢这种,因为.sdf也可以通过脚本导入。更适合如果你需要用代码删除或者添加模型。如果添加模型后不需要再删除或者添加,那么使用.world应该就足够了。

具体方法类似.首先写一个.sdf文件。比如。

<?xml version="1.0" ?>
<sdf version="1.6">
  <model name="plant_model">
    <static>true</static> <!-- Set to true if the plant is static -->
    <link name="link">
      <visual name="plant0">
        <geometry>
          <mesh><uri>file://plant0.dae</uri></mesh>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

然后运行gazebo

gazebo

然后导入模型

gz model --spawn-file='full/path/of/your/sdf/file.sdf' --model-name=your_model_name

或者删除模型

gz model --delete --model-name your_model_name

注意这种方法是在terminal中运行,当在代码中导入.sdf时,需要不同的语句, python参考这里: ros_gazebo_spawn_models/spawn_urdf_sdf/scripts/spawn_sdf.py at master · roboticist8/ros_gazebo_spawn_models · GitHub

c++参考这里: ros_gazebo_spawn_models/spawn_urdf_sdf/src/spawn_sdf.cpp at master · roboticist8/ros_gazebo_spawn_models · GitHub 

3 Demo

3.1 文件夹结构

附赠一个demo关于如何组织文件夹,以及如何写.sdf脚本来导入模型。注意将meshes文件夹加到系统路径中,保证它能被都搜到。可以参考这里:Gazebo : Tutorial : Color And Texture Models

也有其他较方便的方法,比如修改meshes文件夹所在功能包的package.xml文件,在最后</package>前加上这个:

<export>

<!-- Other tools can request additional information be placed here -->

<gazebo_ros gazebo_model_path="${prefix}/meshes/"/>

</export>

这样,在每次gazebo启动,这个路径会被默认加进去。

关键是建立三个文件夹,(1)meshes:放置你的.dae文件 (2)scripts:放置 .materials 文件 (3)textures: 放置材质 .png文件。scripts文件夹包含 .material文件,描述了材质文件,下边我会放一个例子。

3.2 .sdf脚本

最重要的是如何写你的.sdf文件, 示例如下:

<?xml version="1.0" ?>
<sdf version="1.4">
  <model name='tomato1'>
    <pose>0 0 0 0 0 0</pose>
    <!-- <static>1</static> -->
    <static>0</static>
    <link name='link'>
      <gravity>0</gravity>
      <collision name='collision'>
        <geometry>
          <mesh>
            <uri>model://tomato/meshes/plant0.dae</uri>
          </mesh>
        </geometry>
        <max_contacts>10</max_contacts>
        <surface>
          <contact>
            <ode/>
          </contact>
          <bounce/>
          <friction>
            <ode/>
          </friction>
        </surface>
      </collision>
      <visual name='tomato1'>
        <geometry>
          <mesh>
            <uri>model://tomato/meshes/plant0.dae</uri>
          </mesh>
        </geometry>
        <material>
          <script>
            <uri>model://tomato/materials/scripts</uri>
            <uri>model://tomato/materials/textures</uri>
            <name>plant0</name>
          </script>
        </material>
      </visual>
      <velocity_decay>
        <linear>0</linear>
        <angular>0</angular>
      </velocity_decay>
      <self_collide>0</self_collide>
      <kinematic>0</kinematic>
      <gravity>1</gravity>
    </link>
  </model>
</sdf>

注意这里边所有url的路径都是相对路径, 因为上层路径已经被 explort 到系统路径了。分别对应.dae 文件,scripts文件夹,和 textures文件夹。下边的<name>plant0<>/name>是在.materials 中定义的,如下所示:

material plant0
{
    receive_shadows on
    technique
    {
        pass
        {
            alpha_rejection greater 128
            texture_unit
            {
                texture plant0_tex.png
            }
        }
    }
}

本质上,你可以理解为 .material文件定义了一个映射,将plant0_tex.png材质映射到名字plant0上。然后在.sdf中,你只需要refer到这个名字,就能找到这个.png。

3.3 python 导入.sdf

def spawn_plant(self, plant_idx, plant_rotation, plant_position = [0, 0, 0], print_log = True):
        # Set poses for plants.  use -0.024 if acc_pc is above the ground plan
        # plant_pose = Pose(position=Point(1, 0, 1.15), orientation = Quaternion(*quaternion_from_euler(0, 0, plant_rotation))) #plant_poses saved the poses of all plant 0-10 that you want to create
        
        self.plant_idx = plant_idx
        self.plant_rotation = plant_rotation
        plant_pose = Pose(position=Point(plant_position[0]/100, plant_position[1]/100, plant_position[2]/100), orientation = Quaternion(*quaternion_from_euler(0, 0, plant_rotation))) #plant_poses saved the poses of all plant 0-10 that you want to create
        # self.visual_roi(plant_idx, plant_rotation)
        plant_position = [str(i) for i in plant_position]
        plant_position = ''.join(plant_position)
        rospy.wait_for_service("/gazebo/spawn_sdf_model")
        try:
            spawner = rospy.ServiceProxy("/gazebo/spawn_sdf_model", SpawnModel)  ###many methods to call a service. (1) spawner(para1, para2, para3) (2) spawner.call(para_class)
            
            spawner(

                model_name=self.model_name + str(plant_idx),
                model_xml=open(
                    self.model_path + self.model_name + str(plant_idx) + ".sdf",
                    "r",
                ).read(),
                robot_namespace="/map",
                initial_pose=plant_pose,
                reference_frame="world",
            )
            if print_log:
                print('spawning plants [%s] successfully'% str(self.model_name + str(plant_idx)))
        except rospy.ServiceException as e:
            print("Service call failed: ", e)

这个过程经过本人探索,基本上是使用自定义模型 (结构及颜色复杂),使用自定义texture最简便的方法。有更简便的方法也欢迎交流。

4. 模型只有单面被相机获取

在gazebo中加载的模型通常需要与相机交互,即-模型能够被相机捕捉到。包括RGB,深度, 或者雷达。有时候会出现这样一个问题,模型只有单面能被相机捕捉,而模型另一面相机光线会直接穿透,而不能被捕捉。比如,有一片叶子,相机能从叶子的一个方向看到,而从另一个面就看不到。经过多次测试,终于找到一个方法。这个问题主要是三维模型的画法造成的。比如有人画薄的东西 喜欢用一个面,经过多次弯折, 比如叶子。这就造成一个问题。面只有一个法线方向,而gazebo中只有法线射出的方向能够被观测到。比如画一个球,法线方向从球的每一个三角面朝外。那么从球外的每一个角度都能看到这个球,因为法线方向总是朝外。而进入球的内部,看不到球,因为法线方向朝外。解决方法也很简单,就是给面加一个厚度。下边提供一个解决办法。具体使用blender.

具体:

(1) 点击物体->进入编辑模型 (TAB)->全选所有点(A) -> overlays (一个小向下的箭头,如下所示)->face orientation。

会得到如下的模型。会看到,面有红的蓝的。蓝的是朝外的方向(能被相机拍到),红的是朝内的方向(拍不到)。

解决方法是加厚度。右边小扳手,加一个实体化修改器(solidify)。在厚度那里加一个小厚度。

可以看到模型变成了全蓝色,所以从每个角度都能被观察到。最后export 新的 .dae.

### 寻找与 Gazebo 相关的点云数据集 对于涉及 Gazebo 的仿真环境中获取或使用点云数据集的需求,通常有几种方法可以实现这一目标。一种常见的方式是从公开的数据集中下载适合于仿真的点云数据将其集成到 Gazebo 中。 #### 方法一:利用现有的公共点云库 一些知名的点云数据库提供了可以直接用于研究和开发工作的资源。例如 KITTI 数据集、SemanticKITTI 或者 ModelNet40 等,这些数据集包含了丰富的三维物体模型及其对应的标注信息。虽然它们非专门为 Gazebo 设计,但是可以通过适当转换应用于该平台中[^1]。 #### 方法二:通过 ROS 和 PCL 获取实时点云 如果希望获得更贴近实际场景的效果,则可以在基于 ROS (Robot Operating System) 构建的应用程序里运行传感器节点,在虚拟世界内模拟激光雷达或其他类型的扫描仪设备采集周围环境的信息,将得到的结果保存下来作为自定义的数据集合。这部分操作往往涉及到 Point Cloud Library(PCL),这是一个专门处理点云运算的强大工具箱[^2]。 #### 示例代码展示如何从 LIDAR 传感器读取点云将之发布给其他订阅者: ```cpp // C++ code snippet to publish point cloud data using ROS and PCL. #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include <sensor_msgs/PointCloud2.h> void lidarCallback(const sensor_msgs::PointCloud2ConstPtr& input){ pcl::fromROSMsg(*input, *cloud); } int main(int argc, char** argv){ ros::init(argc, argv, "lidar_publisher"); ros::NodeHandle nh; // Subscribe to the topic where LiDAR publishes its raw scans ros::Subscriber sub = nh.subscribe("/velodyne_points", 1, lidarCallback); // Publish processed or original point clouds on a new topic ros::Publisher pub = nh.advertise<sensor_msgs::PointCloud2> ("output_pointcloud", 1); } ``` 为了使上述过程更加简便高效,还可以考虑采用已经封装好的插件或者软件包,比如 `gazebo_ros_pkgs` 提供了一系列接口使得开发者能够轻松地把真实世界的感知能力引入到仿真当中去[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值