.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成功加载自定义物体,需完成以下几个方面的操作: #### 1. 文件格式转换 由于 Gazebo 不支持直接加载 `.ply` 文件,因此需要将其转换为 `.dae` 格式。`.dae` 是一种 COLLADA 文件格式,能够携带颜色和其他材质信息[^1]。 可以通过以下工具或方法进行文件转换: - 使用 Blender 将 `.ply` 转换为 `.dae`。 - 利用 MeshLab 工具处理导出为 `.dae` 格式的模型。 以下是通过命令行使用 `meshlabserver` 的示例脚本: ```bash meshlabserver -i input.ply -o output.dae -m vc vn vt ``` 此命令会将输入的 `.ply` 文件转为带有顶点颜色 (`vc`) 和法线 (`vn`) 的 `.dae` 文件。 #### 2. 配置物体描述文件 (URDF/SDF) 为了使 Gazebo 正确识别和渲染该物体,需要创建对应的 URDF 或 SDF 描述文件。这些文件用于定义物体的几何形状、物理属性以及视觉效果。 下面是一个简单的 SDF 示例,展示如何引入外部 `.dae` 模型作为链接的一部分: ```xml <?xml version="1.0"?> <sdf version="1.6"> <model name="custom_object"> <link name="body"> <visual name="visual"> <geometry> <mesh><uri>file://path/to/output.dae</uri></mesh> </geometry> </visual> <collision name="collision"> <geometry> <mesh><uri>file://path/to/output.dae</uri></mesh> </geometry> </collision> </link> </model> </sdf> ``` 注意 `<uri>` 属性中的路径应指向实际存储位置,确保其相对于 ROS/Gazebo 运行的工作目录有效[^3]。 #### 3. 添加 Gazebo 特定扩展标签 如果希望进一步定制仿真的行为,则可以在上述 XML 结构中加入 `<gazebo>` 块来声明额外参数。例如设定惯性矩阵或者应用特定插件: ```xml <gazebo reference="body"> <mu1>1.0</mu1> <mu2>1.0</mu2> <kp>1e8</kp> <kd>1e4</kd> </gazebo> ``` 以上片段设置了摩擦系数(`mu1`, `mu2`) 及接触刚度(`kp`)与阻尼(`kd`)等动力学特性。 #### 4. 导入插件及测试运行 对于更复杂的交互需求(比如灯光控制),可能还需要开发专属的 Gazebo Plugin 部署到 `/devel/lib/<package_name>/` 下面[^2]。最后,在启动世界之前确认所有依赖资源均已正确安装于工作空间内。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值