SDFormat文件Python解析器。
项目描述
PySDF (python-sdformat)
PySDF是一套为SDFormat XML提供的Python绑定。其理念是提供一个感觉上与XML一样快(或更快)的方法来处理SDF,同时增加了语法高亮、自动完成、验证和一些程度的验证。
当前的绑定可以从任何版本读取SDF并将其解析成通用表示。默认情况下,修改不会被验证或验证,这提供了更多的灵活性,但将来会添加可选的验证和验证方法。
安装
pip install python-sdformat
使用
绑定的元素根据官方SDF规范分组,但有少数例外。这意味着您可以通过遵循规范的嵌套来找到您需要的项目,例如,/sensor/imu/angular_velocity/x
元素对应于类pysdf.Sensor.Imu.AngularVelocity.X
,在SDF内部可以访问为parsed_sensor.imu.angular_velocity.x
。此之前提到的例外是
- 绑定使用
snake_case
作为变量名,使用CamelCase
作为类名 - 如果一个元素可能在父元素内部出现多次,其相应的属性将是一个元组,其名称将是复数,例如
world.models[idx]
。 - 元素
Pose
、Frame
、Plugin
和Include
在多个元素中频繁出现,并被提升到顶级,即您将使用pysdf.Pose
而不是pysdf.Model.Pose
。
示例
基本读取和写入
from pysdf import SDF
sample_sdf = """<?xml version="1.0" ?>
<sdf version="1.6">
<model name="empty_axis">
<link name="link1" />
<link name="link2" />
<joint name="joint" type="fixed">
<parent>link1</parent>
<child>link2</child>
<axis/>
</joint>
</model>
</sdf>
"""
# string round-trip
parsed = SDF.from_xml(sample_sdf)
sdf_string = parsed.to_xml()
# file round-trip
parsed.to_file("sample.sdf")
parsed = SDF.from_file("sample.sdf")
# prettify/reformat SDF to have nice indentation
parsed = SDF.from_file("sample.sdf", remove_blank_text=True)
parsed.to_file("sample.sdf", pretty_print=True)
手动构建SDF
from pysdf import SDF, Link, Joint, Model
reference_sdf = """
<sdf version="1.6">
<model name="empty_axis">
<link name="link1" />
<link name="link2" />
<joint name="joint" type="fixed">
<parent>link1</parent>
<child>link2</child>
</joint>
</model>
</sdf>
"""
element = SDF(
Model(
Link(name="link1"),
Link(name="link2"),
Joint(
Joint.Parent(text="link1"),
Joint.Child(text="link2"),
# attributes are set at the end
# because python only accepts kwargs at the end.
name="joint",
type="fixed",
),
name="empty_axis",
),
version="1.6",
)
element.to_file("sample.sdf", pretty_print=True)
基本修改
from pysdf import Link, State, Model, Link
# Complex elements (with own children) are added on first read
element = Link()
element.to_xml() # "<link/>"
element.inertial
element.to_xml() # "<link><inertial/></link>"
# Simple elements (basic types) are added on first write (__set__):
element.inertial.mass
element.to_xml() # "<link><inertial/></link>"
element.inertial.mass = 5.0
element.to_xml()
# "<link><inertial><mass>5.0</mass></inertial></link>"
# default values are populated where applicable
assert element.inertial.inertia.ixx == 1.0
# Where possible types are converted automatically
element = State.Model()
element.scale # (1.0, 1.0, 1.0), tuple
element.scale = "1 2 3"
assert element.scale == (1.0, 2.0, 3.0)
element.scale = [5, 5, 5]
assert element.scale == (5.0, 5.0, 5.0)
# Inserting children works from sequences of kwargs
element = Model()
element.add(Link(name="test"))
element.add(Link(name="test2"), Link(name="test3"))
element.to_xml()
# '<model><link name="test"/><link name="test2"/><link name="test3"/><pose/></model>'
完整修改示例
from pysdf import SDF, Link
sample_sdf = """<?xml version="1.0" ?>
<sdf version="1.6">
<model name="empty_axis">
<link name="link1" />
<link name="link2" />
<joint name="joint" type="fixed">
<parent>link1</parent>
<child>link2</child>
</joint>
</model>
</sdf>
"""
parsed = SDF.from_xml(sample_sdf, remove_blank_text=True)
model = parsed.model
model.name = "modified_model"
model.links[1].add(Link.ParticleEmitter(
Link.ParticleEmitter.Emitting(text="true"),
name="my_emitter",
type="box"
))
parsed.to_file("sample.sdf", pretty_print=True)
迭代和过滤
您可以使用element.iter()
递归地遍历子树(广度优先)的所有子元素。iter()
还接受一个filter
关键字参数,允许您仅返回从调用者到子元素具有所需路径的子元素。筛选器匹配路径的尾部,路径元素由/
字符分隔,任何SDF标签都是有效的路径元素。这允许轻松选择和批量编辑特定的子元素,例如,使用filter="pose"
选择SDF中的所有姿态元素,或者使用filter="model/pose"
选择所有直接属于模型的姿态元素(模型的姿态)。
对于这个长的SDF示例,我想展示一些更实际的例子。
from pysdf import SDF
import numpy as np
# taken from:
# https://github.com/ignitionrobotics/sdformat/blob/sdf12/test/sdf/joint_nested_parent_child.sdf
large_example = """
<sdf version="1.8">
<model name="joint_nested_parent_child">
<model name="M1">
<link name="L1">
<pose>0 0 1 0 0 0</pose>
</link>
<link name="L2">
<pose>1 1 0 0 0 0</pose>
</link>
<frame name="F1">
<pose>1 0 0 0 0 0</pose>
</frame>
<model name="M2">
<pose>0 0 1 1.570796326790 0 0</pose>
<link name="L1"/>
</model>
</model>
<link name="L1">
<pose>0 0 10 0 1.57079632679 0</pose>
</link>
<frame name="F1" attached_to="M1::L1">
<pose>1 0 0 0 0 0</pose>
</frame>
<!-- Joint with a parent link in a nested model -->
<joint name="J1" type="fixed">
<pose>1 0 0 0 0 0</pose>
<parent>M1::L1</parent>
<child>L1</child>
</joint>
<!-- Joint with a sibling parent frame which is attached to a link in a nested model -->
<joint name="J2" type="fixed">
<pose>0 1 0 0 0 0</pose>
<parent>F1</parent>
<child>L1</child>
</joint>
<!-- Joint with a child link in a nested model -->
<joint name="J3" type="fixed">
<pose>0 0 1 0 0 0</pose>
<parent>L1</parent>
<child>M1::L2</child>
</joint>
<!-- Joint with a child frame in a nested model -->
<joint name="J4" type="fixed">
<pose>0 0 1 0 0 0</pose>
<parent>L1</parent>
<child>M1::F1</child>
</joint>
<!-- Joint with a child model in a nested model -->
<joint name="J5" type="fixed">
<pose>0 0 1 0 0 0</pose>
<parent>L1</parent>
<child>M1::M2</child>
</joint>
<!-- Joint with a nested model frame as a parent -->
<joint name="J6" type="fixed">
<pose>0 0 1 0 0 0</pose>
<parent>M1::__model__</parent>
<child>L1</child>
</joint>
<!-- Joint with a nested model frame as a child -->
<joint name="J7" type="fixed">
<pose>0 0 1 0 0 0</pose>
<parent>L1</parent>
<child>M1::__model__</child>
</joint>
</model>
</sdf>
"""
# remove_blank_text strips whitespace to allow neat formatting
# later on
element = SDF.from_xml(large_example, remove_blank_text=True)
element.version = "1.9" # v1.9 supports pose/@degrees
# convert all poses to degrees
for pose in element.iter("pose"):
pose.degrees = True
pose_ndarray = np.fromstring(pose.text, count=6, sep=" ")
rotation_rad = pose_ndarray[3:]
rotation_deg = rotation_rad / (2*np.pi) * 360
pose_ndarray[3:] = rotation_deg
pose.text = " ".join(map(str, pose_ndarray))
# turn on self-collision for all links in the nested model
for link in element.iter("model/model/link"):
link.self_collide = True
# turn self-collision off (using a different syntax)
for link in element.model.iter("link"):
link.self_collide = False
# offset all links by some vector
for pose in element.iter("link/pose"):
pose_ndarray = np.fromstring(pose.text, count=6, sep=" ")
pose_ndarray[:3] += (0, 0, 1)
pose.text = " ".join(map(str, pose_ndarray))
element.to_file("sample.sdf", pretty_print=True)
项目详情
下载文件
下载适合您平台的文件。如果您不确定该选择哪一个,请了解有关安装包的更多信息。
源分布
python-sdformat-0.4.0.tar.gz (20.3 kB 查看哈希值)
构建分布
python_sdformat-0.4.0-py3-none-any.whl (21.2 kB 查看哈希值)
关闭
python-sdformat-0.4.0.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 8cdc21ce9de0de775787b8052d21d49f2d9be91e1d011ad82081796876dea8ee |
|
MD5 | a626b084b6482791574077230d0fca50 |
|
BLAKE2b-256 | ef3b9b35213bbc3d05858ab40bc91aa628ce826ddd124dd03df0212f8d9d9ad4 |
关闭
python_sdformat-0.4.0-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 089c6a970d82990a6bce7a86fa0368f502f62cc76840bcf9094eb011ffac1bf1 |
|
MD5 | 2b41d3295bbaf3529feadd2147ae4fc9 |
|
BLAKE2b-256 | 6ce5ae0c9206cf631d18c717ce59bff99ffbc76aaf5e485fbb802b40e824c6ef |