Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URDFTools can take a Collection<InputStream> as the first parameters to allow for splitting the URDF up into multiple files #172

Merged
merged 24 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
26bcbdf
Initial attempt at getting the URDF to be split up, it loads properly…
PotatoPeeler3000 Mar 12, 2024
2dfe0e0
Added Gazebo models so the URDF is loaded properly
PotatoPeeler3000 Mar 12, 2024
ecdc44e
Fix breaking tests
PotatoPeeler3000 Mar 12, 2024
04ce97e
Fix merging all the gazebos
PotatoPeeler3000 Mar 12, 2024
0b4374f
Added fix for handleing implicit joint definitions
PotatoPeeler3000 Mar 12, 2024
3559164
Files cleanup because AI isn't the brightest
PotatoPeeler3000 Mar 12, 2024
024de25
Added tests for loading multiple URDF's, created a couple simple URDF…
PotatoPeeler3000 Mar 21, 2024
2414126
Prevent loading duplicate joints by checking if they already exist
PotatoPeeler3000 Mar 21, 2024
8ea0cd2
Updated tests, added a few more, prints are more clear
PotatoPeeler3000 Mar 22, 2024
429fe06
More testing on real robot, still have lots of problems with logger
Mar 23, 2024
28958c7
Got working while logging, need to go through and combine commits
PotatoPeeler3000 Mar 25, 2024
f8b5db4
Tested loading a real log without the composed robot
PotatoPeeler3000 Mar 25, 2024
c052427
More comments
PotatoPeeler3000 Mar 25, 2024
444d3a5
Addressed PR comments. Added more documentation, updated variable nam…
PotatoPeeler3000 Apr 2, 2024
7c369cb
Added method to check if the model is a root model, added docs
PotatoPeeler3000 Apr 2, 2024
4e70560
Update tests to check that the correct name is gotten
PotatoPeeler3000 Apr 2, 2024
0c1eb00
Change method to take in a parent and a child, and the name and paren…
PotatoPeeler3000 Apr 2, 2024
c2b182e
Update RobotModelLoader.java to before PR
PotatoPeeler3000 Apr 2, 2024
f301869
Save still needs work
PotatoPeeler3000 Apr 4, 2024
47e9a82
Fix tests, add better doc for them
Apr 5, 2024
8f2c61b
Fix comments
Apr 5, 2024
f2b98ca
Inline method, add new constructor to allow for more options
Apr 5, 2024
223f9bd
Don't need to worry about parent model being set because the URDF can…
Apr 5, 2024
e818a89
Don't initialize the variable, set to null just needed for reference
Apr 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public static URDFModel loadURDFModel(File urdfFile, Collection<String> resource
try
{
// Internally, the unmarshaller does "new BufferedInputStream(new FileInputStream(urdfFile)" (see AbstractUnmarshallerImpl), no need to have 2 distinct implementations.
return loadURDFModel(new BufferedInputStream(new FileInputStream(urdfFile)), resourceDirectories, null, parserProperties);
return loadURDFModel(Collections.singletonList(new BufferedInputStream(new FileInputStream(urdfFile))), resourceDirectories, null, parserProperties);
}
catch (FileNotFoundException e)
{
Expand All @@ -189,14 +189,19 @@ public static URDFModel loadURDFModel(File urdfFile, Collection<String> resource
* @return the model.
*/
public static URDFModel loadURDFModel(InputStream inputStream, Collection<String> resourceDirectories, ClassLoader resourceClassLoader) throws JAXBException
{
return loadURDFModel(Collections.singletonList(inputStream), resourceDirectories, resourceClassLoader, DEFAULT_URDF_PARSER_PROPERTIES);
}

public static URDFModel loadURDFModel(Collection<InputStream> inputStream, Collection<String> resourceDirectories, ClassLoader resourceClassLoader) throws JAXBException
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
{
return loadURDFModel(inputStream, resourceDirectories, resourceClassLoader, DEFAULT_URDF_PARSER_PROPERTIES);
}

/**
* Parse a {@link URDFModel} from the given input stream.
*
* @param inputStream the stream to be loaded.
* @param inputStreams the stream to be loaded.
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
* @param resourceDirectories paths to resource directories. This allows to search for resources
* that are defined outside the {@code inputStream}.
* @param resourceClassLoader the class loader is used to retrieve the resources. If the resources
Expand All @@ -209,66 +214,188 @@ public static URDFModel loadURDFModel(InputStream inputStream, Collection<String
* done.
* @return the model.
*/
public static URDFModel loadURDFModel(InputStream inputStream,
public static URDFModel loadURDFModel(Collection<InputStream> inputStreams,
Collection<String> resourceDirectories,
ClassLoader resourceClassLoader,
URDFParserProperties parserProperties) throws JAXBException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add another factory for backward compatibility with the signature:
URDFModel loadURDFModel(InputStream inputStream, Collection<String> resourceDirectories, ClassLoader resourceClassLoader, URDFParserProperties parserProperties)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this... lets see if I can link it....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't link it but its on line 210

{
Set<String> allResourceDirectories = new HashSet<>(resourceDirectories);
JAXBContext context = JAXBContext.newInstance(URDFModel.class);
Unmarshaller um = context.createUnmarshaller();

URDFModel combinedURDFModel = new URDFModel();

try
{
Set<String> allResourceDirectories = new HashSet<>(resourceDirectories);
URDFModel urdfModel;
JAXBContext context = JAXBContext.newInstance(URDFModel.class);
Unmarshaller um = context.createUnmarshaller();

if (!parserProperties.ignoreNamespace)
for (InputStream inputStream : inputStreams)
{
urdfModel = (URDFModel) um.unmarshal(inputStream);
URDFModel urdfModel;
if (!parserProperties.ignoreNamespace)
{
urdfModel = (URDFModel) um.unmarshal(inputStream);
}
else
{
InputSource is = new InputSource(inputStream);
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);

XMLReader reader;
try
{
reader = sax.newSAXParser().getXMLReader();
}
catch (ParserConfigurationException | SAXException e)
{
throw new JAXBException(e);
}

SAXSource source = new SAXSource(reader, is);
urdfModel = (URDFModel) um.unmarshal(source);
}

resolvePaths(urdfModel, allResourceDirectories, resourceClassLoader);

if (!parserProperties.linksToIgnore.isEmpty() && urdfModel.getLinks() != null)
{
urdfModel.getLinks().removeIf(link -> parserProperties.linksToIgnore.contains(link.getName()));
}

if (!parserProperties.jointsToIgnore.isEmpty() && urdfModel.getJoints() != null)
{
urdfModel.getJoints().removeIf(joint -> parserProperties.jointsToIgnore.contains(joint.getName()));
}

if (!parserProperties.parseSensors && urdfModel.getGazebos() != null)
{
urdfModel.getGazebos().removeIf(gazebo -> gazebo.getSensor() != null);
}

if (parserProperties.handleImplicitJointDefinitions)
handleImplicitJointDefinitions(urdfModel);

// Merge the current URDFModel with the combined URDFModel
combinedURDFModel = mergeURDFModels(combinedURDFModel, urdfModel);
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
}
else
}
finally
{
for (InputStream inputStream : inputStreams)
{
InputSource is = new InputSource(inputStream);
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
XMLReader reader;

try
{
reader = sax.newSAXParser().getXMLReader();
inputStream.close();
}
catch (SAXException | ParserConfigurationException e)
catch (IOException e)
{
throw new JAXBException(e);
LogTools.error(e.getMessage());
}

SAXSource source = new SAXSource(reader, is);
urdfModel = (URDFModel) um.unmarshal(source);
}
resolvePaths(urdfModel, allResourceDirectories, resourceClassLoader);
}

if (!parserProperties.linksToIgnore.isEmpty() && urdfModel.getLinks() != null)
urdfModel.getLinks().removeIf(urdfLink -> parserProperties.linksToIgnore.contains(urdfLink.getName()));
if (!parserProperties.jointsToIgnore.isEmpty() && urdfModel.getJoints() != null)
urdfModel.getJoints().removeIf(urdfJoint -> parserProperties.jointsToIgnore.contains(urdfJoint.getName()));
if (!parserProperties.parseSensors)
urdfModel.getGazebos().removeIf(gazebo -> gazebo.getSensor() != null);
return combinedURDFModel;
}

if (parserProperties.handleImplicitJointDefinitions)
handleImplicitJointDefinitions(urdfModel);
private static URDFModel mergeURDFModels(URDFModel baseModel, URDFModel additionalModel)
{
// Create a new URDFModel to hold the merged components
URDFModel mergedModel = new URDFModel();
List<String> mergedLinkNames = new ArrayList<>();
List<String> mergedJointNames = new ArrayList<>();
List<String> mergedGazeboNames = new ArrayList<>();
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved

return urdfModel;
// Set name of merged urdf
if (baseModel.getName() == null)
{
mergedModel.setName(additionalModel.getName());
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
}
finally
else
{
try
mergedModel.setName(baseModel.getName());
}

// Merge links that haven't already been added
List<URDFLink> mergedLinks = new ArrayList<>();
if (baseModel.getLinks() != null)
{
mergedLinks.addAll(baseModel.getLinks());
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
for (URDFLink link : baseModel.getLinks())
{
inputStream.close();
mergedLinkNames.add(link.getName());
}
catch (IOException e)
}
if (additionalModel.getLinks() != null)
{
for (URDFLink link : additionalModel.getLinks())
{
LogTools.error(e.getMessage());
if (!mergedLinkNames.contains(link.getName()))
{
mergedLinks.add(link);
mergedLinkNames.add(link.getName());
}
else
{
LogTools.warn("Link ({}) has already been added to urdf, skipping link", link.getName());
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
mergedModel.setLinks(new ArrayList<>(mergedLinks));

// Merge joints that haven't already been added
List<URDFJoint> mergedJoints = new ArrayList<>();
if (baseModel.getJoints() != null)
{
mergedJoints.addAll(baseModel.getJoints());
for (URDFJoint joint : baseModel.getJoints())
{
mergedJointNames.add(joint.getName());
}
}
if (additionalModel.getJoints() != null)
{
for (URDFJoint joint : additionalModel.getJoints())
{
if (!mergedJointNames.contains(joint.getName()))
{
mergedJoints.add(joint);
mergedJointNames.add(joint.getName());
}
else
{
LogTools.warn("Joint ({}) has already been added to urdf, skipping joint", joint.getName());
}
}
}
mergedModel.setJoints(new ArrayList<>(mergedJoints));

// Merge Gazebos that haven't already been added
List<URDFGazebo> mergedGazebos = new ArrayList<>();
if (baseModel.getGazebos() != null)
{
mergedGazebos.addAll(baseModel.getGazebos());
for (URDFGazebo gazebo : baseModel.getGazebos())
{
mergedGazeboNames.add(gazebo.getReference());
}
}
if (additionalModel.getGazebos() != null)
{
for (URDFGazebo gazebo : additionalModel.getGazebos())
{
if (!mergedGazeboNames.contains(gazebo.getReference()))
{
mergedGazebos.add(gazebo);
mergedGazeboNames.add(gazebo.getReference());
}
else
{
LogTools.warn("Gazebo ({}) has already been added to urdf, skipping gazebo", gazebo.getReference());
}
}
}
mergedModel.setGazebos(new ArrayList<>(mergedGazebos));

return mergedModel;
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -2398,7 +2525,7 @@ public static URDFLinkReference toURDFLinkReference(RigidBodyDefinition rigidBod
/**
* This class provides extra properties for tweaking operations when parsing a URDF file. It is used
* in both
* {@link URDFTools#loadURDFModel(InputStream, Collection, ClassLoader, URDFParserProperties)} and
* {@link URDFTools#loadURDFModel(Collection, Collection, ClassLoader, URDFParserProperties)} and
* {@link URDFTools#toRobotDefinition(URDFModel, URDFParserProperties)}.
*
* @author Sylvain Bertrand
Expand All @@ -2425,7 +2552,7 @@ public static class URDFParserProperties
* </p>
*
* @param ignoreNamespace {@code true} to ignore namespaces. Recommended value {@code false}.
* @see URDFTools#loadURDFModel(InputStream, Collection, ClassLoader, URDFParserProperties)
* @see URDFTools#loadURDFModel(Collection, Collection, ClassLoader, URDFParserProperties)
*/
public void setIgnoreNamespace(boolean ignoreNamespace)
{
Expand All @@ -2439,7 +2566,7 @@ public void setIgnoreNamespace(boolean ignoreNamespace)
* URDF files.
*
* @param nameOfJointToIgnore the name of a joint to be ignored when parsing the URDF file.
* @see URDFTools#loadURDFModel(InputStream, Collection, ClassLoader, URDFParserProperties)
* @see URDFTools#loadURDFModel(Collection, Collection, ClassLoader, URDFParserProperties)
*/
public void addJointToIgnore(String nameOfJointToIgnore)
{
Expand All @@ -2453,7 +2580,7 @@ public void addJointToIgnore(String nameOfJointToIgnore)
* URDF files.
*
* @param nameOfLinkToIgnore the name of a link to be ignored when parsing the URDF file.
* @see URDFTools#loadURDFModel(InputStream, Collection, ClassLoader, URDFParserProperties)
* @see URDFTools#loadURDFModel(Collection, Collection, ClassLoader, URDFParserProperties)
*/
public void addLinkToIgnore(String nameOfLinkToIgnore)
{
Expand Down
69 changes: 69 additions & 0 deletions scs2-examples/src/main/resources/urdf/limbA.urdf
PotatoPeeler3000 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" ?>
<robot name="limbA" xmlns:xacro="http://ros.org/wiki/xacro">

<joint name="jointA" type="revolute">
<axis xyz="0 1 0"/>
<origin rpy="0 0 0" xyz="0.1 0 0.2"/>
<parent link="baseLink"/>
<child link="bodyAB"/>
<limit effort="190" lower="-1.1" upper="0.4141" velocity="5.89"/>
</joint>
<link name="bodyAB">
<inertial>
<mass value="0.025"/>
<origin rpy="0 0 0" xyz="0 0 0.1"/>
<inertia ixx="1.0e-8" ixy="0" ixz="0" iyy="1.0e-8" iyz="0" izz="1.0e-8"/>
</inertial>
<visual>
<geometry>
<cylinder radius="0.0125" length="0.4"/>
</geometry>
<material>
<color rgba="0.92 0.85 0.2 1"/>
</material>
<origin rpy="0 0 0" xyz="0 0 0.1"/>
</visual>
</link>
<joint name="jointB" type="revolute">
<axis xyz="0 1 0"/>
<origin rpy="0 0 0" xyz="0 0 0.2"/>
<parent link="bodyAB"/>
<child link="endEffectorLink"/>
<limit effort="190" lower="-1.1" upper="0.4141" velocity="5.89"/>
<mimic joint="jointA" multiplier="1.25" offset="0"/>
</joint>
<link name="endEffectorLink">
<inertial>
<mass value="5"/>
<origin rpy="0 0 0" xyz="0 0 0.1"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.005"/>
</inertial>
<visual>
<geometry>
<box size="0.0125 0.0125 0.2"/>
</geometry>
<material>
<color rgba="0.2 0.7 0.9 1"/>
</material>
<origin rpy="0 0 0" xyz="0 0 0.1"/>
</visual>
</link>
<joint name="jointC" type="revolute">
<axis xyz="1 0 0"/>
<origin rpy="0 0 0" xyz="0.1 0 0.0"/>
<parent link="baseLink"/>
<child link="head"/>
<limit effort="190" lower="-1.1" upper="0.4141" velocity="5.89"/>
</joint>
<link name="head">
<visual>
<geometry>
<sphere radius="0.1"/>
</geometry>
<material>
<color rgba="0.2 0.7 0.9 1"/>
</material>
<origin rpy="0 0 0" xyz="0.1 0 0.3"/>
</visual>
</link>
</robot>
Loading
Loading