Isaac Sim 一百讲(3):USD(上)
从零开始的 Isaac Sim 之路,第一季开始!
前言#
在上一讲中,我们介绍了 Prim 的概念,Isaac Sim 中的一切资产被通过 Prim 以梳状的结构组织起来,并且通过 API 来完成其他属性的赋予。作为新手使用 Isaac Sim 的时候,读者可能常常在创建诸如 Franka 的时候陷入长时间的卡顿,这种由于请求 Isaac Sim 服务器中的资产而陷入的卡顿事实上是一种多余的开销,那么一种想法很自然会出现,如何通过一种更加本地化的方法来管理我的全部资产?USD 是这一切的答案。
USD#
如何理解 USD,可以理解为这就是 Isaac Sim 文件的保存形式,USD 文件和诸如 .obj
或者 .glb
等内容相似,只不过其管理了更多的内容,包括光照、物理等属性,换句话说,事实上 USD 里保存的就是我们上文提到的 Prim 的结构以及全部的属性信息。
当我们默认创建 Franka 的时候,事实上就是在向服务器请求一个 Franka 的模型文件,以 USD 的形式,并且添加到 stage 中。其中 USD 文件包含二进制以及文字形式的全部信息,USDC 则是二进制形式,USDA 为文本的形式,USDZ 则管理一个压缩包。值得一提的是,在 USD 的家族中,事实上只有 Blender 才可以创建 USDZ 文件,而 Isaac Sim 则只能读取,却只能保存为 USDA/C/_ 的格式,这意味着其中的贴图文件使用默认的绝对路径进行管理,在传输给其他人的时候,也就很容易贴图丢失,打开的内容只剩白模。
GLB To USD#
在读者了解 USD 之前,可能更多听说过的还是以 GLB 为典型的模型资产,而这更主要的原因其实在于,以 Objaverse 为首的大规模模型库都是以 .glb
的形式进行保存的。直接通过某种传输的方式,让读者下载本文中提及的模型文件,并且进行进一步的处理(如转为 USD 并添加到场景中)是一件不现实的事情。一方面我们在后续可能会用到很多的资产,而另一方面,我们也很难每次都单独提供这些文件,于是授人以鱼不如授人以渔,我们将首先详解如何获得这些数字资产,并且转换为 USD 文件。
Objaverse#
Objaverse 是一个包含了 800K 模型资产的 dataset,而更进一步,其升级版本 Objaverse-XL 则包含了 10M 的模型资产,这些内容事实上包含大量的脏资产,如质量较低的扫描物体,或者不常出现在现实中的物体及虚拟物体。清洗的部分并非我们的讲解范围,而部分的其他工作可能公布一些 Objaverse 的 subset,这些 subset 的质量更高,并且可能被加上了更多的标注。
首先可以安装 objaverse:
pip install objaverse
bash并且可以通过以下的代码获得一个模型的路径:
import objaverse
print(objaverse.__version__)
uids = objaverse.load_uids()
print(len(uids))
annotations = objaverse.load_annotations(uids)
random_object_uids = random.sample(uids, 10)
import multiprocessing
processes = multiprocessing.cpu_count()
objects = objaverse.load_objects(
uids=random_object_uids,
download_processes=processes
)
print(objects)
python你可以注意到 objects
是一个字典,其中的键是 uid
,而值则是对应的模型的路径,你可以在这个路径下面获得这个模型对应的 glb
文件,或者直接使用shutil
来拷贝:
import os
import shutil
os.makedirs('models', exist_ok=True)
for uid, path in objects.items():
os.makedirs(f'models/{uid}', exist_ok=True)
shutil.copy(path, f'models/{uid}.glb')
python转换脚本#
接下来就是转换的脚本了,这个脚本本身是十分复杂的,但是好在 Isaac Sim 已经给出了实践。你需要安装程序版本的 Isaac Sim,
cd /home/$USER/.local/share/ov/pkg/isaac-sim-4.1.0/standalone_examples/api/omni.kit.asset_converter/
vim asset_usd_converter.py
bash你就可以看到这个程序的基本实现的版本了,直接指定目录之后执行,即可获得 USD 文件了。在这里同样给出一个我自己稍微改了一些内容的版本,具体就是多了一个传参而已:
import argparse
import asyncio
from isaacsim import SimulationApp
import os
from pathlib import Path
from tqdm import tqdm
async def convert(in_file, out_file, load_materials=False):
import omni.kit.asset_converter
def progress_callback(progress, total_steps):
pass
converter_context = omni.kit.asset_converter.AssetConverterContext()
converter_context.ignore_materials = not load_materials
converter_context.use_meter_as_world_unit = True
instance = omni.kit.asset_converter.get_instance()
task = instance.create_converter_task(
in_file, out_file, progress_callback, converter_context
)
success = True
while True:
success = await task.wait_until_finished()
if not success:
await asyncio.sleep(0.1)
else:
break
return success
def asset_convert(args):
supported_file_formats = ["glb", "obj", "fbx"]
for folder in args.folders:
local_asset_output = folder + f"/../{args.dist_folder}"
result = omni.client.create_folder(f"{local_asset_output}")
for folder in args.folders:
print(f"\nConverting folder {folder}...")
(result, models) = omni.client.list(folder)
for i, entry in tqdm(enumerate(models)):
if i >= args.max_models:
print(f"max models ({args.max_models}) reached, exiting conversion")
break
model = str(entry.relative_path)
model_name = os.path.splitext(model)[0]
model_format = (os.path.splitext(model)[1])[1:]
if model_format in supported_file_formats:
input_model_path = folder + "/" + model
converted_model_path = folder + f"/../{args.dist_folder}/" + model_name + ".usd"
if not os.path.exists(converted_model_path):
status = asyncio.get_event_loop().run_until_complete(
convert(input_model_path, converted_model_path, True)
)
if not status:
print(f"ERROR Status is {status}")
print(f"---Added {converted_model_path}")
if __name__ == "__main__":
kit = SimulationApp()
import omni
from omni.isaac.core.utils.extensions import enable_extension
enable_extension("omni.kit.asset_converter")
parser = argparse.ArgumentParser("Convert GLB assets to USD")
parser.add_argument(
"--folders",
type=str,
nargs="+",
default=None,
help="List of folders to convert (space seperated).",
)
parser.add_argument(
"--max-models",
type=int,
default=50,
help="If specified, convert up to `max-models` per folder.",
)
parser.add_argument(
"--load-materials",
action="store_true",
help="If specified, materials will be loaded from meshes",
)
parser.add_argument(
"--dist-folder",
type=str,
default="usd",
help="If specified, converted assets will be placed in this folder.",
)
args, unknown_args = parser.parse_known_args()
dist_folder = Path(args.dist_folder)
dist_folder.mkdir(parents=True, exist_ok=True)
if args.folders is not None:
asset_convert(args)
else:
print(f"No folders specified via --folders argument, exiting")
kit.close()
python在使用的时候可以执行:
python mesh2usd.py --folders /mnt/data/assets/objaverse-glb --max-models 1000000 --load-materials --dist-folder objaverse-usd
bash这里面,就可以创建一个 /mnt/data/assets/objaverse-usd
的文件夹,里面包含所有的 USD 文件了。
添加 USD#
在获得了 USD 之后,我们就可以通过某种方式将这些 USD 文件添加到 Isaac Sim 中,这样我们就不需要再使用一些 CUBE 来作为一个 Demo 的示例了,而是可以真正使用这些模型。