DLR-RM / BlenderProc

A procedural Blender pipeline for photorealistic training image generation
GNU General Public License v3.0
2.77k stars 448 forks source link

[BUG]:RuntimeError: MeshObject obj_000003 has no rigid_body component enabled #705

Closed hetolin closed 2 years ago

hetolin commented 2 years ago

Describe the bug A clear and concise description of what the bug is.

Error: Python: Traceback (most recent call last):
  File "/home/ubuntu/dev/blenderproc/main_tless_random.py", line 151, in <module>
    obj.disable_rigidbody()
  File "/home/ubuntu/dev/BlenderProc/blenderproc/python/types/MeshObjectUtility.py", line 283, in disable_rigidbody
    raise RuntimeError(f"MeshObject {self.get_name()} has no rigid_body component enabled")
RuntimeError: MeshObject obj_000003 has no rigid_body component enabled

General Information

  1. Which BlenderProc version are you using? 2.4.1, Blender3.2.1

  2. On which operating system are you? ubuntu16.04

  3. Have you checked the issue tracker to see if a similar issue has been opened? no similar issue

  4. Have you changed BlenderProc in any way besides the config file? If yes, are you sure that this change does not affect the problem you are having? no chang

To Reproduce Steps to reproduce the behavior:

  1. Provide the full command you used to run BlenderProc: blenderproc run main_tless_random.py ./resources/datasets/ resources/cctextures/ ./output --num_scenes=20

  2. Provide the full python file, you used:

    
    import blenderproc as bproc
    import argparse
    import os
    import numpy as np

parser = argparse.ArgumentParser() parser.add_argument('bop_parent_path', help="Path to the bop datasets parent directory") parser.add_argument('cc_textures_path', default="resources/cctextures", help="Path to downloaded cc textures") parser.add_argument('output_dir', help="Path to where the final files will be saved ") parser.add_argument('--num_scenes', type=int, default=2000, help="How many scenes with 25 images each to generate") args = parser.parse_args()

bproc.init()

load bop objects into the scene

target_bop_objs = bproc.loader.load_bop_objs(bop_dataset_path = os.path.join(args.bop_parent_path, 'tless'), model_type = 'cad', mm2m = True)

load distractor bop objects

itodd_dist_bop_objs = bproc.loader.load_bop_objs(bop_dataset_path = os.path.join(args.bop_parent_path, 'itodd'), mm2m = True) ycbv_dist_bop_objs = bproc.loader.load_bop_objs(bop_dataset_path = os.path.join(args.bop_parent_path, 'ycbv'), mm2m = True) hb_dist_bop_objs = bproc.loader.load_bop_objs(bop_dataset_path = os.path.join(args.bop_parent_path, 'hb'), mm2m = True)

load BOP datset intrinsics

bproc.loader.load_bop_intrinsics(bop_dataset_path = os.path.join(args.bop_parent_path, 'tless'))

set shading and hide objects

for obj in (target_bop_objs + itodd_dist_bop_objs + ycbv_dist_bop_objs + hb_dist_bop_objs): obj.set_shading_mode('auto') obj.hide(True)

create room

room_planes = [bproc.object.create_primitive('PLANE', scale=[2, 2, 1]), bproc.object.create_primitive('PLANE', scale=[2, 2, 1], location=[0, -2, 2], rotation=[-1.570796, 0, 0]), bproc.object.create_primitive('PLANE', scale=[2, 2, 1], location=[0, 2, 2], rotation=[1.570796, 0, 0]), bproc.object.create_primitive('PLANE', scale=[2, 2, 1], location=[2, 0, 2], rotation=[0, -1.570796, 0]), bproc.object.create_primitive('PLANE', scale=[2, 2, 1], location=[-2, 0, 2], rotation=[0, 1.570796, 0])] for plane in room_planes: plane.enable_rigidbody(False, collision_shape='BOX', mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99)

sample light color and strenght from ceiling

light_plane = bproc.object.create_primitive('PLANE', scale=[3, 3, 1], location=[0, 0, 10]) light_plane.set_name('light_plane') light_plane_material = bproc.material.create('light_material')

sample point light on shell

light_point = bproc.types.Light() light_point.set_energy(100)

load cc_textures

cc_textures = bproc.loader.load_ccmaterials(args.cc_textures_path)

Define a function that samples 6-DoF poses

def sample_pose_func(obj: bproc.types.MeshObject): min = np.random.uniform([-0.3, -0.3, 0.0], [-0.2, -0.2, 0.0]) max = np.random.uniform([0.2, 0.2, 0.4], [0.3, 0.3, 0.6]) obj.set_location(np.random.uniform(min, max)) obj.set_rotation_euler(bproc.sampler.uniformSO3())

activate depth rendering without antialiasing and set amount of samples for color rendering

bproc.renderer.enable_depth_output(activate_antialiasing=False) bproc.renderer.set_max_amount_of_samples(50)

for i in range(args.num_scenes):

# Sample bop objects for a scene
sampled_target_bop_objs = list(np.random.choice(target_bop_objs, size=20))
sampled_distractor_bop_objs = list(np.random.choice(itodd_dist_bop_objs, size=2, replace=False))
sampled_distractor_bop_objs += list(np.random.choice(ycbv_dist_bop_objs, size=2, replace=False))
sampled_distractor_bop_objs += list(np.random.choice(hb_dist_bop_objs, size=2, replace=False))

# Randomize materials and set physics
for obj in (sampled_target_bop_objs + sampled_distractor_bop_objs):        
    mat = obj.get_materials()[0]
    if obj.get_cp("bop_dataset_name") in ['itodd', 'tless']:
        grey_col = np.random.uniform(0.1, 0.9)   
        mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1])        
    mat.set_principled_shader_value("Roughness", np.random.uniform(0, 0.5))
    if obj.get_cp("bop_dataset_name") == 'itodd':  
        mat.set_principled_shader_value("Metallic", np.random.uniform(0.5, 1.0))
    if obj.get_cp("bop_dataset_name") == 'tless':
        mat.set_principled_shader_value("Specular", np.random.uniform(0.3, 1.0))
        mat.set_principled_shader_value("Metallic", np.random.uniform(0, 0.5))
    obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99)
    obj.hide(False)

# Sample two light sources
light_plane_material.make_emissive(emission_strength=np.random.uniform(3,6), 
                                emission_color=np.random.uniform([0.5, 0.5, 0.5, 1.0], [1.0, 1.0, 1.0, 1.0]))  
light_plane.replace_materials(light_plane_material)
light_point.set_color(np.random.uniform([0.5,0.5,0.5],[1,1,1]))
location = bproc.sampler.shell(center = [0, 0, 0], radius_min = 1, radius_max = 1.5,
                        elevation_min = 5, elevation_max = 89)
light_point.set_location(location)

# sample CC Texture and assign to room planes
random_cc_texture = np.random.choice(cc_textures)
for plane in room_planes:
    plane.replace_materials(random_cc_texture)

# Sample object poses and check collisions 
bproc.object.sample_poses(objects_to_sample = sampled_target_bop_objs + sampled_distractor_bop_objs,
                        sample_pose_func = sample_pose_func, 
                        max_tries = 1000)

# Physics Positioning
bproc.object.simulate_physics_and_fix_final_poses(min_simulation_time=3,
                                                max_simulation_time=10,
                                                check_object_interval=1,
                                                substeps_per_frame = 20,
                                                solver_iters=25)

# BVH tree used for camera obstacle checks
bop_bvh_tree = bproc.object.create_bvh_tree_multi_objects(sampled_target_bop_objs + sampled_distractor_bop_objs)

cam_poses = 0
while cam_poses < 25:
    # Sample location
    location = bproc.sampler.shell(center = [0, 0, 0],
                            radius_min = 0.65,
                            radius_max = 0.94,
                            elevation_min = 5,
                            elevation_max = 89)
    # Determine point of interest in scene as the object closest to the mean of a subset of objects
    poi = bproc.object.compute_poi(np.random.choice(sampled_target_bop_objs, size=15, replace=False))
    # Compute rotation based on vector going from location towards poi
    rotation_matrix = bproc.camera.rotation_from_forward_vec(poi - location, inplane_rot=np.random.uniform(-3.14159, 3.14159))
    # Add homog cam pose based on location an rotation
    cam2world_matrix = bproc.math.build_transformation_mat(location, rotation_matrix)

    # Check that obstacles are at least 0.3 meter away from the camera and make sure the view interesting enough
    if bproc.camera.perform_obstacle_in_view_check(cam2world_matrix, {"min": 0.3}, bop_bvh_tree):
        # Persist camera pose
        bproc.camera.add_camera_pose(cam2world_matrix, frame=cam_poses)
        cam_poses += 1

# render the whole pipeline
data = bproc.renderer.render()

# Write data in bop format
bproc.writer.write_bop(os.path.join(args.output_dir, 'bop_data'),
                       target_objects = sampled_target_bop_objs,
                       dataset = 'tless',
                       depth_scale = 0.1,
                       depths = data["depth"],
                       colors = data["colors"], 
                       color_file_format = "JPEG",
                       ignore_dist_thres = 10)

for obj in (sampled_target_bop_objs + sampled_distractor_bop_objs):      
    obj.disable_rigidbody()
    obj.hide(True)

3. Provide a link to all 3D models you used, if they are from one of the publicly available supported datasets, provide the name or path so that it is possible to reproduce the error.
`wget https://bop.felk.cvut.cz/media/data/bop_datasets/tless_models.zip`

**Expected behavior**
A clear and concise description of what you expected to happen.
render next scene after finishing the 25 images

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.
themasterlink commented 2 years ago

Hey,

Based on your error: MeshObject obj_000003 has no rigid_body component enabled

It seems that not all objects have a rigid body state. You have to call this function on all objects:

https://github.com/DLR-RM/BlenderProc/blob/adee42bed19a1ea6fa2ea0db90fc700a443cea5f/blenderproc/python/types/MeshObjectUtility.py#L205

You already have done this for the room plane, but you have to do it as well for target_bop_objs + itodd_dist_bop_objs + ycbv_dist_bop_objs + hb_dist_bop_objs.

Best, Max

hetolin commented 2 years ago

Thanks. The code mention above is provided by official Blenproc official example code. And it have called this function (enable_rigidbody) on all objects as follows:

# Randomize materials and set physics
    for obj in (sampled_target_bop_objs + sampled_distractor_bop_objs):        
        mat = obj.get_materials()[0]
        if obj.get_cp("bop_dataset_name") in ['itodd', 'tless']:
            grey_col = np.random.uniform(0.1, 0.9)   
            mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1])        
        mat.set_principled_shader_value("Roughness", np.random.uniform(0, 0.5))
        if obj.get_cp("bop_dataset_name") == 'itodd':  
            mat.set_principled_shader_value("Metallic", np.random.uniform(0.5, 1.0))
        if obj.get_cp("bop_dataset_name") == 'tless':
            mat.set_principled_shader_value("Specular", np.random.uniform(0.3, 1.0))
            mat.set_principled_shader_value("Metallic", np.random.uniform(0, 0.5))
        obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99)
        obj.hide(False)

And it still meets the problem of MeshObject obj_000003 has no rigid_body component enabled

themasterlink commented 2 years ago

Ahh, sorry.

Hmm, that means the hide value is not properly used in the physics.

I wonder why the physics are set inside of the loop? @MartinSmeyer

The issue is that you only call in on a sampled subset and not all objects. Move the enable to the top function and it should work. And then we can figure out why the hide value is ignored.

Best, Max

hetolin commented 2 years ago

Hi, I move the enable to the top function and call it on all objects as follows, but it still throw the same error "MeshObject obj_000004 has no rigid_body component enabled"

#  Randomize materials and set physics

# CHANGE 
# for obj in (sampled_target_bop_objs + sampled_distractor_bop_objs):        

# to
for obj in (target_bop_objs + itodd_dist_bop_objs + ycbv_dist_bop_objs + hb_dist_bop_objs):
        mat = obj.get_materials()[0]
        if obj.get_cp("bop_dataset_name") in ['itodd', 'tless']:
            grey_col = np.random.uniform(0.1, 0.9)   
            mat.set_principled_shader_value("Base Color", [grey_col, grey_col, grey_col, 1])        
        mat.set_principled_shader_value("Roughness", np.random.uniform(0, 0.5))
        if obj.get_cp("bop_dataset_name") == 'itodd':  
            mat.set_principled_shader_value("Metallic", np.random.uniform(0.5, 1.0))
        if obj.get_cp("bop_dataset_name") == 'tless':
            mat.set_principled_shader_value("Specular", np.random.uniform(0.3, 1.0))
            mat.set_principled_shader_value("Metallic", np.random.uniform(0, 0.5))
        obj.enable_rigidbody(True, mass=1.0, friction = 100.0, linear_damping = 0.99, angular_damping = 0.99)
        obj.hide(False)
MartinSmeyer commented 2 years ago

Hello,

To solve your issue, you can keep the original script and set this line:

https://github.com/DLR-RM/BlenderProc/blob/adee42bed19a1ea6fa2ea0db90fc700a443cea5f/examples/datasets/bop_challenge/main_tless_random.py#L66

to

sampled_target_bop_objs = list(np.random.choice(target_bop_objs, size=20, replace = False))

In the T-LESS script only, we sample with replacement and thus get two pointers to the same object. This leads to the error when disabling physics the second time. I will fix it.

I wonder why the physics are set inside of the loop? @MartinSmeyer

Because you only want to activate the physics for the current sampled subset of objects. If they are not used for the current scene, you hide them and disable their rigid body attribute so that they do not interfere in the simulation. Saves quite some loading time.

MartinSmeyer commented 2 years ago

@themasterlink Should we maybe print a warning instead of crashing the program when disabling rigidbody although it was not set?

https://github.com/DLR-RM/BlenderProc/blob/adee42bed19a1ea6fa2ea0db90fc700a443cea5f/blenderproc/python/types/MeshObjectUtility.py#L278-L283

We allow enabling rigidbody multiple times..

hetolin commented 2 years ago

Hello,

To solve your issue, you can keep the original script and set this line:

https://github.com/DLR-RM/BlenderProc/blob/adee42bed19a1ea6fa2ea0db90fc700a443cea5f/examples/datasets/bop_challenge/main_tless_random.py#L66

to

sampled_target_bop_objs = list(np.random.choice(target_bop_objs, size=20, replace = False))

In the T-LESS script only, we sample with replacement and thus get two pointers to the same object. This leads to the error when disabling physics the second time. I will fix it.

I wonder why the physics are set inside of the loop? @MartinSmeyer

Because you only want to activate the physics for the current sampled subset of objects. If they are not used for the current scene, you hide them and disable their rigid body attribute so that they do not interfere in the simulation. Saves quite some loading time.

Thanks for your prompt reply It works.