6.isaac sim4.2 教程-Core API-多机器人,多任务

本教程将两种不同类型的机器人集成到同一个仿真中。它详细说明了如何构建程序逻辑以在子任务之间切换。通过本教程,你将获得构建更复杂的机器人交互仿真经验。

1. 创建一个场景

首先,按照以下步骤将 JetbotFranka PandaCube 添加到场景中,并通过子任务来简化代码:

python 复制代码
# Adding Multiple Robots
# 创建场景
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    def __init__(
        self,
        name
    ):
        super().__init__(name=name, offset=None)
        self._jetbot_goal_position = np.array([130, 30, 0]) # 初始化时,定义了 Jetbot 的目标位置
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0])
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path="/World/Fancy_Robot",
                name="fancy_robot",
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        self._franka.set_world_pose(position=np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=np.array([1.0, 0, 0]))
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.add_task(RobotsPlaying(name="awesome_task"))  # 添加自定义的任务
        return

2. 驱动Jetbot 把cube推给Franka.

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    def __init__(
        self,
        name
    ):
        super().__init__(name=name, offset=None)
        self._jetbot_goal_position = np.array([1.3, 0.3, 0]) # 初始化时,定义了 Jetbot 的目标位置
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0])
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path="/World/Fancy_Robot",
                name="fancy_robot",
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        self._franka.set_world_pose(position=np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=np.array([1.0, 0, 0]))
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.add_task(RobotsPlaying(name="awesome_task"))  # 添加自定义的任务
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        task_params = self._world.get_task("awesome_task").get_params()
        self._jetbot = self._world.scene.get_object(task_params["jetbot_name"]["value"])
        self._cube_name = task_params["cube_name"]["value"]
        # 这里使用了 WheelBasePoseController 来控制 Jetbot 的运动
        self._jetbot_controller = WheelBasePoseController(
            name="cool_controller",
            open_loop_wheel_controller=DifferentialController(
                name="simple_control",
                wheel_radius=0.03,
                wheel_base=0.1125
            )
        )
        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._jetbot_controller.reset()
        await self._world.play_async()
        return


    def physics_step(self, step_size):
        current_observations = self._world.get_observations()
        self._jetbot.apply_action(
            self._jetbot_controller.forward(
                start_position=current_observations[self._jetbot.name]["position"],
                start_orientation=current_observations[self._jetbot.name]["orientation"],
                goal_position=current_observations[self._jetbot.name]["goal_position"]
            )
        )
        return

3. 增加任务逻辑

下一步,驱动 jetbot 后退让出空间给franka去把cube捡起并放到指定位置。

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
from omni.isaac.core.utils.types import ArticulationAction
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    def __init__(
        self,
        name
    ):
        super().__init__(name=name, offset=None)
        self._jetbot_goal_position = np.array([1.3, 0.3, 0]) # 初始化时,定义了 Jetbot 的目标位置
        # 增加任务逻辑标志--任务事件
        self._task_event = 0
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0])
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path="/World/Fancy_Robot",
                name="fancy_robot",
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        self._franka.set_world_pose(position=np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=np.array([1.0, 0, 0]))
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            # 通过任务事件来控制 Jetbot 的行为
            "task_event": self._task_event,
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    # 该方法在每个物理步之前调用,进行判断当前 Jetbot 应该做什么。
    # 它根据任务事件的状态来决定 Jetbot 的行为。
    def pre_step(self, control_index, simulation_time):
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if self._task_event == 0:
            current_jetbot_position, _ = self._jetbot.get_world_pose()
            # 计算 Jetbot 到目标位置的距离,如果距离小于 4cm,则认为 Jetbot 到达了目标位置。
            if np.mean(np.abs(current_jetbot_position[:2] - self._jetbot_goal_position[:2])) < 0.04:
                self._task_event += 1
                self._cube_arrive_step_index = control_index
        elif self._task_event == 1:
            # Jetbot 有200个物理步长的时间来后退
            if control_index - self._cube_arrive_step_index == 200:
                self._task_event += 1 # 任务事件加1,表示 Jetbot 后退的时间结束。
        return

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.add_task(RobotsPlaying(name="awesome_task"))  # 添加自定义的任务
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        task_params = self._world.get_task("awesome_task").get_params()
        self._jetbot = self._world.scene.get_object(task_params["jetbot_name"]["value"])
        self._cube_name = task_params["cube_name"]["value"]
        # 这里使用了 WheelBasePoseController 来控制 Jetbot 的运动
        self._jetbot_controller = WheelBasePoseController(
            name="cool_controller",
            open_loop_wheel_controller=DifferentialController(
                name="simple_control",
                wheel_radius=0.03,
                wheel_base=0.1125
            )
        )
        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._jetbot_controller.reset()
        await self._world.play_async()
        return


    def physics_step(self, step_size):
        current_observations = self._world.get_observations()
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if current_observations["task_event"] == 0:
            self._jetbot.apply_wheel_actions(
                self._jetbot_controller.forward(
                    start_position=current_observations[self._jetbot.name]["position"],
                    start_orientation=current_observations[self._jetbot.name]["orientation"],
                    goal_position=current_observations[self._jetbot.name]["goal_position"]))
            
        # 如果任务事件为 1,表示 Jetbot 需要向后移动。
        elif current_observations["task_event"] == 1:
            # Go backwards
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[-8, -8]))
                    
        # 如果任务事件为 2,表示 Jetbot 停止移动。
        elif current_observations["task_event"] == 2:
            # Apply zero velocity to override the velocity applied before.
            # Note: target joint positions and target joint velocities will stay active unless changed
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[0.0, 0.0]))
        return

4. 机器人交接任务

现在该是franka去pickplace了

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
from omni.isaac.core.utils.types import ArticulationAction
from omni.isaac.franka.controllers import PickPlaceController
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    def __init__(
        self,
        name
    ):
        super().__init__(name=name, offset=None)
        self._jetbot_goal_position = np.array([1.3, 0.3, 0]) # 初始化时,定义了 Jetbot 的目标位置
        # 增加任务逻辑标志--任务事件
        self._task_event = 0
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0])
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path="/World/Fancy_Robot",
                name="fancy_robot",
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        self._franka.set_world_pose(position=np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=np.array([1.0, 0, 0]))
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            # 通过任务事件来控制 Jetbot 的行为
            "task_event": self._task_event,
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        # 获取 PickPlace 任务的观察值,并将其添加到 observations 字典中。
        observations.update(self._pick_place_task.get_observations())
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    # 该方法在每个物理步之前调用,进行判断当前 Jetbot 应该做什么。
    # 它根据任务事件的状态来决定 Jetbot 的行为。
    def pre_step(self, control_index, simulation_time):
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if self._task_event == 0:
            current_jetbot_position, _ = self._jetbot.get_world_pose()
            # 计算 Jetbot 到目标位置的距离,如果距离小于 4cm,则认为 Jetbot 到达了目标位置。
            if np.mean(np.abs(current_jetbot_position[:2] - self._jetbot_goal_position[:2])) < 0.04:
                self._task_event += 1
                self._cube_arrive_step_index = control_index
        elif self._task_event == 1:
            # Jetbot 有200个物理步长的时间来后退
            if control_index - self._cube_arrive_step_index == 200:
                self._task_event += 1 # 任务事件加1,表示 Jetbot 后退的时间结束。
        return

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.add_task(RobotsPlaying(name="awesome_task"))  # 添加自定义的任务
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        # 获得任务参数
        task_params = self._world.get_task("awesome_task").get_params()
        # 获得jetbot和cube的名称和参数
        self._jetbot = self._world.scene.get_object(task_params["jetbot_name"]["value"])
        self._cube_name = task_params["cube_name"]["value"]
        # 获得franka机械臂的名称和参数
        self._franka = self._world.scene.get_object(task_params["franka_name"]["value"])
        # 这里使用了 WheelBasePoseController 来控制 Jetbot 的运动
        self._jetbot_controller = WheelBasePoseController(
            name="cool_controller",
            open_loop_wheel_controller=DifferentialController(
                name="simple_control",
                wheel_radius=0.03,
                wheel_base=0.1125
            )
        )
        # 增加了 PickPlaceController 来控制 Franka Panda 机械臂的抓取和放置任务。
        self._franka_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._jetbot_controller.reset()
        # 重置 Franka Panda 的控制器
        self._franka_controller.reset()
        await self._world.play_async()
        return


    def physics_step(self, step_size):
        current_observations = self._world.get_observations()
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if current_observations["task_event"] == 0:
            self._jetbot.apply_wheel_actions(
                self._jetbot_controller.forward(
                    start_position=current_observations[self._jetbot.name]["position"],
                    start_orientation=current_observations[self._jetbot.name]["orientation"],
                    goal_position=current_observations[self._jetbot.name]["goal_position"]))
            
        # 如果任务事件为 1,表示 Jetbot 需要向后移动。
        elif current_observations["task_event"] == 1:
            # Go backwards
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[-8, -8]))
                    
        # 如果任务事件为 2,表示 Jetbot 停止移动。
        elif current_observations["task_event"] == 2:
            # Apply zero velocity to override the velocity applied before.
            # Note: target joint positions and target joint velocities will stay active unless changed
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[0.0, 0.0]))
            # jetbot后退完后,让 Franka Panda 机械臂执行抓取和放置任务
            actions = self._franka_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"])
            self._franka.apply_action(actions)
        # Pause once the controller is done
        if self._franka_controller.is_done():
            self._world.pause()
        return

5. 任务参数化

本教程描述了如何在 Omniverse Isaac Sim 中添加多个任务。它解释了如何向任务中添加参数以扩展你的仿真。

python 复制代码
# 多个任务
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
from omni.isaac.core.utils.types import ArticulationAction
from omni.isaac.franka.controllers import PickPlaceController
# find_unique_string_name 用于生成一个唯一的字符串名称,通常用于 Prim 路径 或 场景名称,以确保这些名称不会重复。
from omni.isaac.core.utils.string import find_unique_string_name  
# 验证一个 Prim 路径是否有效,即路径是否已存在于当前仿真场景中。      
from omni.isaac.core.utils.prims import is_prim_path_valid     
from omni.isaac.core.objects.cuboid import VisualCuboid
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    # 位置被偏移量 (offset) 调整。
    def __init__(self,name,offset=None):
        super().__init__(name=name, offset=offset)
        self._jetbot_goal_position = np.array([1.3, 0.3, 0]) + self._offset # 初始化时,定义了 Jetbot 的目标位置
        # 增加任务逻辑标志--任务事件
        self._task_event = 0
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0]),
            offset=offset
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        # 为Jetbot创建一个唯一的名称和Prim路径
        jetbot_name = find_unique_string_name(
            initial_name="fancy_jetbot", is_unique_fn=lambda x: not self.scene.object_exists(x)
        )
        jetbot_prim_path = find_unique_string_name(
            initial_name="/World/Fancy_Jetbot", is_unique_fn=lambda x: not is_prim_path_valid(x)
        )

        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path=jetbot_prim_path,
                name=jetbot_name,
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        # 将Jetbot添加到任务对象中
        self._task_objects[self._jetbot.name] = self._jetbot
        # 获取PickPlace任务中的Franka机器人
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        current_position, _ = self._franka.get_world_pose()
        self._franka.set_world_pose(position=current_position + np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=current_position + np.array([1.0, 0, 0]))
        # 调用父类方法,将任务对象按照偏移量进行平移
        self._move_task_objects_to_their_frame()
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            # 通过任务事件来控制 Jetbot 的行为
            "task_event": self._task_event,
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        # 获取 PickPlace 任务的观察值,并将其添加到 observations 字典中。
        observations.update(self._pick_place_task.get_observations())
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    # 该方法在每个物理步之前调用,进行判断当前 Jetbot 应该做什么。
    # 它根据任务事件的状态来决定 Jetbot 的行为。
    def pre_step(self, control_index, simulation_time):
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if self._task_event == 0:
            current_jetbot_position, _ = self._jetbot.get_world_pose()
            # 计算 Jetbot 到目标位置的距离,如果距离小于 4cm,则认为 Jetbot 到达了目标位置。
            if np.mean(np.abs(current_jetbot_position[:2] - self._jetbot_goal_position[:2])) < 0.04:
                self._task_event += 1
                self._cube_arrive_step_index = control_index
        elif self._task_event == 1:
            # Jetbot 有200个物理步长的时间来后退
            if control_index - self._cube_arrive_step_index == 100:
                self._task_event += 1 # 任务事件加1,表示 Jetbot 后退的时间结束。
        return

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        return

    def setup_scene(self):
        world = self.get_world()
        world.add_task(RobotsPlaying(name="awesome_task", offset=np.array([0, -1.0, 0])))  # 添加自定义的任务
        # 仅用于可视化,展示Franka原本应该在的位置
        VisualCuboid(prim_path="/new_cube_1",
                name="visual_cube",
                position=np.array([1.0, 0, 0.05]),
                scale=np.array([0.1, 0.1, 0.1]))
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        # 获得任务参数
        task_params = self._world.get_task("awesome_task").get_params()
        # 获得jetbot和cube的名称和参数
        self._jetbot = self._world.scene.get_object(task_params["jetbot_name"]["value"])
        self._cube_name = task_params["cube_name"]["value"]
        # 获得franka机械臂的名称和参数
        self._franka = self._world.scene.get_object(task_params["franka_name"]["value"])
        # 这里使用了 WheelBasePoseController 来控制 Jetbot 的运动
        self._jetbot_controller = WheelBasePoseController(
            name="cool_controller",
            open_loop_wheel_controller=DifferentialController(
                name="simple_control",
                wheel_radius=0.03,
                wheel_base=0.1125
            )
        )
        # 增加了 PickPlaceController 来控制 Franka Panda 机械臂的抓取和放置任务。
        self._franka_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):
        robots_playing_task = self._world.get_task("awesome_task")
        robots_playing_task._task_event = 0  # 直接重置任务实例的事件
        self._jetbot_controller.reset()
        # 重置 Franka Panda 的控制器
        self._franka_controller.reset()
        await self._world.play_async()
        return


    def physics_step(self, step_size):
        current_observations = self._world.get_observations()
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if current_observations["task_event"] == 0:
            self._jetbot.apply_wheel_actions(
                self._jetbot_controller.forward(
                    start_position=current_observations[self._jetbot.name]["position"],
                    start_orientation=current_observations[self._jetbot.name]["orientation"],
                    goal_position=current_observations[self._jetbot.name]["goal_position"]))
            
        # 如果任务事件为 1,表示 Jetbot 需要向后移动。
        elif current_observations["task_event"] == 1:
            # Go backwards
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[-2, -2]))
                    
        # 如果任务事件为 2,表示 Jetbot 停止移动。
        elif current_observations["task_event"] == 2:
            # Apply zero velocity to override the velocity applied before.
            # Note: target joint positions and target joint velocities will stay active unless changed
            self._jetbot.apply_wheel_actions(ArticulationAction(joint_velocities=[0.0, 0.0]))
            # jetbot后退完后,让 Franka Panda 机械臂执行抓取和放置任务
            actions = self._franka_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"])
            self._franka.apply_action(actions)
        # Pause once the controller is done
        if self._franka_controller.is_done():
            self._world.pause()
        return

6. 扩展到多个任务

python 复制代码
# 多个任务
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.franka.tasks import PickPlace
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.tasks import BaseTask
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
from omni.isaac.core.utils.types import ArticulationAction
from omni.isaac.franka.controllers import PickPlaceController
# find_unique_string_name 用于生成一个唯一的字符串名称,通常用于 Prim 路径 或 场景名称,以确保这些名称不会重复。
from omni.isaac.core.utils.string import find_unique_string_name  
# 验证一个 Prim 路径是否有效,即路径是否已存在于当前仿真场景中。      
from omni.isaac.core.utils.prims import is_prim_path_valid     
from omni.isaac.core.objects.cuboid import VisualCuboid
import numpy as np

# RobotsPlaying 继承自 BaseTask 类,表示一个包含多个机器人任务的复合任务。
class RobotsPlaying(BaseTask):
    # 位置被偏移量 (offset) 调整。
    def __init__(self,name,offset=None):
        super().__init__(name=name, offset=offset)
        # 随机化jetbot目标位置
        self._jetbot_goal_position = np.array([np.random.uniform(1.2, 1.6), 0.3, 0]) + self._offset # 初始化时,定义了 Jetbot 的目标位置
        # 增加任务逻辑标志--任务事件
        self._task_event = 0
        # 使用 PickPlace 任务类创建了一个 拾取和放置任务,并定义了立方体的初始位置和目标位置。
        self._pick_place_task = PickPlace(
            cube_initial_position=np.array([0.1, 0.3, 0.05]),
            target_position=np.array([0.7, -0.3, 0.0515 / 2.0]),
            offset=offset
            )
        return


    def set_up_scene(self, scene):
        super().set_up_scene(scene)
        self._pick_place_task.set_up_scene(scene)
        # 为Jetbot创建一个唯一的名称和Prim路径
        jetbot_name = find_unique_string_name(
            initial_name="fancy_jetbot", is_unique_fn=lambda x: not self.scene.object_exists(x)
        )
        jetbot_prim_path = find_unique_string_name(
            initial_name="/World/Fancy_Jetbot", is_unique_fn=lambda x: not is_prim_path_valid(x)
        )

        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 在场景中添加 Jetbot 机器人
        # 通过 WheeledRobot 类来创建一个轮式机器人实例,并将其添加到场景中。
        # 这里指定了轮子关节的名称和机器人模型
        self._jetbot = scene.add(
            WheeledRobot(
                prim_path=jetbot_prim_path,
                name=jetbot_name,
                wheel_dof_names=["left_wheel_joint", "right_wheel_joint"],
                create_robot=True,
                usd_path=jetbot_asset_path,
                position=np.array([0, 0.3, 0]))
        )
        # 将Jetbot添加到任务对象中
        self._task_objects[self._jetbot.name] = self._jetbot
        # 获取PickPlace任务中的Franka机器人
        pick_place_params = self._pick_place_task.get_params()
        self._franka = scene.get_object(pick_place_params["robot_name"]["value"])
        # 将 Franka Panda 的默认位置设置为 (1.0, 0, 0),即重置后的位置。
        current_position, _ = self._franka.get_world_pose()
        self._franka.set_world_pose(position=current_position + np.array([1.0, 0, 0]))
        self._franka.set_default_state(position=current_position + np.array([1.0, 0, 0]))
        # 调用父类方法,将任务对象按照偏移量进行平移
        self._move_task_objects_to_their_frame()
        return

    # 该方法获取 Jetbot 的信息
    def get_observations(self):
        current_jetbot_position, current_jetbot_orientation = self._jetbot.get_world_pose()
        # Observations needed to drive the jetbot to push the cube
        observations= {
            # 通过任务事件来控制 Jetbot 的行为
            self.name + "_event": self._task_event, # 改变任务事件以使其唯一
            self._jetbot.name: {
                "position": current_jetbot_position,
                "orientation": current_jetbot_orientation,
                "goal_position": self._jetbot_goal_position
            }
        }
        # 获取 PickPlace 任务的观察值,并将其添加到 observations 字典中。
        observations.update(self._pick_place_task.get_observations())
        return observations


    def get_params(self):
        # 为了避免硬编码,获取了 PickPlace 任务的参数并将其返回,同时也添加了 Jetbot 和 Franka Panda 的名称作为任务的参数。
        pick_place_params = self._pick_place_task.get_params()
        params_representation = pick_place_params
        params_representation["jetbot_name"] = {"value": self._jetbot.name, "modifiable": False}
        params_representation["franka_name"] = pick_place_params["robot_name"]
        return params_representation

    # 该方法在每个物理步之前调用,进行判断当前 Jetbot 应该做什么。
    # 它根据任务事件的状态来决定 Jetbot 的行为。
    def pre_step(self, control_index, simulation_time):
        # 如果任务事件为 0,表示 Jetbot 需要移动到目标位置。
        if self._task_event == 0:
            current_jetbot_position, _ = self._jetbot.get_world_pose()
            # 计算 Jetbot 到目标位置的距离,如果距离小于 4cm,则认为 Jetbot 到达了目标位置。
            if np.mean(np.abs(current_jetbot_position[:2] - self._jetbot_goal_position[:2])) < 0.04:
                self._task_event += 1
                self._cube_arrive_step_index = control_index
        elif self._task_event == 1:
            # Jetbot 有200个物理步长的时间来后退
            if control_index - self._cube_arrive_step_index == 100:
                self._task_event += 1 # 任务事件加1,表示 Jetbot 后退的时间结束。
        return

    def post_reset(self):
        self._franka.gripper.set_joint_positions(self._franka.gripper.joint_opened_positions)

class MyHelloWorld(BaseSample): # 继承自basesample
    def __init__(self) -> None:
        super().__init__()
        # Add lists for tasks,
        self._tasks = []
        self._num_of_tasks = 3
        #  Add lists for controllers
        self._franka_controllers = []
        self._jetbot_controllers = []
        # Add lists for variables needed for control
        self._jetbots = []
        self._frankas = []
        self._cube_names = []
        return

    def setup_scene(self):
        world = self.get_world()
        # 添加多个 RobotsPlaying 任务,每个任务都有一个偏移量
        # 这里使用了循环来创建多个任务实例,并将它们添加到世界中。
        for i in range(self._num_of_tasks):
            world.add_task(RobotsPlaying(
                name="my_awesome_task_" + str(i), 
                offset=np.array([0, (i * 2) - 3, 0])
            ))
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        for i in range(self._num_of_tasks):
            self._tasks.append(self._world.get_task(name="my_awesome_task_" + str(i)))
            # 获取每个任务的变量
            task_params = self._tasks[i].get_params()
            self._frankas.append(self._world.scene.get_object(task_params["franka_name"]["value"]))
            self._jetbots.append(self._world.scene.get_object(task_params["jetbot_name"]["value"]))
            self._cube_names.append(task_params["cube_name"]["value"])
            # 每个任务都要定义控制器
            # 通过改变 dt,可以控制机器人在执行拾取和放置任务时的过渡速度,使其变得更慢,
            # 这对于在一个场景中处理多个物体(如多个立方体)时可能会非常有用,确保机器人能更精确地完成每个任务。
            self._franka_controllers.append(
                PickPlaceController(
                    name="pick_place_controller",
                    gripper=self._frankas[i].gripper,
                    robot_articulation=self._frankas[i],
                    events_dt=[0.008, 0.002, 0.5, 0.1, 0.05, 0.05, 0.0025, 1, 0.008, 0.08]
                )
            )
            self._jetbot_controllers.append(
                WheelBasePoseController(
                    name="cool_controller",
                    open_loop_wheel_controller=
                    DifferentialController(
                        name="simple_control",
                        wheel_radius=0.03,
                        wheel_base=0.1125
                    )
                )
            )

        self._world.add_physics_callback("sim_step", callback_fn=self.physics_step)
        await self._world.play_async()
        return


    async def setup_post_reset(self):
        for i in range(len(self._tasks)):
            # Reset all controllers
            self._franka_controllers[i].reset()
            self._jetbot_controllers[i].reset()
        await self._world.play_async()
        return


    def physics_step(self, step_size):
        current_observations = self._world.get_observations()
        for i in range(len(self._tasks)):
            # Apply actions for each task
            if current_observations[self._tasks[i].name + "_event"] == 0:
                self._jetbots[i].apply_wheel_actions(
                    self._jetbot_controllers[i].forward(
                        start_position=current_observations[self._jetbots[i].name]["position"],
                        start_orientation=current_observations[self._jetbots[i].name]["orientation"],
                        goal_position=current_observations[self._jetbots[i].name]["goal_position"]))
            elif current_observations[self._tasks[i].name + "_event"] == 1:
                self._jetbots[i].apply_wheel_actions(ArticulationAction(joint_velocities=[-8.0, -8.0]))
            elif current_observations[self._tasks[i].name + "_event"] == 2:
                self._jetbots[i].apply_wheel_actions(ArticulationAction(joint_velocities=[0.0, 0.0]))
                actions = self._franka_controllers[i].forward(
                    picking_position=current_observations[self._cube_names[i]]["position"],
                    placing_position=current_observations[self._cube_names[i]]["target_position"],
                    current_joint_positions=current_observations[self._frankas[i].name]["joint_positions"])
                self._frankas[i].apply_action(actions)
        return

    # This function is called after a hot reload or a clear
    # to delete the variables defined in this extension application
    def world_cleanup(self):
        self._tasks = []
        self._franka_controllers = []
        self._jetbot_controllers = []
        self._jetbots = []
        self._frankas = []
        self._cube_names = []
        return

可以看出cube最终位置都不一样。

相关推荐
fishjar1006 分钟前
LLaMA-Factory安装部署
人工智能·深度学习
feifeikon6 分钟前
模型篇(Bert llama deepseek)
人工智能·深度学习·自然语言处理
IoT砖家涂拉拉26 分钟前
萌宠语聊新模板!借助On-App AI降噪与音频处理技术,远程安抚宠物更轻松、更安心!
人工智能·ai·app·音视频·智能家居·智能硬件·宠物
马里马里奥-1 小时前
OpenVINO initialization error: Failed to find plugins.xml file
人工智能·openvino
Teamhelper_AR1 小时前
AR+AI:工业设备故障诊断的“秒级响应”革命
人工智能·ar
天天爱吃肉82182 小时前
效率提升新范式:基于数字孪生的汽车标定技术革命
python·嵌入式硬件·汽车
飞哥数智坊2 小时前
Cursor Claude 模型无法使用的解决方法
人工智能·claude·cursor
麻雀无能为力2 小时前
CAU数据挖掘 第五章 聚类问题
人工智能·数据挖掘·聚类·中国农业大学计算机
DogDaoDao2 小时前
2025年 GitHub 主流开源视频生成模型介绍
人工智能·深度学习·开源·大模型·github·音视频·视频生成
大千AI助手2 小时前
OpenAI GPT-4o技术详解:全能多模态模型的架构革新与生态影响
人工智能·深度学习·机器学习·自然语言处理·大模型·openai·gpt-4o