How to Load and Parse URDF, SDF, MJCF, and SKEL Files Using DART's IO Module
DART provides a unified dart::io API that automatically detects and parses URDF, SDF, MJCF, and SKEL files through the readWorld() and readSkeleton() functions, eliminating the need to manually select format-specific parsers.
The dartsim/dart robotics toolkit consolidates robot description loading into a single interface located in dart/io/read.hpp. Whether you are importing a URDF robot model, an SDF simulation world, a MuJoCo MJCF file, or DART's native SKEL format, the IO module handles format inference, resource retrieval, and parser dispatch automatically.
Unified IO API Overview
DART's modern IO layer centers on two high-level functions defined in dart/io/read.hpp and implemented in dart/io/read.cpp:
readWorld(const common::Uri&, const ReadOptions&)– returns asimulation::WorldPtrreadSkeleton(const common::Uri&, const ReadOptions&)– returns adynamics::SkeletonPtr
Both functions follow a three-stage pipeline implemented in readWorld (lines 40-57). First, resolveOptions() normalizes the ReadOptions object, ensuring a valid ResourceRetriever exists and collecting package directories. Second, inferFormat() determines the file type by examining the extension via inferFormatFromExtension() or parsing the XML root element via inferFormatFromXmlRoot() (lines 96-121). Finally, a switch statement dispatches to the appropriate concrete parser based on the resolved ModelFormat enum.
Supported File Formats and Parsers
DART supports four major description formats through dedicated parser classes:
| Format | Extension / Root Element | Parser Class |
|---|---|---|
| SKEL | .skel (<skel>) |
utils::SkelParser |
| SDF | .sdf / .world (<sdf>) |
utils::SdfParser |
| URDF | .urdf (<robot>) |
utils::UrdfParser |
| MJCF | .xml (<mujoco>) |
utils::MjcfParser |
The dispatch logic in read.cpp (lines 57-95) routes requests to these parsers while handling format-specific configuration such as URDF package resolution and SDF default root joint types.
How Format Detection Works
When you pass ModelFormat::Auto (the default), DART employs a two-tier inference strategy:
- Extension Analysis:
inferFormatFromExtension()maps file suffixes (.skel,.sdf,.urdf,.mjcf) to their respective formats. - XML Root Inspection: If the extension is ambiguous (e.g.,
.xml),inferFormatFromXmlRoot()uses tinyxml2 to examine the root tag—<skel>,<sdf>,<robot>, or<mujoco>—and identifies the correct parser.
You can bypass automatic detection by explicitly setting the format in ReadOptions:
dart::io::ReadOptions opt;
opt.format = dart::io::ModelFormat::Urdf;
Loading Worlds and Skeletons
Auto-Detecting File Format
The simplest invocation relies on automatic format inference to load any supported file type:
#include <dart/io/read.hpp>
int main()
{
const dart::common::Uri uri("file:///path/to/model.sdf");
// Auto-detects format and returns a WorldPtr
auto world = dart::io::readWorld(uri);
if (!world) {
std::cerr << "Failed to load world!\n";
return 1;
}
std::cout << "World contains " << world->getNumSkeletons()
<< " skeleton(s).\n";
}
Loading URDF with Package Directories
URDF files frequently reference resources via package:// URIs. DART resolves these through the PackageResourceRetriever configured via ReadOptions::addPackageDirectory():
#include <dart/io/read.hpp>
int main()
{
dart::io::ReadOptions opt;
opt.format = dart::io::ModelFormat::Urdf;
opt.addPackageDirectory("my_robot", "/home/user/robot_pkg");
const dart::common::Uri uri("file:///home/user/robot_pkg/robot.urdf");
auto skeleton = dart::io::readSkeleton(uri, opt);
if (!skeleton) {
std::cerr << "Could not read URDF skeleton.\n";
return 1;
}
std::cout << "Skeleton name: " << skeleton->getName() << '\n';
}
The getUrdfResourceRetriever() function (lines 191-212 in read.cpp) constructs a resource retriever chain that intercepts package:// schemes and maps them to the directories specified in opt.urdfPackageDirectories.
Loading SDF with Custom Root Joint Types
By default, SDF models receive floating root joints. You can enforce fixed joints via ReadOptions:
#include <dart/io/read.hpp>
int main()
{
dart::io::ReadOptions opt;
opt.sdfDefaultRootJointType = dart::io::RootJointType::Fixed;
const dart::common::Uri uri("file:///my/sim.world");
auto world = dart::io::readWorld(uri, opt);
}
The translation from io::RootJointType to utils::SdfParser::RootJointType occurs inside readWorld (lines 262-268).
Loading MJCF Models
MuJoCo XML (MJCF) files are treated as world descriptions. Access individual skeletons through the returned world pointer:
#include <dart/io/read.hpp>
int main()
{
const dart::common::Uri uri("file:///tmp/mjcf_model.xml");
// MJCF loads as a world containing the model
auto world = dart::io::readWorld(uri);
auto skel = world->getSkeleton(0);
}
readWorld dispatches to utils::MjcfParser::readWorld (lines 77-78 in read.cpp).
Error Handling with tryReadWorld
For exception-free error handling, use the result-based API that wraps readWorld:
#include <dart/io/read.hpp>
#include <iostream>
int main()
{
const dart::common::Uri uri("file:///missing/file.skel");
auto result = dart::io::tryReadWorld(uri);
if (!result) {
std::cerr << "Error: " << result.error().what() << '\n';
return 1;
}
auto world = result.value();
std::cout << "Loaded world with " << world->getNumSkeletons()
<< " skeleton(s).\n";
}
tryReadWorld returns a common::Result object containing either the WorldPtr or an error message.
Key Classes and Configuration
ReadOptions (defined in dart/io/read.hpp, lines 85-108) serves as the configuration container:
format– ExplicitModelFormatenum value orAutoresourceRetriever– Customcommon::ResourceRetrieverfor network or embedded resourcesurdfPackageDirectories– Map of package names to filesystem pathssdfDefaultRootJointType– Specifies fixed or floating root joints for SDF
common::ResourceRetriever abstracts file access, enabling support for file://, dart://, and package:// schemes through a composite retriever pattern defined in dart/common/resource_retriever.hpp.
Summary
- Unified API: Use
dart::io::readWorld()anddart::io::readSkeleton()fromdart/io/read.hppto load any supported format. - Automatic Detection: DART infers file types from extensions (
.urdf,.sdf,.skel,.mjcf) or XML root elements (<robot>,<sdf>,<skel>,<mujoco>). - URDF Packages: Configure
package://resolution viaReadOptions::addPackageDirectory(). - SDF Configuration: Control root joint behavior with
ReadOptions::sdfDefaultRootJointType. - Error Handling: Use
tryReadWorld()ortryReadSkeleton()for result-based error propagation instead of exceptions.
Frequently Asked Questions
How does DART determine which parser to use for a file?
DART examines the file extension first through inferFormatFromExtension(); if the extension is generic (like .xml), it parses the XML root element using inferFormatFromXmlRoot() to identify whether the document contains <robot>, <sdf>, <skel>, or <mujoco> tags. This logic resides in dart/io/read.cpp lines 96-121.
Can I force DART to use a specific parser instead of auto-detecting?
Yes. Set the format field in your ReadOptions object to a specific ModelFormat enum value such as ModelFormat::Urdf, ModelFormat::Sdf, ModelFormat::Mjcf, or ModelFormat::Skel before calling readWorld() or readSkeleton().
Why does my URDF file fail to load resources using package:// URIs?
The default resource retriever does not resolve package:// schemes. You must populate ReadOptions::urdfPackageDirectories using addPackageDirectory() to map package names to absolute filesystem paths. The IO module then constructs a PackageResourceRetriever (lines 191-212 in read.cpp) to handle these requests.
Does DART support loading individual skeletons from MJCF files?
No. The MJCF parser (utils::MjcfParser) only exposes a world-loading interface. You must call readWorld() and then extract skeletons using world->getSkeleton(index) or iterate through all skeletons in the returned world.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →