4. isaac sim4.2 教程-Core API-Hello robot

本教程详细介绍了如何在 Omniverse Isaac Sim 的扩展应用中添加并移动一个移动机器人。完成本教程后,您将了解如何向仿真环境中添加机器人,并使用 Python 对其轮子施加动作。

1. 前置条件

在开始本教程前,请先学习 Hello World 教程。

本教程基于 Hello World 教程中开发的 Awesome Example 源码继续扩展。

2. 添加一个机器人

首先,在场景中添加一个 NVIDIA Jetbot 。这样,您就可以通过 Python 访问 Omniverse Nucleus 服务器上的 Isaac Sim 机器人、传感器和环境资源库,并利用 Content 窗口 进行浏览。

在拖动添加的时候,直接把他拖到world下就可以,如果直接拖到场景中是与world并列的。

接下来看看怎么用代码添加

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.core import World

from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.core.robots import Robot
import carb

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()
        
        # 拿到资源的根路径
        assets_root_path = get_assets_root_path()
        if assets_root_path is None:
            # Use carb to 记录日志 warnings, errors and infos in your application (shown on terminal)
            carb.log_error("Could not find nucleus server with /Isaac folder")
        
        # 拿到具体资源路径:根路径+具体资源
        asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 这将会创建 a new XFormPrim and 把它添加到场景world中
        add_reference_to_stage(usd_path=asset_path, prim_path="/World/Fancy_Robot")
        
        # 将机器人封装为Robot对象并添加到场景
        # Note: this call doesn't create the Jetbot in the stage window, it was already
        # created with the add_reference_to_stage
        jetbot_robot = world.scene.add(Robot(prim_path="/World/Fancy_Robot", name="fancy_robot"))

        # 物理系统初始化前无法获取动力学信息
        print("Num of degrees of freedom before first reset: " + str(jetbot_robot.num_dof)) # prints None
        return
    
    # 初始化时机:
    #     发生在 setup_scene() 完成之后
    #     发生在 setup_post_load() 执行之前

    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")

        # 物理系统初始化后可获取动力学参数
        print("Num of degrees of freedom after first reset: " + str(self._jetbot.num_dof)) # prints 2
        print("Joint Positions after first reset: " + str(self._jetbot.get_joint_positions()))
        return
    

再次load:

这就通过代码加进来了。

3. 移动机器人

把代码改成:

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.core import World

from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.core.utils.stage import add_reference_to_stage
from omni.isaac.core.robots import Robot
import carb

from omni.isaac.core.utils.types import ArticulationAction # 我们要给机器人发送动作,动作类型就是ArticulationAction
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()
        
        # 拿到资源的根路径
        assets_root_path = get_assets_root_path()
        if assets_root_path is None:
            # Use carb to 记录日志 warnings, errors and infos in your application (shown on terminal)
            carb.log_error("Could not find nucleus server with /Isaac folder")
        
        # 拿到具体资源路径:根路径+具体资源
        asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # 这将会创建 a new XFormPrim and 把它添加到场景world中
        add_reference_to_stage(usd_path=asset_path, prim_path="/World/Fancy_Robot")
        
        # 将机器人封装为Robot对象并添加到场景
        # Note: this call doesn't create the Jetbot in the stage window, it was already
        # created with the add_reference_to_stage
        jetbot_robot = world.scene.add(Robot(prim_path="/World/Fancy_Robot", name="fancy_robot"))

        # 物理系统初始化前无法获取动力学信息
        print("Num of degrees of freedom before first reset: " + str(jetbot_robot.num_dof)) # prints None
        return
    
    # 初始化时机:
    #     发生在 setup_scene() 完成之后
    #     发生在 setup_post_load() 执行之前

    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")

        # 物理系统初始化后可获取动力学参数
        # print("Num of degrees of freedom after first reset: " + str(self._jetbot.num_dof)) # prints 2
        # print("Joint Positions after first reset: " + str(self._jetbot.get_joint_positions()))

        # 首先要拿到机器人控制器(遥控器)
        self._jetbot_articulation_controller = self._jetbot.get_articulation_controller()
        # 设置自动执行的任务--通过回调函数
        # 每一个物理步长都会调用一次这个回调函数
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        return
    
    def send_robot_actions(self, step_size):
        # 每个关节控制器(articulation controller)都提供了 apply_action 方法。
        # 该方法用于向机器人发送动作指令,其参数是 ArticulationAction 类型,
        # 可以包含 joint_positions(关节位置)、joint_efforts(关节力矩)、joint_velocities(关节速度)这三个可选参数。
        self._jetbot_articulation_controller.apply_action(ArticulationAction(
            joint_positions=None, # 不关心
            joint_efforts=None, # 不关心
            joint_velocities=5 * np.random.rand(2,) # 只关注速度
            ))
        return

3.1 使用 WheeledRobot Class

Omniverse Isaac Sim 也有robot-specific extensions that 提供更适配的函数 and access to other controllers and tasks (more on this later). Now you'll re-write the previous code using the WheeledRobot class to make it simpler.

python 复制代码
from omni.isaac.wheeled_robots.robots import WheeledRobot #轮式机器人

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()
        
        # # 拿到资源的根路径
        # assets_root_path = get_assets_root_path()
        # if assets_root_path is None:
        #     # Use carb to 记录日志 warnings, errors and infos in your application (shown on terminal)
        #     carb.log_error("Could not find nucleus server with /Isaac folder")
        
        # # 拿到具体资源路径:根路径+具体资源
        # asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        # # 这将会创建 a new XFormPrim and 把它添加到场景world中
        # add_reference_to_stage(usd_path=asset_path, prim_path="/World/Fancy_Robot")
        
        # # 将机器人封装为Robot对象并添加到场景
        # # Note: this call doesn't create the Jetbot in the stage window, it was already
        # # created with the add_reference_to_stage
        # jetbot_robot = world.scene.add(Robot(prim_path="/World/Fancy_Robot", name="fancy_robot"))

        # # 物理系统初始化前无法获取动力学信息
        # print("Num of degrees of freedom before first reset: " + str(jetbot_robot.num_dof)) # prints None

        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"
        self._jetbot = world.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,
            )
        )
        return
    
    # 初始化时机:
    #     发生在 setup_scene() 完成之后
    #     发生在 setup_post_load() 执行之前

    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")

        # 物理系统初始化后可获取动力学参数
        # print("Num of degrees of freedom after first reset: " + str(self._jetbot.num_dof)) # prints 2
        # print("Joint Positions after first reset: " + str(self._jetbot.get_joint_positions()))

        # 首先要拿到机器人控制器(遥控器),如果用wheelrobot类就不用这个控制器了
        # self._jetbot_articulation_controller = self._jetbot.get_articulation_controller()
        # 设置自动执行的任务--通过回调函数
        # 每一个物理步长都会调用一次这个回调函数
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        return
    
    def send_robot_actions(self, step_size):
        # 每个关节控制器(articulation controller)都提供了 apply_action 方法。
        # 该方法用于向机器人发送动作指令,其参数是 ArticulationAction 类型,
        # 可以包含 joint_positions(关节位置)、joint_efforts(关节力矩)、joint_velocities(关节速度)这三个可选参数。
        # self._jetbot_articulation_controller.apply_action(ArticulationAction(
        #     joint_positions=None, # 不关心
        #     joint_efforts=None, # 不关心
        #     joint_velocities=5 * np.random.rand(2,) # 只关注速度
        #     ))

        # 直接用wheelrobot类就可以了,不用对controller进行操作
        self._jetbot.apply_wheel_actions(ArticulationAction(
            joint_positions=None,
            joint_efforts=None,
            joint_velocities=5 * np.random.rand(2,)
            ))
        return

4. 增加一个自定义控制器

本教程将介绍如何创建并使用自定义控制器来移动移动机器人,随后详细说明如何在Omniverse Isaac Sim中运用各类可用控制器。完成本教程后,您将能更轻松地在Omniverse Isaac Sim中添加与控制机器人。

python 复制代码
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.wheeled_robots.robots import WheeledRobot
from omni.isaac.core.utils.types import ArticulationAction
from omni.isaac.core.controllers import BaseController
import numpy as np

class CoolController(BaseController):
    def __init__(self):
        super().__init__(name="my_cool_controller")
        # An open loop controller that uses a unicycle model
        self._wheel_radius = 0.03 # 轮子半径3cm
        self._wheel_base = 0.1125 # 轴距11.25cm
        return

    def forward(self, command): # 这个函数是用来接收命令的
        # command will have two elements, first element is the forward velocity
        # second element is the angular velocity (yaw only).
        joint_velocities = [0.0, 0.0]
        # 差速驱动模型
        joint_velocities[0] = ((2 * command[0]) - (command[1] * self._wheel_base)) / (2 * self._wheel_radius)
        joint_velocities[1] = ((2 * command[0]) + (command[1] * self._wheel_base)) / (2 * self._wheel_radius)
        # A controller has to return an ArticulationAction
        return ArticulationAction(joint_velocities=joint_velocities)

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()
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"

        world.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,
            )
        )
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        # 初始化自己的控制器
        self._my_controller = CoolController()
        return
    
    def send_robot_actions(self, step_size):
        # 让控制器去计算动作
        self._jetbot.apply_action(self._my_controller.forward(command=[0.20, np.pi / 4])) # 前进速度0.2m/s,角速度pi/4
        return

    async def setup_pre_reset(self): # reset之前,场景重置之前保存/暂停等
        return

    async def setup_post_reset(self): # 重置之后,恢复初始化状态
        return

    def world_cleanup(self):# 释放内存等
        return

4.1 Using the Available Controllers

Omniverse Isaac Sim also provides different controllers under many robot extensions. Re-write the previous code using the DifferentialController class and add a WheelBasePoseController

刚才我们用的就是右边的,给一个高层的命令,然后通过控制器去计算,驱动。

现在来用下已经实现好的轮姿态控制器和差速控制器,wheelbase就是要知道目标的位置和方向才能移动到制定位置,时应高层的命令。差速控制器是精确到控制轮子的速度。

isaacsim4.2/exts/omni.isaac.wheeled_robots/omni/isaac/wheeled_robots/controllers/init.py中还有其他控制器

python 复制代码
# Using the Available Controllers
from omni.isaac.examples.base_sample import BaseSample
from omni.isaac.core.utils.nucleus import get_assets_root_path
from omni.isaac.wheeled_robots.robots import WheeledRobot
# This extension includes several generic controllers that could be used with multiple robots
from omni.isaac.wheeled_robots.controllers.wheel_base_pose_controller import WheelBasePoseController
# Robot specific controller
from omni.isaac.wheeled_robots.controllers.differential_controller import DifferentialController
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()
        assets_root_path = get_assets_root_path()
        jetbot_asset_path = assets_root_path + "/Isaac/Robots/Jetbot/jetbot.usd"

        world.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,
            )
        )
        return
    
    async def setup_post_load(self):
        self._world = self.get_world()
        self._jetbot = self._world.scene.get_object("fancy_robot")
        self._world.add_physics_callback("sending_actions", callback_fn=self.send_robot_actions)
        # 初始化自己的控制器
        self._my_controller = WheelBasePoseController(
            name="cool_controller",
            open_loop_wheel_controller=DifferentialController( # 开环就是不会判断是否真的到达目标位置,闭环会有反馈
                name="simple_control",
                wheel_radius=0.03,
                wheel_base=0.1125
            ),
            is_holonomic=False # 非全向轮
        )

        return
    
    def send_robot_actions(self, step_size):
        # 获取当前机器人的位姿
        position, orientation = self._jetbot.get_world_pose()
        self._jetbot.apply_action(
            self._my_controller.forward(
                # 开始时的位姿
                start_position=position,
                start_orientation=orientation,
                # 目标位置
                goal_position=np.array([0.8, 0.8])
            )
        )
        return

可以看到小车一开始是转变了一下朝向才开始移动的:

相关推荐
春哥的研究所10 分钟前
AI人工智能名片小程序源码系统,名片小程序+分销商城+AI客服,包含完整搭建教程
人工智能·微信小程序·小程序
ahead~14 分钟前
【大模型入门】访问GPT_API实战案例
人工智能·python·gpt·大语言模型llm
喜欢吃豆14 分钟前
深入企业内部的MCP知识(三):FastMCP工具转换(Tool Transformation)全解析:从适配到增强的工具进化指南
java·前端·人工智能·大模型·github·mcp
pany21 分钟前
写代码的节奏,正在被 AI 改写
前端·人工智能·aigc
我爱一条柴ya1 小时前
【AI大模型】神经网络反向传播:核心原理与完整实现
人工智能·深度学习·神经网络·ai·ai编程
万米商云1 小时前
企业物资集采平台解决方案:跨地域、多仓库、百部门——大型企业如何用一套系统管好百万级物资?
大数据·运维·人工智能
新加坡内哥谈技术1 小时前
Google AI 刚刚开源 MCP 数据库工具箱,让 AI 代理安全高效地查询数据库
人工智能
慕婉03071 小时前
深度学习概述
人工智能·深度学习
大模型真好玩1 小时前
准确率飙升!GraphRAG如何利用知识图谱提升RAG答案质量(额外篇)——大规模文本数据下GraphRAG实战
人工智能·python·mcp
19891 小时前
【零基础学AI】第30讲:生成对抗网络(GAN)实战 - 手写数字生成
人工智能·python·深度学习·神经网络·机器学习·生成对抗网络·近邻算法