本教程将向仿真场景中引入第二个机器人——Franka Panda机械臂,详细讲解如何将其添加到场景中,并演示如何使用其PickAndPlaceController(抓放控制器)。通过本教程的学习,您将积累更多在Omniverse Isaac Sim中使用不同机器人及控制器类的实践经验。
1. 创建场景
# 机械臂
from omni.isaac.examples.base_sample import BaseSample
# This extension has franka related tasks and controllers as well
from omni.isaac.franka import Franka
from omni.isaac.core.objects import DynamicCuboid
import numpy as np
class MyHelloWorld(BaseSample): # 继承自basesample
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.scene.add_default_ground_plane()
# adding a Franka robot
franka = world.scene.add(Franka(prim_path="/World/Fancy_Franka", name="fancy_franka"))
world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0.3, 0.3, 0.3]),
scale=np.array([0.0515, 0.0515, 0.0515]),
color=np.array([0, 0, 1.0]),
)
)
return
2. Using the PickAndPlace Controller
可以看到franka带了3个控制器,我们这里用第一个
# 机械臂
from omni.isaac.examples.base_sample import BaseSample
# This extension has franka related tasks and controllers as well
from omni.isaac.franka import Franka
from omni.isaac.core.objects import DynamicCuboid
from omni.isaac.franka.controllers import PickPlaceController
import numpy as np
class MyHelloWorld(BaseSample): # 继承自basesample
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.scene.add_default_ground_plane()
# adding a Franka robot
franka = world.scene.add(Franka(prim_path="/World/Fancy_Franka", name="fancy_franka"))
world.scene.add(
DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0.3, 0.3, 0.3]),
scale=np.array([0.0515, 0.0515, 0.0515]),
color=np.array([0, 0, 1.0]),
)
)
return
async def setup_post_load(self):
self._world = self.get_world()
self._franka = self._world.scene.get_object("fancy_franka")
self._fancy_cube = self._world.scene.get_object("fancy_cube")
# 初始化自己的控制器
self._controller = PickPlaceController(
name="pick_place_controller",
gripper=self._franka.gripper,
robot_articulation=self._franka,
)
# 设置自动执行的任务--通过回调函数
self._world.add_physics_callback("sim_step", callback_fn=self.physics_step)
# load之后要设置夹爪初始位置,确定仿真启动之后机器人从同一个已知状态开始
self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)
await self._world.play_async()
return
# 重置之后要做的事情
async def setup_post_reset(self):
self._controller.reset()
# 重启之后也要重置夹爪位置/打开夹爪
self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)
await self._world.play_async()
return
def physics_step(self, step_size):
cube_position, _ = self._fancy_cube.get_world_pose()
goal_position = np.array([-0.3, -0.3, 0.0515 / 2.0])
current_joint_positions = self._franka.get_joint_positions()
# 生成action
actions = self._controller.forward(
picking_position=cube_position,
placing_position=goal_position,
current_joint_positions=current_joint_positions,
)
self._franka.apply_action(actions)
if self._controller.is_done():
self._world.pause()
return
def world_cleanup(self):# 释放内存等
return
3. 什么是task
在 Omniverse Isaac Sim 中,Task 类提供了一种模块化的方式,用于管理场景创建、信息检索和指标计算。它适用于构建具有高级逻辑的更复杂场景。在本教程中,你将需要基于 Task 类重构之前的代码。
3.1 定义自己的task
# 自己定义task
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka import Franka
from omni.isaac.core.objects import DynamicCuboid
from omni.isaac.franka.controllers import PickPlaceController
from omni.isaac.core.tasks import BaseTask # 任务基类
import numpy as np
# FrankaPlaying 任务子类
class FrankaPlaying(BaseTask):
#NOTE: 这里只有basetask任务类中的一部分,还有更多可以查看原本实现
def __init__(self, name):
super().__init__(name=name, offset=None)
self._goal_position = np.array([-0.3, -0.3, 0.0515 / 2.0]) # 立方体目标位置
self._task_achieved = False # 任务完成标志
return
# setup all 在任务中的 assets
def set_up_scene(self, scene):
super().set_up_scene(scene)
scene.add_default_ground_plane()
# 创建蓝色立方体 (可移动目标)
self._cube = scene.add(DynamicCuboid(
prim_path="/World/random_cube",
name="fancy_cube",
position=np.array([0.3, 0.3, 0.3]),
scale=np.array([0.0515, 0.0515, 0.0515]),
color=np.array([0, 0, 1.0])
))
# 添加Franka机械臂
self._franka = scene.add(
Franka(
prim_path="/World/Fancy_Franka",
name="fancy_franka"
)
)
return
# 任务中需要的信息通过 get_observations
def get_observations(self):
cube_position, _ = self._cube.get_world_pose() # 获取立方体世界坐标
current_joint_positions = self._franka.get_joint_positions() # 获取机械臂关节角度
# 构建观察值字典 (供控制器使用)
observations = {
self._franka.name: {
"joint_positions": current_joint_positions,
},
self._cube.name: {
"position": cube_position,
"goal_position": self._goal_position
}
}
return observations
# for instance we can check here if the task was accomplished by
# changing the color of the cube once its accomplished
# 每物理步模拟前调用
def pre_step(self, control_index, simulation_time):
cube_position, _ = self._cube.get_world_pose()
# 检查立方体是否到达目标位置(误差<2cm)
if not self._task_achieved and np.mean(np.abs(self._goal_position - cube_position)) < 0.02:
# PreviewSurface, we can set its color once the target is reached.
self._cube.get_applied_visual_material().set_color(color=np.array([0, 1.0, 0]))# 变绿色
self._task_achieved = True # 标记任务完成
return
# 每次重置后调用
def post_reset(self):
self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions) # 打开夹爪
self._cube.get_applied_visual_material().set_color(color=np.array([0, 0, 1.0])) # 重置为蓝色
self._task_achieved = False # 重置任务状态
return
class MyHelloWorld(BaseSample): # 继承自basesample
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.add_task(FrankaPlaying(name="my_first_task")) # 添加自定义任务
return
async def setup_post_load(self):
self._world = self.get_world()
self._franka = self._world.scene.get_object("fancy_franka") # 获取机械臂对象
# 初始化抓放控制器
self._controller = PickPlaceController(
name="pick_place_controller",
gripper=self._franka.gripper, # 夹爪对象
robot_articulation=self._franka # 机械臂对象
)
# 注册物理步进回调
self._world.add_physics_callback("sim_step", callback_fn=self.physics_step)
await self._world.play_async() # 开始仿真
# 重置之后要做的事情
async def setup_post_reset(self):
self._controller.reset()
# 重启之后也要重置夹爪位置/打开夹爪
self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)
await self._world.play_async()
return
def physics_step(self, step_size):
# 1. 获取当前观察值(立方体位置/目标位置/关节角度)
current_observations = self._world.get_observations()
# 2. 控制器计算动作指令
actions = self._controller.forward(
picking_position=current_observations["fancy_cube"]["position"],
placing_position=current_observations["fancy_cube"]["goal_position"],
current_joint_positions=current_observations["fancy_franka"]["joint_positions"]
)
# 3. 应用动作到机械臂
self._franka.apply_action(actions)
# 4. 检查任务完成状态
if self._controller.is_done():
self._world.pause() # 暂停仿真
return
def world_cleanup(self):# 释放内存等
return
最后成功了cube就有蓝变绿.
3.2 使用Pick and Place Task
Omniverse Isaac Sim 也提供了不同的任务类在许多的机器人扩展中,我们这里重写提供的pickplace
# Use the Pick and Place Task
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.controllers import PickPlaceController
from omni.isaac.franka.tasks import PickPlace
import numpy as np
class MyHelloWorld(BaseSample): # 继承自basesample
def __init__(self) -> None:
super().__init__()
return
def setup_scene(self):
world = self.get_world()
world.add_task(PickPlace(name="awesome_task")) # 添加自带的任务,包含了机械臂方块等
return
async def setup_post_load(self):
self._world = self.get_world()
# 每个被定义好的任务都有一个 get_params()和set_params()方法
# 通过 get_params() 获取任务参数
task_params = self._world.get_task("awesome_task").get_params()
self._franka = self._world.scene.get_object(task_params["robot_name"]["value"])
self._cube_name = task_params["cube_name"]["value"]
self._controller = PickPlaceController(
name="pick_place_controller",
gripper=self._franka.gripper,
robot_articulation=self._franka,
)
# 注册物理步进回调
self._world.add_physics_callback("sim_step", callback_fn=self.physics_step)
await self._world.play_async() # 开始仿真
return
# 重置之后要做的事情
async def setup_post_reset(self):
self._controller.reset()
# 重启之后也要重置夹爪位置/打开夹爪
self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)
await self._world.play_async()
return
def physics_step(self, step_size):
# 1. 获取当前观察值(立方体位置/目标位置/关节角度)
current_observations = self._world.get_observations()
# 2. 控制器计算动作指令
actions = self._controller.forward(
picking_position=current_observations[self._cube_name]["position"],
placing_position=current_observations[self._cube_name]["target_position"],
current_joint_positions=current_observations[self._franka.name]["joint_positions"],
)
# 3. 应用动作到机械臂
self._franka.apply_action(actions)
# 4. 检查任务完成状态
if self._controller.is_done():
self._world.pause() # 暂停仿真
return
整体来说更简单,直接调用人家封装好的就可以,不用我们自己设置各种各样的东西。