├── .gitignore
├── Makefile
├── test_data
├── README.md
└── robots
│ └── bh280.urdf
├── cmake
└── FindTinyXML2.cmake
├── bin
└── fix_stls
├── catkin-env-hooks
└── 20.or_urdf.sh.in
├── scripts
├── startup_herb.py
├── startup_tim.py
└── load.py
├── .travis.yml
├── package.xml
├── empty.iv
├── src
├── catkin_finder.h
├── boostfs_helpers.h
├── urdf_loader.h
├── catkin_finder.cpp
├── picojson.h
└── urdf_loader.cpp
├── LICENSE
├── CMakeLists.txt
├── CHANGELOG.rst
├── tests
└── test_urdf_loader.py
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | include $(shell rospack find mk)/cmake.mk
2 |
--------------------------------------------------------------------------------
/test_data/README.md:
--------------------------------------------------------------------------------
1 | Many of the robot files here have certain tags removed so as to not depend on herbpy. They should not be treated as complete URDF files and should only be used for testing OR_URDF.
2 |
--------------------------------------------------------------------------------
/cmake/FindTinyXML2.cmake:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2014 Andrew Kelley
2 | # This file is MIT licensed.
3 | # See http://opensource.org/licenses/MIT
4 |
5 | # TinyXML2_FOUND
6 | # TinyXML2_INCLUDE_DIRS
7 | # TinyXML2_LIBRARIES
8 |
9 | find_path(TinyXML2_INCLUDE_DIRS NAMES tinyxml2.h)
10 | find_library(TinyXML2_LIBRARIES NAMES tinyxml2)
11 |
12 | include(FindPackageHandleStandardArgs)
13 | find_package_handle_standard_args(TinyXML2 DEFAULT_MSG TinyXML2_LIBRARIES TinyXML2_INCLUDE_DIRS)
14 |
15 | mark_as_advanced(TinyXML2_INCLUDE_DIRS TinyXML2_LIBRARIES)
16 |
--------------------------------------------------------------------------------
/bin/fix_stls:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Repairs binary STL files to make them smaller and easier to import
4 | #
5 | # author: Pras Velagapudi
6 | #
7 | # Usage: ./cleanup.sh [model directory]
8 | #
9 |
10 | # Test for a provided directory, otherwise use current directory
11 | if test "$1" == ""
12 | then
13 | STL_DIR=`pwd`
14 | else
15 | STL_DIR="$1"
16 | fi
17 |
18 | # Replace the 'solid' keyword that sometimes shows up at the start of
19 | # binary STL files, causing them to be imported incorrectly as ASCII.
20 | sed -i 's/^solid/MODEL/g' `find $1 -iname \*.stl`
21 |
--------------------------------------------------------------------------------
/catkin-env-hooks/20.or_urdf.sh.in:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # determine if we're in the devel or install space
4 | if [ "@DEVELSPACE@" = "True" -o "@DEVELSPACE@" = "true" ]
5 | then
6 | PLUGINS=@CATKIN_DEVEL_PREFIX@/lib/openrave-@OpenRAVE_LIBRARY_SUFFIX@
7 | else
8 | PLUGINS=@CMAKE_INSTALL_PREFIX@/lib/openrave-@OpenRAVE_LIBRARY_SUFFIX@
9 | fi
10 |
11 | # prepend to paths (if not already there)
12 | # from http://unix.stackexchange.com/a/124447
13 | case ":${OPENRAVE_PLUGINS:=$PLUGINS}:" in
14 | *:$PLUGINS:*) ;;
15 | *) OPENRAVE_PLUGINS="$PLUGINS:$OPENRAVE_PLUGINS" ;;
16 | esac
17 |
18 | export OPENRAVE_PLUGINS
19 |
--------------------------------------------------------------------------------
/scripts/startup_herb.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Loads a URDF to test the loader.
4 | #
5 | import os;
6 |
7 | # Get this script path (in or_urdf) and add it to the openrave path
8 | or_urdf_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')
9 | or_plugin_path = os.getenv('OPENRAVE_PLUGINS', '')
10 | os.environ['OPENRAVE_PLUGINS'] = os.pathsep.join([or_urdf_path, or_plugin_path])
11 |
12 | import openravepy
13 |
14 | env = openravepy.Environment()
15 | env.SetViewer('qtcoin')
16 | plugin = openravepy.RaveCreateModule(env, "urdf")
17 |
18 | plugin.SendCommand("load /homes/pkv/ros/local/systemconf/herb2.urdf")
19 | #plugin.SendCommand("load /homes/pkv/ros/local/apps/librarian/tema_tim_description/robots/tim/tim.urdf")
20 | #plugin.SendCommand("load /homes/pkv/ros/local/herb_urdf/robots/herb_urdf.URDF")
21 |
--------------------------------------------------------------------------------
/scripts/startup_tim.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Loads a URDF to test the loader.
4 | #
5 | import os;
6 |
7 | # Get this script path (in or_urdf) and add it to the openrave path
8 | or_urdf_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib')
9 | or_plugin_path = os.getenv('OPENRAVE_PLUGINS', '')
10 | os.environ['OPENRAVE_PLUGINS'] = os.pathsep.join([or_urdf_path, or_plugin_path])
11 |
12 | import openravepy
13 |
14 | env = openravepy.Environment()
15 | env.SetViewer('qtcoin')
16 | plugin = openravepy.RaveCreateModule(env, "urdf")
17 |
18 | #plugin.SendCommand("load /homes/pkv/ros/local/systemconf/herb2.urdf")
19 | plugin.SendCommand("load /homes/pkv/ros/local/apps/librarian/tema_tim_description/robots/tim/tim.urdf")
20 | #plugin.SendCommand("load /homes/pkv/ros/local/herb_urdf/robots/herb_urdf.URDF")
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | sudo: required
3 | language: generic
4 | cache:
5 | - apt
6 | env:
7 | global:
8 | - REPOSITORY=or_urdf
9 | - DISTRIBUTION=https://raw.githubusercontent.com/personalrobotics/pr-rosinstalls/feature/rosdistro/repositories.yml
10 | # Install test fixture dependencies.
11 | before_install:
12 | - mkdir -p "${HOME}/workspace/src"
13 | - cd "${HOME}/workspace"
14 | - curl -sSo distribution.yml "${DISTRIBUTION}"
15 | - git clone https://github.com/personalrobotics/pr-cleanroom.git scripts
16 | - ./scripts/internal-setup.sh
17 | - export PACKAGE_NAMES="$(./scripts/internal-get-packages.py distribution.yml ${REPOSITORY})"
18 | install:
19 | - mv "${TRAVIS_BUILD_DIR}" src
20 | - ./scripts/internal-distro.py --workspace=src distribution.yml --repository "${REPOSITORY}"
21 | script:
22 | - ./scripts/internal-build.sh ${PACKAGE_NAMES}
23 | - ./scripts/internal-test.sh ${PACKAGE_NAMES}
24 | after_script:
25 | - ./scripts/view-all-results.sh test_results
26 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | or_urdf
4 | 0.4.0
5 |
6 | OpenRAVE plugin for loading URDF and SRDF files.
7 |
8 | https://github.com/personalrobotics/or_urdf
9 | Michael Koval
10 | Michael Koval
11 | Pras Velagapudi
12 | BSD
13 | catkin
14 | boost
15 | tinyxml2
16 | openrave
17 | roslib
18 | srdfdom
19 | urdf
20 | boost
21 | tinyxml2
22 | openrave
23 | roslib
24 | srdfdom
25 | urdf
26 | python-nose
27 | python-numpy
28 | python
29 |
30 |
--------------------------------------------------------------------------------
/empty.iv:
--------------------------------------------------------------------------------
1 | #Inventor V2.0 ascii
2 |
3 | Separator {
4 | Info {
5 | string "empty.iv generated by IVCON."
6 | string "Original data in file NO_IN_NAME."
7 | }
8 | Separator {
9 | LightModel {
10 | model PHONG
11 | }
12 | MatrixTransform { matrix
13 | 0.000000 0.000000 0.000000 0.000000
14 | 0.000000 0.000000 0.000000 0.000000
15 | 0.000000 0.000000 0.000000 0.000000
16 | 0.000000 0.000000 0.000000 0.000000
17 | }
18 | Material {
19 | ambientColor 0.2 0.2 0.2
20 | diffuseColor 0.8 0.8 0.8
21 | emissiveColor 0.0 0.0 0.0
22 | specularColor 0.0 0.0 0.0
23 | shininess 0.2
24 | transparency 0.0
25 | }
26 | MaterialBinding {
27 | value PER_VERTEX_INDEXED
28 | }
29 | NormalBinding {
30 | value PER_VERTEX_INDEXED
31 | }
32 | TextureCoordinateBinding {
33 | value PER_VERTEX_INDEXED
34 | }
35 | ShapeHints {
36 | vertexOrdering COUNTERCLOCKWISE
37 | shapeType UNKNOWN_SHAPE_TYPE
38 | faceType CONVEX
39 | creaseAngle 6.28319
40 | }
41 | Coordinate3 {
42 | point [
43 | ]
44 | }
45 | TextureCoordinate2 {
46 | point [
47 | ]
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/catkin_finder.h:
--------------------------------------------------------------------------------
1 | /** \file catkin_finder.h
2 | * \brief Utility to resolve package to file URIs in catkin workspaces
3 | * \author Michael Koval
4 | * \author Chris Dellin
5 | * \date 2015
6 | */
7 |
8 | /* (C) Copyright 2015 Carnegie Mellon University */
9 |
10 | #ifndef OR_URDF_CATKIN_FINDER_H_
11 | #define OR_URDF_CATKIN_FINDER_H_
12 |
13 | #include
14 | #include
15 | #include
16 |
17 | namespace or_urdf {
18 |
19 | /// Utility to resolve "package://" paths against catkin workspaces.
20 | /// Should replicate the behaviour of the tool
21 | /// catkin_find package relative/path
22 | ///
23 | /// Resolution code borrowed heavily from
24 | /// aikido::util::CatkinResourceRetriever,
25 | /// simplified to remove DART dependencies
26 | /// and to do local path resolution only
27 | class CatkinFinder
28 | {
29 | public:
30 | CatkinFinder();
31 | ~CatkinFinder();
32 |
33 | // input: package:// URI
34 | // output: native local filesystem path if found ("" otherwise)
35 | std::string find(const std::string & uri) const;
36 |
37 | private:
38 | struct Workspace {
39 | std::string mPath;
40 | std::unordered_map mSourceMap;
41 | };
42 | std::vector mWorkspaces;
43 | static std::vector getWorkspaces();
44 | };
45 |
46 | } // namespace or_urdf
47 |
48 | #endif // ifndef OR_URDF_CATKIN_FINDER_H_
49 |
--------------------------------------------------------------------------------
/scripts/load.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | PACKAGE = 'or_urdf'
3 | import argparse, openravepy, os, sys
4 |
5 | parser = argparse.ArgumentParser(description='loads a URDF model into OpenRAVE')
6 | parser.add_argument('urdf_path', type=str)
7 | parser.add_argument('srdf_path', type=str, default='', nargs='?')
8 | parser.add_argument('-i', '--interactive', action='store_true', help='display the model in an OpenRAVE viewer')
9 | parser.add_argument('-c', '--config', type=str, action='store', help='config file for urdf generation')
10 | args = parser.parse_args()
11 |
12 | # Load the plugin.
13 | env = openravepy.Environment()
14 | plugin = openravepy.RaveCreateModule(env, 'urdf')
15 | if plugin is None:
16 | parser.error('Unable to load urdf plugin.')
17 | sys.exit(1)
18 |
19 | # Generate the KinBody XML.
20 | try:
21 | kinbody_xml = plugin.SendCommand('load {0:s} {1:s}'.format(args.urdf_path, args.srdf_path))
22 | if kinbody_xml is None:
23 | raise openravepy.openrave_exception('An unknown error has occurred.')
24 | except openravepy.openrave_exception, e:
25 | parser.error('Failed generating KinBody: {0:s}'.format(e.message))
26 | sys.exit(1)
27 |
28 | #env.SetViewer('qtcoin')
29 |
30 | body = env.GetBodies()[0]
31 | handles = list()
32 |
33 | """
34 | for link in body.GetLinks():
35 | pose = link.GetTransform()
36 | handle = openravepy.misc.DrawAxes(env, pose, 0.2, 2)
37 | handles.append(handle)
38 | """
39 |
40 | if args.interactive:
41 | import IPython; IPython.embed()
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Carnegie Mellon University
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | - Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | - Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 | - Neither the name of Carnegie Mellon University nor the names of its
13 | contributors may be used to endorse or promote products derived from this
14 | software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 | POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(or_urdf)
3 |
4 | list(INSERT CMAKE_MODULE_PATH 0 "${PROJECT_SOURCE_DIR}/cmake")
5 |
6 | find_package(catkin REQUIRED COMPONENTS
7 | roslib
8 | srdfdom
9 | urdf
10 | )
11 |
12 | find_package(Boost REQUIRED COMPONENTS
13 | filesystem
14 | system
15 | )
16 | find_package(OpenRAVE REQUIRED)
17 | find_package(TinyXML2 REQUIRED)
18 |
19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
20 |
21 | catkin_package()
22 | catkin_add_env_hooks("20.${PROJECT_NAME}"
23 | SHELLS sh
24 | DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/catkin-env-hooks"
25 | )
26 |
27 | include_directories(
28 | ${Boost_INCLUDE_DIRS}
29 | ${catkin_INCLUDE_DIRS}
30 | ${TinyXML2_INCLUDE_DIRS}
31 | ${OpenRAVE_INCLUDE_DIRS}
32 | )
33 | link_directories(
34 | ${Boost_LIBRARY_DIRS}
35 | ${catkin_LIBRARY_DIRS}
36 | ${OpenRAVE_LIBRARY_DIRS}
37 | )
38 |
39 | add_library("${PROJECT_NAME}_plugin" MODULE
40 | src/urdf_loader.cpp
41 | src/catkin_finder.cpp
42 | )
43 | set_target_properties(${PROJECT_NAME}_plugin PROPERTIES
44 | PREFIX ""
45 | COMPILE_FLAGS "${OpenRAVE_CXX_FLAGS}"
46 | LINK_FLAGS "${OpenRAVE_LINK_FLAGS}"
47 | LIBRARY_OUTPUT_DIRECTORY
48 | "${CATKIN_DEVEL_PREFIX}/lib/openrave-${OpenRAVE_LIBRARY_SUFFIX}")
49 | target_link_libraries("${PROJECT_NAME}_plugin"
50 | ${catkin_LIBRARIES}
51 | ${TinyXML2_LIBRARIES}
52 | ${OpenRAVE_LIBRARIES}
53 | )
54 |
55 | if(CATKIN_ENABLE_TESTING)
56 | catkin_add_nosetests(tests)
57 | endif(CATKIN_ENABLE_TESTING)
58 |
59 | install(TARGETS "${PROJECT_NAME}_plugin"
60 | LIBRARY DESTINATION "lib/openrave-${OpenRAVE_LIBRARY_SUFFIX}"
61 | )
62 |
63 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 | Changelog for package or_urdf
3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 |
5 | 0.4.0 (2015-10-12)
6 | ------------------
7 | * Added support for multiple collision objects within one link (`#13 `_)
8 | * Added missing dependency on OpenRAVE
9 | * Set the source URI of loaded bodies (`#14 `_)
10 | * Contributors: Chris Dellin, Dawid Seredyński, Michael Koval
11 |
12 | 0.3.0 (2015-04-15)
13 | ------------------
14 | * Update README.md
15 | * Added some more unsupported features to the README
16 | * Fixed a typo (thanks @cdellin!)
17 | * Merge pull request `#12 `_ from personalrobotics/bugfix/8
18 | Support package:// URDF and SRDF URIs
19 | * Support package:// URDF and SRDF URIs (`#8 `_)
20 | * Merge pull request `#11 `_ from personalrobotics/bugfix/ContinuousJointLimits
21 | Fixed support for continuous joints.
22 | * Added a fix for limits on continuous joints.
23 | * Don't convert revolute joints to continuous joints.
24 | This was previously happening if a continuous joint defined any limits,
25 | including velocity and effort limits.
26 | * Force continuous joints to have +/-INF limits.
27 | * Contributors: Michael Koval
28 |
29 | 0.2.0 (2015-03-30)
30 | ------------------
31 | * Replaced RAVELOG_INFO with RAVELOG_DEBUG.
32 | * Manually apply the URDF collision scale to meshes.
33 | * Added support for scaling visual meshes.
34 | * Added LICENSE.
35 | * Added a README.
36 | * Stripped out YAML dependencies (unnecessary if an SRDF is being used).
37 | * Contributors: Michael Koval, Prasanna Velagapudi
38 |
--------------------------------------------------------------------------------
/src/boostfs_helpers.h:
--------------------------------------------------------------------------------
1 | /**
2 | * \file boostfs_helpers.h
3 | * \brief Helper functions for BoostFS.
4 | * \author Pras Velagapudi
5 | * \date 2013
6 | */
7 | /* (C) Copyright 2013 Carnegie Mellon University */
8 | #include
9 | #include
10 | #include "boost/version.hpp"
11 |
12 | namespace boost
13 | {
14 | #if BOOST_VERSION > 104900
15 | namespace filesystem
16 | #else
17 | namespace filesystem3
18 | #endif
19 | {
20 | template < >
21 | path& path::append< typename path::iterator >( typename path::iterator begin, typename path::iterator end, const codecvt_type& cvt)
22 | {
23 | for( ; begin != end ; ++begin )
24 | *this /= *begin;
25 | return *this;
26 | }
27 |
28 | // Return path when appended to a_From will resolve to same as a_To
29 | boost::filesystem::path make_relative( boost::filesystem::path a_From, boost::filesystem::path a_To )
30 | {
31 | a_From = boost::filesystem::absolute( a_From ); a_To = boost::filesystem::absolute( a_To );
32 | boost::filesystem::path ret;
33 | boost::filesystem::path::const_iterator itrFrom( a_From.begin() ), itrTo( a_To.begin() );
34 |
35 | // Find common base
36 | for( boost::filesystem::path::const_iterator toEnd( a_To.end() ), fromEnd( a_From.end() ) ;
37 | itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo );
38 |
39 | // Navigate backwards in directory to reach previously found base
40 | for( boost::filesystem::path::const_iterator fromEnd( a_From.end() ); itrFrom != fromEnd; ++itrFrom ) {
41 | if( (*itrFrom) != "." )
42 | ret /= "..";
43 | }
44 |
45 | // Now navigate down the directory branch
46 | ret.append( itrTo, a_To.end() );
47 | return ret;
48 | }
49 | }
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/tests/test_urdf_loader.py:
--------------------------------------------------------------------------------
1 | #!//bin/env python
2 | import numpy
3 | import unittest
4 | import openravepy
5 | import subprocess
6 | import os
7 | import sys
8 |
9 | # Find the bh_280 URDF file
10 | from catkin.find_in_workspaces import find_in_workspaces
11 |
12 | share_directories = find_in_workspaces(search_dirs=['share'],
13 | project='or_urdf',
14 | path='test_data/'
15 | )
16 |
17 | if not share_directories:
18 | raise ValueError('Unable to find BH280 model.')
19 |
20 | found_models = False
21 | for share_directory in share_directories:
22 | urdf_path = os.path.join(share_directories[0], 'robots', 'bh280.urdf')
23 | if os.path.exists(urdf_path):
24 | found_models=True
25 | break
26 |
27 | if not found_models:
28 | raise ValueError('Unable to find BH280 URDF file.')
29 |
30 |
31 | class TestORUrdf(unittest.TestCase):
32 | def setUp(self):
33 | self.env = openravepy.Environment()
34 | urdf_module = openravepy.RaveCreateModule(self.env, 'urdf')
35 |
36 | with self.env:
37 | arg = 'load {:s}'.format(urdf_path)
38 | bh_name = urdf_module.SendCommand(arg)
39 | self.bh_body = self.env.GetKinBody(bh_name)
40 |
41 | def tearDown(self):
42 | self.env.Destroy()
43 |
44 | def TestLinkLocalInertia(self):
45 | #Test inertia for a random link
46 | hand_base_link = self.bh_body.GetLink('/hand_base')
47 | numpy.testing.assert_array_almost_equal(
48 | hand_base_link.GetLocalInertia(),
49 | numpy.diag([0.0006986,0.00050354,0.00062253]))
50 |
51 | def TestLinkMass(self):
52 | #Test mass for a random link
53 | finger0_0 = self.bh_body.GetLink('/finger0_0')
54 | numpy.testing.assert_almost_equal(finger0_0.GetMass(),0.14109)
55 |
56 | def TestLinkLocalCOM(self):
57 | finger0_0 = self.bh_body.GetLink('/finger0_0')
58 | numpy.testing.assert_array_almost_equal(
59 | finger0_0.GetLocalCOM(),
60 | numpy.array([0.030616,-7.3219e-05,-0.011201]))
61 |
62 | def TestJointLimits(self):
63 | #Test extent and velocity limits of a join
64 | joint = self.bh_body.GetJoint('/j00')
65 | numpy.testing.assert_array_almost_equal(
66 | joint.GetLimits()[0],numpy.array([0]))
67 | numpy.testing.assert_array_almost_equal(
68 | joint.GetLimits()[1],numpy.array([numpy.pi]))
69 | numpy.testing.assert_array_almost_equal(
70 | joint.GetMaxVel(),2.0)
71 |
72 | def TestParentChild(self):
73 | #Test parent and child links of joint
74 | joint = self.bh_body.GetJoint('/j01')
75 | numpy.testing.assert_equal(
76 | joint.GetHierarchyChildLink().GetName(),'/finger0_1')
77 | numpy.testing.assert_equal(
78 | joint.GetHierarchyParentLink().GetName(),'/finger0_0')
79 |
80 | def TestMimicEquation(self):
81 | #Test mimic equation for a join
82 | joint = self.bh_body.GetJoint('/j22')
83 | numpy.testing.assert_equal(
84 | joint.GetMimicEquation(),'/j21*0.321429+0.000000')
85 |
--------------------------------------------------------------------------------
/src/urdf_loader.h:
--------------------------------------------------------------------------------
1 | /** \file urdf_loader.h
2 | * \brief Interface to a URDF loading plugin for OpenRAVE
3 | * \author Pras Velagapudi
4 | * \date 2013
5 | */
6 |
7 | /* (C) Copyright 2013 Carnegie Mellon University */
8 | #ifndef URDF_LOADER_H
9 | #define URDF_LOADER_H
10 |
11 | #include
12 | #include
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include
21 |
22 | #include "catkin_finder.h"
23 |
24 | namespace or_urdf
25 | {
26 | class URDFLoader : public OpenRAVE::ModuleBase
27 | {
28 | public:
29 | /** Opens a URDF file and returns a robot in OpenRAVE */
30 | bool loadURI(std::ostream& sout, std::istream& sin);
31 | bool loadJsonString(std::ostream& sout, std::istream& sin);
32 | bool deprecatedLoad(std::ostream&, std::istream& sin);
33 |
34 | /** Constructs plugin and registers functions */
35 | URDFLoader(OpenRAVE::EnvironmentBasePtr env) : OpenRAVE::ModuleBase(env)
36 | {
37 | __description = "URDFLoader: Loader that imports URDF files.";
38 | _env = env;
39 |
40 | RegisterCommand("LoadURI", boost::bind(&URDFLoader::loadURI, this, _1, _2),
41 | "load URDF and SRDF from file/URI");
42 | RegisterCommand("LoadString", boost::bind(&URDFLoader::loadJsonString, this, _1, _2),
43 | "load URDF and SRDF from json string wrapping XML");
44 | RegisterCommand("load", boost::bind(&URDFLoader::deprecatedLoad, this, _1, _2),
45 | "Deprecated method name to load URDF and SRDF from file/URI");
46 | }
47 |
48 | void Destroy() { RAVELOG_INFO("URDF loader unloaded from environment\n"); }
49 |
50 | virtual ~URDFLoader() {}
51 |
52 | void ParseURDF(urdf::Model &model, std::vector &link_infos,
53 | std::vector &joint_infos);
54 |
55 | void ParseSRDF(urdf::Model const &urdf,
56 | srdf::Model const &srdf,
57 | std::vector &link_infos,
58 | std::vector &joint_infos,
59 | std::vector &manip_infos);
60 |
61 | void ProcessGeometryGroupTagsFromURDF(
62 | TiXmlDocument &xml_doc,
63 | std::vector &link_infos);
64 |
65 | /* This is called on env.LoadProblem(m, 'command') */
66 | int main(const std::string& cmd) { RAVELOG_INFO("URDF loader initialized with command: %s\n", cmd.c_str()); return 0; }
67 |
68 | private:
69 | /** Reference to OpenRAVE environment, filled in on construction */
70 | OpenRAVE::EnvironmentBasePtr _env;
71 | or_urdf::CatkinFinder _catkin_finder;
72 |
73 | std::string loadModel(urdf::Model &urdf_model, TiXmlDocument &xml_doc,
74 | std::shared_ptr srdf_model = nullptr,
75 | std::string uri = std::string());
76 | std::string resolveURI(const std::string &path) const;
77 | std::string resolveURIorPath(const std::string &path) const;
78 | };
79 |
80 | } /* namespace or_urdf */
81 |
82 | #endif // URDF_LOADER_H
83 |
--------------------------------------------------------------------------------
/test_data/robots/bh280.urdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # or_urdf
2 |
3 | [](https://travis-ci.org/personalrobotics/or_urdf)
4 |
5 | or_urdf is an OpenRAVE plugin for loading a URDF and, optionally, SRDF file as
6 | an OpenRAVE `KinBody` or `Robot`. This package provides the OpenRAVE `URDF`
7 | module through a plugin. This plugin can be instantiated using the following
8 | Python code:
9 |
10 | ```python
11 | module = RaveCreateModule(env, 'urdf')
12 | ```
13 |
14 | Once the module has been instantiated, you can use the module to load a
15 | `KinBody` by calling the custom `load` command with a path to a URDF file.
16 | Similarly, you can create an OpenRAVE `Robot` calling the `load` command with
17 | paths to both `URDF` and `SRDF` files.
18 |
19 |
20 | ## Why URDF and SRDF?
21 |
22 | [URDF](http://wiki.ros.org/urdf) and [SRDF](http://wiki.ros.org/srdf) are the
23 | standard XML file formats used to describe robots in [ROS](http://www.ros.org).
24 | These files are [available for many robots](http://wiki.ros.org/urdf/Examples)
25 | and are a key requirement of using
26 | [MoveIt!](http://moveit.ros.org) and/or [RViz](http://wiki.ros.org/rviz). This
27 | module uses the standard [urdfdom](https://github.com/ros/urdfdom) and
28 | [srdfdom](https://github.com/ros-planning/srdfdom) parsers and, thus, should be
29 | compatible with any robot specification that obeys the URDF and SRDF standards.
30 |
31 |
32 | ## Why programmatic construction?
33 |
34 | or_urdf takes the unique approach of *progammatically constructing OpenRAVE
35 | objects* from the URDF and SRDF files. This is in contrast to other conversion
36 | functions, like the [`urdf_to_collada`
37 | script](http://wiki.ros.org/collada_urdf), which converts the input URDF into
38 | an intermediate file format. Programmatically constructing the OpenRAVE objects
39 | has several key advantages over this alternative approach:
40 |
41 | 1. Relative `file://` and `package://` URI's are resolved at run-time.
42 | 2. There is no need to re-generate any auto-generated files when the URDF or
43 | SRDF files change.
44 | 3. There is no loss in precision due to the serialization and deserialization
45 | of floating point numbers.
46 | 4. The URDF and SRDF specifications can be loaded directly from the
47 | `robot_description` and `semantic_robot_description` ROS parameters.
48 |
49 |
50 | ## Loading a KinBody from URDF
51 |
52 | The following code will load the URDF model `/path/to/my/model.urdf` as an
53 | OpenRAVE `KinBody`:
54 |
55 | ```python
56 | with env:
57 | name = module.SendCommand('load /path/to/my/model.urdf')
58 | body = env.GetKinBody(name)
59 | ```
60 |
61 | The following OpenRAVE properties have no equivalent in URDF and, thus, must be
62 | manually configured on a `KinBody` created by or_urdf:
63 |
64 | - Acceleration limits (`robot.SetDOFAccelerationLimits`)
65 | - DOF resolutions (`robot.SetDOFResolutions`)
66 | - Mass and inertia properties are **currently untested**
67 |
68 | The `load` command programmatically creates a `KinBody` from the URDF by
69 | building a list of `LinkInfo` and `JointInfo` structures. This `KinBody` is
70 | then added to the environment with the `anonymous` flag set. The command
71 | returns the name of the object that was added to the environment. This is
72 | necessary to avoid introducing name conflicts or ambiguity when loading
73 | multiple instances of a URDF file into the same environment.
74 |
75 | You can easily change the name of the resultant `KinBody` if the name generated
76 | by this automatic procedure are undesirable. To do so, you must: (1) remove the
77 | `KinBody` from the environment, (2) change the `KinBody`'s name, and (3) add
78 | the `KinBody` back to the environment. For example:
79 |
80 | ```python
81 | with env:
82 | name = module.SendCommand('load /path/to/my/model.urdf')
83 | body = env.GetKinBody(name)
84 |
85 | env.Remove(body)
86 | body.SetName('my_custom_name')
87 | env.Add(body)
88 | ```
89 |
90 |
91 | ## Loading a Robot from URDF and SRDF
92 |
93 | or_urdf also supports loading an OpenRAVE `Robot` from the combination of a
94 | URDF and SRDF file. In this case, the URDF file fills the role of an OpenRAVE
95 | `.kinbody.xml` file and the SRDF file fills the role of an OpenRAVE
96 | `.robot.xml` file. Unfortunately, there is not a direct mapping between the
97 | SRDF file format and the features supported by OpenRAVE. or_urdf performs the
98 | conversion as follows:
99 |
100 | - ``: flags the pair of links ad adjacent
101 | - ``: is used to specify one or more `` tags, which:
102 | - ``: creates a sphere geometry in the `spheres` geometry group
103 | - ``: defines a manipulator
104 | - `name`: sets the name of the OpenRAVE manipulator
105 | - `parent_group`: contains the manipulator DOFs (this is **required**)
106 | - `group`: contains the gripper DOFs (this is **required**, but the group be empty)
107 | - ``: not used
108 | - ``: not used
109 | - ``: not used
110 | - ``: not used, except if referenced in ``
111 |
112 | The following OpenRAVE properties have no equivalent in SRDF and, thus, must be
113 | manually configured on a `Robot` create by `or_urdf`:
114 |
115 | - Robot DOF weights (`robot.SetDOFWeights`)
116 | - Manipulator IK solver (`manipulaor.SetIkSolver`)
117 | - Manipulator closing/chucking direction (`manipulaor.SetClosingDirection` or
118 | `manipulator.SetChuckingDirection`, depending upon the version of OpenRAVE)
119 | - Manipulator tool transform and direction (`manipulator.SetLocalToolTransform`
120 | and `manipulator.SetLocalToolDirection`)
121 | - All methods on `KinBody` listed above
122 |
123 | Just as with creating a `KinBody`, or_urdf programmatically creates the `Robot`
124 | by constructing `LinkInfo`, `JointInfo`, and `ManipulatorInfo` structs. The
125 | following code creates an OpenRAVE `Robot` from the paired
126 | `/path/to/my/model.urdf` and `/path/to/my/model.srdf` files:
127 |
128 | ```python
129 | with env:
130 | name = module.SendCommand('load /path/to/my/model.urdf /path/to/my/model.srdf')
131 | body = env.GetRobot(name)
132 | ```
133 |
134 | See above for more information about how to rename the robot created by the
135 | module.
136 |
137 |
138 | ## License
139 |
140 | or_urdf is licensed under a BSD license. See `LICENSE` for more information.
141 |
142 |
143 | # Contributors
144 |
145 | or_urdf was developed by the
146 | [Personal Robotics Lab](https://personalrobotics.ri.cmu.edu) in the
147 | [Robotics Institute](https://www.ri.cmu.edu) at
148 | [Carnegie Mellon University](http://www.cmu.edu). This library is developed and
149 | maintained by
150 | [Michael Koval](https://github.com/mkoval) and
151 | [Pras Velagapudi](https://github.com/psigen).
152 |
153 |
--------------------------------------------------------------------------------
/src/catkin_finder.cpp:
--------------------------------------------------------------------------------
1 | /** \file catkin_finder.h
2 | * \brief Utility to resolve package to file URIs in catkin workspaces
3 | * \author Michael Koval
4 | * \author Chris Dellin
5 | * \date 2015
6 | */
7 |
8 | /* (C) Copyright 2015 Carnegie Mellon University */
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include "catkin_finder.h"
17 |
18 | static const std::string CATKIN_MARKER(".catkin");
19 |
20 | namespace {
21 |
22 | std::string getPackageNameFromXML(const std::string& _path)
23 | {
24 | using tinyxml2::XMLHandle;
25 | using tinyxml2::XMLElement;
26 |
27 | tinyxml2::XMLDocument document;
28 | if (document.LoadFile(_path.c_str()))
29 | {
30 | RAVELOG_WARN("Failed loading package.xml file '%s': %s\n",
31 | _path.c_str(), document.GetErrorStr1());
32 | return "";
33 | }
34 |
35 | XMLHandle root_handle(document.RootElement());
36 | XMLHandle name_handle = root_handle.FirstChildElement("name");
37 | XMLElement* name_element = name_handle.ToElement();
38 |
39 | if (!name_element)
40 | {
41 | RAVELOG_WARN("Failed loading package.xml file '%s': "
42 | "File does not contain a element.\n", _path.c_str());
43 | return "";
44 | }
45 |
46 | if (!name_element->GetText())
47 | {
48 | RAVELOG_WARN("Failed loading package.xml file '%s': "
49 | " element is empty.\n", _path.c_str());
50 | return "";
51 | }
52 |
53 | std::string package_name = name_element->GetText();
54 | boost::algorithm::trim(package_name);
55 |
56 | if (package_name.empty())
57 | {
58 | RAVELOG_WARN("Failed loading package.xml file '%s': "
59 | " element is empty.\n", _path.c_str());
60 | return "";
61 | }
62 |
63 | return package_name;
64 | }
65 |
66 | void searchForPackages(const boost::filesystem::path& _packagePath,
67 | std::unordered_map& _packageMap)
68 | {
69 | using boost::filesystem::directory_iterator;
70 | using boost::filesystem::path;
71 | using boost::filesystem::file_status;
72 | using boost::filesystem::exists;
73 |
74 | // Ignore this directory if it contains a CATKIN_IGNORE file.
75 | const path catkin_ignore_path = _packagePath / "CATKIN_IGNORE";
76 | if (exists(catkin_ignore_path))
77 | return;
78 |
79 | // Try loading the package.xml file.
80 | const path package_xml_path = _packagePath / "package.xml";
81 | if (exists(package_xml_path))
82 | {
83 | const std::string package_name = getPackageNameFromXML(
84 | package_xml_path.string());
85 | if (!package_name.empty())
86 | {
87 | const auto result = _packageMap.insert(
88 | std::make_pair(package_name, _packagePath.string()));
89 | if (!result.second)
90 | {
91 | RAVELOG_WARN("Found two package.xml files for package '%s': '%s' and '%s'.\n",
92 | package_name.c_str(),
93 | result.first->second.c_str(),
94 | _packagePath.c_str());
95 | }
96 | return; // Don't search for packages inside packages.
97 | }
98 | }
99 |
100 | // Recurse on subdirectories.
101 | directory_iterator it(_packagePath);
102 | directory_iterator end;
103 |
104 | while (it != end)
105 | {
106 | boost::system::error_code status_error;
107 | const file_status status = it->status(status_error);
108 | if (status_error)
109 | {
110 | RAVELOG_WARN("Failed recursing into directory '%s'.\n", it->path().c_str());
111 | continue;
112 | }
113 |
114 | if (status.type() == boost::filesystem::directory_file)
115 | searchForPackages(it->path().string(), _packageMap);
116 |
117 | ++it;
118 | }
119 | }
120 |
121 | } // anonymous namespace
122 |
123 | or_urdf::CatkinFinder::CatkinFinder():
124 | mWorkspaces(getWorkspaces())
125 | {
126 | }
127 |
128 | std::vector or_urdf::CatkinFinder::getWorkspaces()
129 | {
130 | std::vector workspaces;
131 |
132 | // initialize workspaces
133 | // (from CatkinResourceRetriever::getWorkspaces())
134 | using boost::filesystem::path;
135 |
136 | const char *cmake_prefix_path = std::getenv("CMAKE_PREFIX_PATH");
137 | if (!cmake_prefix_path)
138 | {
139 | RAVELOG_WARN("The CMAKE_PREFIX_PATH"
140 | " environmental variable is not defined. Did you source"
141 | " 'setup.bash' in this shell?\n");
142 | return workspaces;
143 | }
144 |
145 | // Split CMAKE_PREFIX_PATH by the ':' path separator delimiter.
146 | std::vector workspace_candidates;
147 | boost::split(workspace_candidates, cmake_prefix_path, boost::is_any_of(":"));
148 |
149 | for (const std::string& workspace_path : workspace_candidates)
150 | {
151 | // Skip empty or non-existant workspace directories
152 | if (workspace_path.empty())
153 | continue;
154 | boost::filesystem::path workspace_bpath(workspace_path);
155 | if (!boost::filesystem::is_directory(workspace_bpath))
156 | continue;
157 |
158 | // Construct workspace
159 | or_urdf::CatkinFinder::Workspace workspace;
160 | workspace.mPath = workspace_path;
161 |
162 | // Filter out directories that are missing the Catkin marker file. If the
163 | // marker file exists, then we also read its contents to build a list of all
164 | // source directories.
165 | boost::filesystem::path marker_bpath = workspace_bpath / CATKIN_MARKER;
166 | if (boost::filesystem::exists(marker_bpath))
167 | {
168 | std::ifstream ifs(marker_bpath.c_str());
169 | std::string contents( (std::istreambuf_iterator(ifs) ),
170 | (std::istreambuf_iterator() ) );
171 |
172 | // Split the string into a list of paths by the ';' delimiter. I'm not
173 | // sure why this doesn't use the standard path separator delimitor ':'.
174 | std::vector source_paths;
175 | if (!contents.empty())
176 | boost::split(source_paths, contents, boost::is_any_of(";"));
177 |
178 | for (const std::string& source_path : source_paths)
179 | searchForPackages(source_path, workspace.mSourceMap);
180 | }
181 | else
182 | {
183 | RAVELOG_WARN("Failed reading package"
184 | " source directories from the marker file '%s"
185 | "'. Resources in the source"
186 | " space of this workspace will not resolve.\n",
187 | marker_bpath.c_str());
188 | }
189 |
190 | workspaces.push_back(workspace);
191 | }
192 |
193 | return workspaces;
194 | }
195 |
196 |
197 | or_urdf::CatkinFinder::~CatkinFinder()
198 | {
199 | }
200 |
201 | std::string or_urdf::CatkinFinder::find(const std::string & uri) const
202 | {
203 | using boost::filesystem::path;
204 |
205 | // ensure uri starts with package://
206 | if (uri.substr(0,10) != "package://")
207 | {
208 | RAVELOG_ERROR("CatkinFinder passed a non package:// path!\n");
209 | return "";
210 | }
211 | std::string nonprefix = uri.substr(10);
212 |
213 | // split into package name and relative path
214 | std::string packageName;
215 | std::string relativePath;
216 | size_t islash = nonprefix.find('/');
217 | if (islash == nonprefix.npos)
218 | {
219 | packageName = uri;
220 | }
221 | else
222 | {
223 | packageName = nonprefix.substr(0,islash);
224 | relativePath = nonprefix.substr(islash+1);
225 | }
226 |
227 | // Sequentially check each chained workspace.
228 | for (const Workspace& workspace : mWorkspaces)
229 | {
230 | // First check the 'devel' or 'install' space.
231 | const path develPath = path(workspace.mPath) / "share" / packageName / relativePath;
232 | if (boost::filesystem::exists(develPath))
233 | return develPath.native();
234 |
235 | // Next, check the source space.
236 | const auto it = workspace.mSourceMap.find(packageName);
237 | if (it != std::end(workspace.mSourceMap))
238 | {
239 | const path sourcePath = path(it->second) / relativePath;
240 | if (boost::filesystem::exists(sourcePath))
241 | return sourcePath.native();
242 | }
243 | }
244 |
245 | RAVELOG_WARN("Could not find file '%s' under catkin package '%s'!\n",
246 | packageName.c_str(), relativePath.c_str());
247 | return "";
248 | }
249 |
--------------------------------------------------------------------------------
/src/picojson.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2009-2010 Cybozu Labs, Inc.
3 | * Copyright 2011-2014 Kazuho Oku
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions are met:
8 | *
9 | * 1. Redistributions of source code must retain the above copyright notice,
10 | * this list of conditions and the following disclaimer.
11 | *
12 | * 2. Redistributions in binary form must reproduce the above copyright notice,
13 | * this list of conditions and the following disclaimer in the documentation
14 | * and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 | * POSSIBILITY OF SUCH DAMAGE.
27 | */
28 | #ifndef picojson_h
29 | #define picojson_h
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include