PyMJCF 使用教程

系列文章目录


前言

重要:如果您在使用 PyMJCF 时发现自己卡住了,请查看本页上的各个重要方框和底部的常见问题部分,看看其中是否有相关内容。

        该库为 MuJoCo 基于 XML 的 MJCF 物理建模语言提供了一个 Python 对象模型。该库的目标是让用户能够在 Python 中轻松地与 MJCF 模型交互并对其进行修改,就像 JavaScript DOM 对 HTML 所做的那样。

        该库的一个主要特点是能够轻松地将多个独立的 MJCF 模型组成一个更大的模型。不同模型或同一模型的多个实例中的重复名称会自动进行消歧处理。

        下面的代码段提供了该库典型用例的一个快速示例。在这里,UpperBody 类可以简单地实例化 Arm 的两个副本,从而减少代码重复。每个 Arm 的身体、关节或 geoms 的名称都自动以其父名称为前缀,因此不会发生名称碰撞。


一、基本操作

1.1 创建 MJCF 模型

        在 PyMJCF 中,模型的基本构件是 mjcf.Element。它对应于生成的 XML 中的一个元素。然而,用户代码不能直接实例化一个通用的 mjcf.Element 对象。

        一个有效的模型总是由一个根 <mujoco> 元素组成。这在 PyMJCF 中被表示为特殊的 mjcf.RootElement 类型,它可以在用户代码中被实例化以创建一个空模型。

from dm_control import mjcf

mjcf_model = mjcf.RootElement()
print(mjcf_model)  # MJCF Element: <mujoco/>

1.2 添加新元素

        新元素的属性可以作为 kwargs 传递:

my_box = mjcf_model.worldbody.add('geom', name='my_box',
                                  type='box', pos=[0, .1, 0])
print(my_box)  # MJCF Element: <geom name="my_box" type="box" pos="0. 0.1 0."/>

1.3 解析现有的 XML 文档

        另外,如果已有一个 XML 文件,PyMJCF 也可以对其进行解析,以创建一个 Python 对象:

from dm_control import mjcf

# Parse from path
mjcf_model = mjcf.from_path(filename)

# Parse from file
with open(filename) as f:
  mjcf_model = mjcf.from_file(f)

# Parse from string
with open(filename) as f:
  xml_string = f.read()
mjcf_model = mjcf.from_xml_string(xml_string)

print(type(mjcf_model))  # <type 'mjcf.RootElement'>

1.4 遍历模型

        请看下面的 MJCF 模型:

<mujoco model="test">
  <default>
    <default class="brick">
      <geom rgba="1 0 0 1"/>
    </default>
  </default>
  <worldbody>
    <body name="foo">
      <freejoint/>
      <inertial pos="0 0 0" mass="1"/>
      <body name="bar">
        <joint name="my_hinge" type="hinge"/>
        <geom name="my_geom" pos="0 1 2" class="brick"/>
      </body>
    </body>
  </worldbody>
</mujoco>

        Element 对象的子元素和 XML 属性都作为 Python 属性公开。这些属性的名称都与它们的 XML 对应属性相同,只有一个例外:class XML 属性被命名为 dclass,以避免与 Python class 关键字冲突:

my_geom = mjcf_model.worldbody.body['foo'].body['bar'].geom['my_geom']
print(isinstance(mjcf_model, mjcf.Element)) # True
print(my_geom.name)    # 'my_geom'
print(my_geom.pos)     # np.array([0., 1., 2.], dtype=float)
print(my_geom.class)   # SyntaxError
print(my_geom.dclass)  # 'brick'

        请注意,对象模型中的属性值不受默认值的影响:

print(mjcf_model.default.default['brick'].geom.rgba)  # [1, 0, 0, 1]
print(my_geom.rgba)  # None

1.5 无需遍历即可查找元素

        我们还可以直接查找元素,而无需遍历对象层次结构:

found_geom = mjcf_model.find('geom', 'my_geom')
print(found_geom == my_geom)  # True

        查找给定类型的所有元素

# Note that <freejoint> is also considered a joint
joints = mjcf_model.find_all('joint')
print(len(joints))  # 2
print(joints[0] == mjcf_model.worldbody.body['foo'].freejoint)  # True
print(joints[1] == mjcf_model.worldbody.body['foo'].body['bar'].joint[0])  # True

        请注意,find_all 返回元素的顺序与它们在模型中声明的顺序相同。

1.6 修改 XML 属性

        属性可以修改、添加或删除:

my_geom.pos = [1, 2, 3]
print(my_geom.pos)   # np.array([1., 2., 3.], dtype=float)
my_geom.quat = [0, 1, 0, 0]
print(my_geom.quat)  # np.array([0., 1., 0., 0.], dtype=float)
del my_geom.quat
print(my_geom.quat)   # None

        违反模式会导致错误:

print(my_geom.poss)  # raise AttributeError (no child or attribute called poss)
my_geom.pos = 'invalid'  # raise ValueError (assigning string to array)
my_geom.pos = [1, 2, 3, 4, 5, 6]  # raise ValueError (array length is too long)

# raise ValueError (mass is a required attribute of <inertial>)
del mjcf_model.find('body', 'foo').inertial.mass

1.7 标识符的唯一性

        PyMJCF 强化了模型中 "标识符 "属性的唯一性。标识符由 <default> 的类属性和所有名称属性组成。它们的唯一性只在特定命名空间内强制执行。例如,<body> 可以与 <geom> 具有相同的名称,而 <position> 和 <velocity> 执行器则不能具有相同的名称。

mjcf_model.worldbody.add('geom', name='my_geom')
foo = mjcf_model.worldbody.find('body', 'foo')
foo.add('my_geom')  # Error, duplicated geom name
foo.add('foo')  # OK, a geom can have the same name as a body
mjcf_model.find('geom', 'foo').name = 'my_geom'  # Error, duplicated geom name

1.8 引用属性

        有些属性是对其他元素的引用。例如,执行器的关节属性指向模型中的 <joint> 元素。

mjcf.Element 可以直接分配给这些引用属性:

my_hinge = mjcf_model.find('joint', 'my_hinge')
my_actuator = mjcf_model.actuator.add('velocity', joint=my_hinge)

        这是分配引用属性的推荐方式,因为它能保证在被引用元素重命名时引用不会失效。另外,字符串也可以分配给引用属性。在这种情况下,PyMJCF 不会尝试验证命名的元素是否确实存在于模型中。

重要:如果被引用的元素与引用属性在不同的模型中(例如在附加模型中),则必须通过直接将 mjcf.Element 对象而不是字符串赋值给属性来创建引用。分配给引用属性的字符串不能包含"/",因为 PyMJCF 会在附加时自动对它们进行作用域划分。

二、附加模型

        在本节中,我们将 mjcf.RootElement 简单地称为 "模型"。模型可以附加到其他模型上,以创建合成场景。

arena = mjcf.RootElement()
arena.worldbody.add('geom', name='ground', type='plane', size=[10, 10, 1])

robot = mjcf.from_xml_file('robot.xml')
arena.attach(robot)

我们把 arena 称为父模型,把机器人称为子模型(或附着模型)。

2.1 附加 frames

        当一个模型被附加到一个场地时,会在父模型中创建一个空体。这个空体称为附着 frame。

        附着 frame 是作为包含附着点的主体的子体创建的,其位置和方向与站点相同。生成 XML 时,附着 frame 的内容会与所附模型的 <worldbody> 内容一致。在生成的 XML 中,附着 frame 的名称是子代的完全/限定/前缀/。尾部的斜线确保附着 frame 的名称不会与用户定义的主体相冲突。

        更具体地说,如果我们有以下父模型和子模型:

<mujoco model="parent">
  <worldbody>
    <body>
      <geom name="foo" type="box" pos="-0.2 0 0.3" size="0.5 0.3 0.1"/>
      <site name="attachment_site" pos="1. 2. 3." quat="1. 0. 0. 1."/>
    </body>
  </worldbody>
</mujoco>

<mujoco model="child">
  <worldbody>
    <geom name="bar" type="box" pos="0.5 0.25 1." size="0.1 0.2 0.3"/>
  </worldbody>
</mujoco>

        那么最终生成的 XML 将是

<!-- PyMJCF-generated XML, contains implementation details -->
<mujoco model="parent">
  <worldbody>
    <body>
      <geom name="foo" type="box" pos="-0.2 0 0.3" size="0.5 0.3 0.1"/>
      <site name="attachment_site" pos="1. 2. 3." quat="1. 0. 0. 1."/>
      <body name="child/" pos="1. 2. 3." quat="1. 0. 0. 1.">
        <geom name="child/my_box" type="box" pos="0.5 0.25 1." size="0.1 0.2 0.3"/>
      </body>
    </body>
  </worldbody>
</mujoco>

        重要:附着 frame 是以对用户透明的方式创建的。特别是,PyMJCF 不会将其作为常规正文处理。生成的 XML 中的名称应视为实现细节,不应依赖。

        尽管如此,有时还是有必要访问附着 frame ,例如在父模型和子模型之间添加一个连接点。最简单的方法是持有对 attach 调用所返回对象的引用:

attachment_frame = parent_model.attach('child')
attachment_frame.add('freejoint')

        另外,如果模型已经被附加,则可以使用附着框架命名空间中的 find 函数来检索附着 frame 。mjcf.traversal_utils 中的 get_attachment_frame 方便函数可以查找子模型的附着 frame ,而无需访问父模型。

frame_1 = parent_model.find('attachment_frame', 'child')

# Convenience function: get the attachment frame directly from a child model
frame_2 = mjcf.traversal_utils.get_attachment_frame(child_model)
print(frame_1 == frame_2)  # True

重要: 为鼓励良好的建模实践,附着 frame 的直接子元素只能是 <joint> 和 <inertial>。其他类型的元素应添加到附加模型的 <worldbody> 中。

2.2 元素所有权

重要:当遍历父模型时,子模型的元素不会出现。

2.3 默认类

        PyMJCF 确保父模型的默认类永远不会影响它的任何子模型。这最大限度地减少了两个模型发生微妙的 "不兼容 "的可能性,因为无论模型附属于哪个模型,其行为方式总是相同的。

        PyMJCF 在实践中实现这一点的方法是将模型的全局 <default> 上下文中的所有内容移到一个名为 / 的默认类中。换句话说,PyMJCF 生成的模型永远不会在全局默认上下文中出现任何内容。相反,生成的模型总是看起来像这样:

<!-- PyMJCF-generated XML, contains implementation details -->
<mujoco>
  <default>
    <default class="/">
      <!-- "global defaults" go here -->
      <geom rgba="1. 0. 0. 1."/>
    </default>
  </default>
</mujoco>

        重要:这种转换对用户是透明的。在 Python 中,上述 geom rgba 设置被当作全局默认值访问,即 mjcf_model.default.geom.rgba。一般来说,用户不必担心 PyMJCF 对默认值的内部处理。

        当模型被附加时,它的 / 默认类会变成 fully/qualified/prefix/。尾部的斜线确保了这种转换不会与用户命名的默认类发生冲突。更具体地说,如果我们有以下父模型和子模型:

<mujoco model="parent">
  <default>
    <geom rgba="1. 0. 0. 1."/>
    <default class="green">
      <geom rgba="0. 1. 0. 1."/>
    </default>
  </default>
</mujoco>

<mujoco model="child">
  <default>
    <joint range="0. 1."/>
    <default class="stiff">
      <joint stiffness="0.1"/>
    </default>
  </default>
</mujoco>

        那么最终生成的 XML 将是

<!-- PyMJCF-generated XML, contains implementation details -->
<mujoco model="parent">
  <default>
    <default class="/">
      <geom rgba="1. 0. 0. 1."/>
      <default class="green">
        <geom rgba="0. 1. 0. 1."/>
      </default>
    </default>
    <default class="child/">
      <joint range="0. 1."/>
      <default class="child/stiff">
        <joint stiffness="0.1"/>
      </default>
    </default>
  </default>
</mujoco>

2.4 全局选项

        如果任何全局选项不同,则无法将一个模型附加到另一个模型。全局选项由 <compiler>, <option>, <size> 和 <visual> 属性组成。与处理默认类一样,这是为了确保两个模型不会出现微妙的 "不兼容"。例如

model_1 = mjcf.RootElement()
model_1.compiler.angle = 'radian'

model_2 = mjcf.RootElement()
model_2.compiler.angle = 'degree'

model_1.attach(model_2)  # Error!

        只有当两个模型明确赋予一个选项不同的值时,该选项才会被认为是冲突的。下面举例说明冲突选项可能产生的问题:

model_1 = mjcf.RootElement()

model_2 = mjcf.RootElement()
model_2.compiler.angle = 'degree'

model_1.attach(model_2)  # No error, but all angles in model_1 are now wrong!

在这里,model_1 假设 MuJoCo 的默认角度单位为弧度。由于没有显式地给 compiler.angle 赋值,PyMJCF 没有检测到与 model_2 中的 angle=degree 冲突。现在,model_1 中的所有角度都被错误地解释为度。

2.5 <worldbody> 以外的元素

        当连接模型时,所有非世界体元素的子元素(如致动器或肌腱)都会自动合并到适当的位置。已命名元素的前缀如前所述。

三、常见问题 {#common-gotchas}

3.1 使用 foo.dclass,而不是 foo.class

        class XML 属性与 PyMJCF 中的 dclass Python 属性相对应。这是因为 class 在 Python 中是一个保留关键字。不过,在 getattr 中使用 "class "也是可以的。

<geom name="my_geom" class="red"/>
print(my_geom.class)   # SyntaxError
print(my_geom.dclass)  # 'red'
print(getattr(my_geom, 'class'))  # 'red'

3.2 foo.type 和 foo.range 没问题

        type 和 range 属性在 Cider 中会触发语法高亮,但它们在 Python 中不是保留字。

my_geom.type = 'capsule'  # OK!
my_joint.range = [-1, 1]  # OK!

3.3 一个模型只能附加一次

一个模型不能附加两次。如果在模拟中需要同一模型的多个副本,可以制作深拷贝。最好是定义一个类来构造模型,然后根据需要多次调用构造函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/588749.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

持续更新|UNIAPP适配APP遇到的问题以及解决方案

在使用UNIAPP开发APP的时候遇到的一些奇奇怪怪问题记录 组件样式丢失 问题&#xff1a;组件引入界面中&#xff0c;在小程序和H5环境下样式正常&#xff0c;而在APP中却出现高度异常问题 解决&#xff1a;增加view标签将组件包裹起来即可正常显示 解决前&#xff1a; 解决后…

全栈开发之路——前端篇(2)文件、组件与setup引入

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 本文系该系列第二篇&#xff0c;主要将介绍各个文件的意义、组件结构与导入以及setup的引入。 目录 一、src外文件介绍.gitignore为git忽略文件env.d.ts用于识别其他文件index.htmljson文件vite.confi…

企业信创中间件落地的 9 个实践经验:从选型测试到使用和运维

背景 随着各个行业对IT系统建设自主可控要求的提升&#xff0c;各个企业对信创中间件的使用范围也越来越广、深度越来越深。与此同时&#xff0c;信创中间件涉及的产品和种类也是多种多样&#xff0c;对于一个企业而言要从哪些维度来评估信创中间件&#xff0c;从而选择适合自…

规控不分家,实际岗位职责是如何划分的

1. 实践和演练 2. 自动驾驶技术分类 3. 自动驾驶关键技术 4. 自动驾驶架构 5. 感知perception

SpringDI方式及Redis应用场景的分享

1、为什么Spring和IDEA 都不推荐使用 Autowired 注解 大家在使用IDEA开发的时候有没有注意到过一个提示&#xff0c;在字段上使用Spring的依赖注入注解Autowired后会出现如下警告Field injection is not recommended (字段注入是不被推荐的)&#xff1b;但是使用Resource却不会…

【linux学习指南】linux指令与实践文件编写

文章目录 &#x1f4dd;前言&#x1f320; linux目录结构&#x1f309;linux命令介绍 &#x1f320;pwd命令&#x1f309;mkdir指令&#xff08;重要&#xff09; &#x1f320;cd 指令&#x1f309;touch指令 &#x1f320;rmdir指令 && rm 指令&#xff08;重要&…

串的介绍与类型定义

串的定义 相关术语 应用 串类型定义 顺序存储实现 为了方便操作&#xff0c;数组下标从1开始存储 链式存储实现

【Leetcode】二叉树基础题思路

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;Leetcode刷题 目录 1.单值二叉树2.相同的树3.对称二叉树4.另一棵树的子树 1.单值二叉树 题目链接&#xff1a;965.单值二叉树 题目描述&#xff1a; 单值二叉树是所有节点的值都相同的二叉树。实现…

MySQL LIKE通配符(%,_)及escape实例讲解

LIKE操作符常用于模式匹配查询数据。以正确的方式使用LIKE运算符对于提高查询性能至关重要。 LIKE操作符允许您从基于指定的模式选择表中的数据。因此&#xff0c;LIKE操作符经常用于SELECT语句的WHERE子句中。 MySQL提供了两个通配符与LIKE操作符一起使用&#xff1a;百分比…

LeetCode 98.验证二叉搜索树

题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff…

移植 SquareLine 导出的 UI 源码到 HMI-Board

目录 准备工具创建 HMI 工程设计 UIUI 移植板级验证更多内容 HMI-Board 为 RT-Thread 联合瑞萨推出的高性价比图形评估套件&#xff0c;取代传统的 HMI 主控板 硬件&#xff0c;一套硬件即可实现 HMI IoT 控制 的全套能力。依托于瑞萨高性能芯片 RA6M3 及 RT-Thread 软件生态…

leetcode870.优势洗牌

题目描述&#xff1a; 给定两个长度相等的数组 nums1 和 nums2&#xff0c;nums1 相对于 nums2 的优势可以用满足 nums1[i] > nums2[i] 的索引 i 的数目来描述。 返回 nums1 的任意排列&#xff0c;使其相对于 nums2 的优势最大化。 示例一&#xff1a; 输入&#xff…

nginx--平滑升级

失败了&#xff0c;等我拍好错继续更新 命令 选项说明 帮助: -? -h 使用指定的配置文件: -c 指定配置指令:-g 指定运行目录:-p 测试配置文件是否有语法错误:-t -T 打印nginx的版本信息、编译信息等:-v -V 发送信号: -s 示例: nginx -s reload 信号说明 立刻停止服务:stop,相…

笔记:编写程序,绘制一个展示支付宝月账单报告的饼图,

文章目录 前言一、饼图是什么&#xff1f;二、分析题目三、编写代码总结 前言 编写程序&#xff0c;绘制一个展示支付宝月账单报告的饼图&#xff0c;实现过程如下&#xff1a; &#xff08;1&#xff09; 导入 matplotlib.pyplot 模块&#xff1b; &#xff08;2&#xff09;…

主成分分析在R语言中的简单应用:使用mvstats包

在数据科学领域&#xff0c;主成分分析&#xff08;PCA&#xff09;是一种广泛使用的技术&#xff0c;主要用于数据降维和探索性数据分析。PCA可以帮助我们发现数据中的模式&#xff0c;减少数据集的复杂性&#xff0c;同时保持数据中最重要的特征。本文将介绍如何在R语言中使用…

【经典算法】LeetCode112. 路径总和(Java/C/Python3/Go实现含注释说明,Easy)

作者主页&#xff1a; &#x1f517;进朱者赤的博客 精选专栏&#xff1a;&#x1f517;经典算法 作者简介&#xff1a;阿里非典型程序员一枚 &#xff0c;记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法&#xff08;公众号同名&#xff09; ❤️觉得文章还…

实时监控RTSP视频流并通过YOLOv5-seg进行智能分析处理

在完成RTSP推流之后&#xff0c;尝试通过开发板接收的视频流数据进行目标检测&#xff0c;编写了一个shell脚本实现该功能&#xff0c;关于视频推流和rknn模型的部署请看之前的内容或者参考官方的文档。 #!/bin/bash # 设置脚本使用的shell解释器为bashSEGMENT_DIR"./seg…

OceanBase开发者大会实录-陈文光:AI时代需要怎样的数据处理技术?

本文来自2024 OceanBase开发者大会&#xff0c;清华大学教授、蚂蚁技术研究院院长陈文光的演讲实录—《AI 时代的数据处理技术》。完整视频回看&#xff0c;请点击这里&#xff1e;> 大家好&#xff0c;我是清华大学、蚂蚁技术研究院陈文光&#xff0c;今天为大家带来《AI 时…

JUC线程

进程和线程&#xff1a; 进程&#xff08;Process&#xff09;是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配的基本单位&#xff0c;是操作系统结构的基础。 线程&#xff08;英语&#xff1a;thread&#xff09;是操作系统能够进行运算调度…

python基础语法--函数

一、函数概述 函数就是执行特定任务完成特定功能的一段代码。可以在程序中将某一段代码定义成函数&#xff0c;并指定一个函数名和接收的输入&#xff08;参数&#xff09;&#xff0c;这样就可以在程序的其他地方通过函数名多次调用并执行该段代码了。 每次调用执行后&#…
最新文章