第四讲、Isaaclab与刚性物体交互
0 前言
官方教程:https://isaac-sim.github.io/IsaacLab/main/source/tutorials/01_assets/run_rigid_object.html
Isaacsim+Isaaclab安装:https://blog.csdn.net/m0_47719040/article/details/146389391?spm=1001.2014.3001.5502
在之前的教程中,我们学习了独立python脚本如何启动Issacsim以及如何在仿真场景中生成不同的对象(prim)。本教程将演示如何创建刚体对象并与之交互。本节将围绕Isaac Lab
中提供的assets.RigidObject
类展开研究。
教程对应的脚本为run_rigid_object.py
在scripts/tutorials/01_assets
目录下。
运行该程序:
- 进入安装 isaac lab 时创建的conda虚拟环境
- 在该环境下进入 isaac sim文件夹中运行
source setup_conda_env.sh
- 终端中输入
python scripts/tutorials/01_assets/run_rigid_object.py
运行你的代码,会在isaac sim中出现4个绿色的锥体。
代码主体部分由三个函数构成:
design_scene()
、run_simulator()
以及main()
,其中main()
作为主函数由design_scene()
和run_simulator()
构成。实际上就是讲整个仿真模拟过程分为两个步骤:
- 场景设计:顾名思义,这部分负责讲所有的对象(prim)添加到场景(stage)中。
- 运行模拟:此部分负责启动模拟器,与场景中的基本对象进行交互,例如改变它们的姿态,以及对他们应用各种复杂指令。
分阶段进行仿真模拟是必要的,因为运行模拟必须在场景设计完成后并重置模拟器才会正常进习惯。重置模拟器后不能再经任何新的对象添加到场景中,但可以通过手柄或键盘与对象进行交互。比如,我们可以通过键盘控制四足机器人前后左右运动。
1 场景设计 design_scene()
构建场景的第一步是向场景中添加一个地平面和一个光源:
# 添加地平面
cfg = sim_utils.GroundPlaneCfg()
cfg.func("/World/defaultGroundPlane", cfg)
# 添加光源
cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.8, 0.8, 0.8))
cfg.func("/World/Light", cfg)
基于assets.RigidObject
向场景中添加多个锥形刚体对象:
前一节中使用下述代码创建刚体对象,通过sim_utils.ConeCfg
指定是锥体,通过rigid_props=sim_utils.RigidBodyPropertiesCfg()
指定刚体属性。
cfg_cone_rigid = sim_utils.ConeCfg(
radius=0.15,
height=0.5,
rigid_props=sim_utils.RigidBodyPropertiesCfg(),
mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
collision_props=sim_utils.CollisionPropertiesCfg(),
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)),
)
cfg_cone_rigid.func(
"/World/Objects/ConeRigid", cfg_cone_rigid, translation=(-0.2, 0.0, 2.0), orientation=(0.5, 0.0, 0.5, 0.0)
)
本节中将生成配置包装到assets.RigidObjectCfg
类中,该类包含对象加载策略、默认初始状态和其他对象信息,将该类实例传递给 assets.RigidObject
类时,会生成对行并再开启仿真时初始化相应的物理属性。
# 创建原点组(Xform 容器),创建四个空变换节点作为父容器
origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
# Prim路径 /World/Origin0 到 /World/Origin3
for i, origin in enumerate(origins):
prim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)
# 创建锥形刚体参数
cone_cfg = RigidObjectCfg(
# 正则表达式 /World/Origin.*/Cone 匹配所有 /World/Origin{i}/Cone 路径
prim_path="/World/Origin.*/Cone", # 正则路径模式
spawn=sim_utils.ConeCfg( # 锥体几何参数
radius=0.1, # 底面半径(米)
height=0.2, # 高度(米)
rigid_props=sim_utils.RigidBodyPropertiesCfg(), # 默认刚体属性
mass_props=sim_utils.MassPropertiesCfg(mass=1.0), # 质量1kg
collision_props=sim_utils.CollisionPropertiesCfg(), # 默认碰撞属性
visual_material=sim_utils.PreviewSurfaceCfg( # 视觉材质
diffuse_color=(0.0, 1.0, 0.0), # 漫反射颜色(RGB绿色)
metallic=0.2 # 金属质感(0-1)
),
),
init_state=RigidObjectCfg.InitialStateCfg(), # 初始状态(默认位姿)
)
# 实例化刚体对象
cone_object = RigidObject(cfg=cone_cfg)
#1 路径匹配:扫描场景中所有匹配 /World/Origin.* 的 Xform 节点
#2 子节点生成:在每个匹配的 Xform 下创建 /Cone 子 Prim
#3 物理绑定:为每个 Cone Prim 添加 PhysX 刚体属性
# /World/Origin0/Cone
# /World/Origin1/Cone
# /World/Origin2/Cone
# /World/Origin3/Cone
# 返回场景信息
scene_entities = {"cone": cone_object}
2 运行模拟循环
循环模拟部分,循环的与刚体进行交互,其中主要包含三个步骤:
- 以固定间隔重置仿真状态
- 步进仿真
- 更新刚体缓冲区 (数据同步,确保获取最新物理状态)
3 重置模拟状态
重置刚体对象prim的模拟状态,需要设置刚体prim的(重置)姿态和速度,两者一起决定了根状态(Root State)。
物理引擎只理解世界坐标系,所以在更新刚体的位置时坐标必须时世界坐标系下的~
物理引擎的坐标系要求:PhysX 等物理引擎仅处理全局坐标系下的刚体状态,忽略 USD 层级中的父节点变换。
# 父 Xform 位置:/World/Origin0 (x=0.25, y=0.25, z=0)
# 锥体局部位置:/World/Origin0/Cone (x=0, y=0, z=0)
# 物理引擎视角:锥体实际位置为 (0.25, 0.25, 0)
从Isaacsim中更直观的看有(Origin0):
assets.RigidObject.data.default_root_state
:获取生成的刚体对象prim的默认根状态。assets.RigidObjectCfg.init_state
:上述默认状态可以从该方法中配置。assets.RigidObject.write_root_pose_to_sim()
:设置刚体对象prim的所需姿态。assets.RigidObject.write_root_velocity_to_sim()
:设置刚体对象prim的所需速度。
"""Runs the simulation loop."""
# 对象提取与初始化
cone_object = entities["cone"] # 从场景字典提取锥体管理器实例
# 仿真时间参数配置
sim_dt = sim.get_physics_dt() # 获取物理步长时间(默认 1/60 秒
sim_time = 0.0 # 累计仿真时间
count = 0 # 步数计数器
# 仿真循环
while simulation_app.is_running(): # 持续运行直到用户停止
# 每250步触发重置
if count % 250 == 0:
# --- 阶段1: 重置参数 ---
sim_time = 0.0 # 重置累计时间
count = 0 # 重置步数计数器
# --- 阶段2: 生成新根状态 ---
root_state = cone_object.data.default_root_state.clone() # 克隆默认状态(包含初始位姿、速度)
# 在原点基础上叠加圆柱形随机偏移
'''
这部分实际上就是把默认的位置转换为圆锥的世界坐标系的位置,实际上就是在初始origins坐标的基础上做了一个随机的偏移。
'''
root_state[:, :3] += origins # origins为各实例父节点的世界坐标
root_state[:, :3] += math_utils.sample_cylinder(
radius=0.1, # 圆柱半径
h_range=(0.25, 0.5), # 高度范围(Z轴)
size=cone_object.num_instances, # 实例数量
device=cone_object.device # 计算设备(CPU/GPU)
)
# --- 阶段3: 写入物理引擎 ---
cone_object.write_root_pose_to_sim(root_state[:, :7]) # 写入位姿(位置 + 四元数)
cone_object.write_root_velocity_to_sim(root_state[:, 7:]) # 写入速度(线速度 + 角速度)
# --- 阶段4: 重置内部状态 ---
cone_object.reset() # 更新对象内部缓冲区
print("----------------------------------------")
print("[INFO]: Resetting object state...")
注:可以修改origins坐标及随机圆柱半径看看结果,以下是我将origins修改为origins = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
的结果,注意这里是刚体,开始仿真后可以看到有的圆锥会被挤走~
4 逐步进行模拟
在开始模拟之前,我们先执行该assets.RigidObject.write_data_to_sim()
方法。该方法会将其他数据(例如外力)写入模拟缓冲区。在本教程中,我们不对刚体施加任何外力,因此该方法并非必需。但为了完整性,我们仍然包含该方法。
# 将其他数据(例如外力)写入模拟缓冲区
cone_object.write_data_to_sim()
# 步进
sim.step()
# 更新参数
sim_time += sim_dt
count += 1
5 更新状态
逐步模拟后,我们更新刚体prim的内部缓冲区,使其与物理引擎的当前状态同步。此操作使用assets.RigidObject.update()
方法完成。
# 更新缓冲区
cone_object.update(sim_dt)
不调用 update() 的后果:
- 状态滞后:读取到的位置/速度比实际物理状态晚 1 帧或多帧
- 控制失效:设置的指令未提交到物理引擎,物体无响应
- 传感器数据过期:相机图像或 LiDAR 点云停留在旧时间戳