Skip to content

Commit

Permalink
Merge pull request #130 from GiulioRomualdi/icub-models-library
Browse files Browse the repository at this point in the history
Implement a machinery to easily access model locations on C++ and Python
  • Loading branch information
traversaro authored Feb 16, 2022
2 parents cf9abf0 + 86e30f0 commit f286488
Show file tree
Hide file tree
Showing 12 changed files with 1,206 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ The format of this document is based on [Keep a Changelog](https://keepachangelo

## [Unreleased]

### Added

* Implement a machinery to easily access model locations on C++ and Python (https://github.com/robotology/icub-models/pull/130)

### Changed

* All Gazebo models installed by icub-models are in SDF format version 1.7. This means that Gazebo >= 11 is required to load them.
Expand Down
59 changes: 57 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,43 @@

cmake_minimum_required(VERSION 3.5)

project(icub-models)
project(icub-models
VERSION 1.22.1)

include(GNUInstallDirs)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}")

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
option(ICUB_MODELS_COMPILE_PYTHON_BINDINGS "Compile the python bindings for iCubModels" OFF)


if(MSVC)
set(CMAKE_DEBUG_POSTFIX "d")
endif()

set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS OFF)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
option(BUILD_SHARED_LIBS "Build libraries as shared as opposed to static" ON)

# Enable RPATH support for installed binaries and libraries
include(AddInstallRPATHSupport)
add_install_rpath_support(BIN_DIRS "${CMAKE_INSTALL_FULL_BINDIR}"
LIB_DIRS "${CMAKE_INSTALL_FULL_LIBDIR}"
INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}"
USE_LINK_PATH)

# Encourage user to specify a build type (e.g. Release, Debug, etc.), otherwise set it to Release.
if(NOT CMAKE_CONFIGURATION_TYPES)
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Setting build type to 'Release' as none was specified.")
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY VALUE "Release")
endif()
endif()

macro(SUBDIRLIST result curdir)
file(GLOB children RELATIVE ${curdir} ${curdir}/*)
Expand Down Expand Up @@ -52,6 +88,8 @@ set(CONFIGURED_MODELS "")
list(APPEND CONFIGURED_MODELS "left_hand_mk3")
list(APPEND CONFIGURED_MODELS "left_wrist_mk2")

set(INSTALLED_URDF_MODELS "")

SUBDIRLIST(ROBOTS_NAMES ${CMAKE_CURRENT_SOURCE_DIR}/iCub/robots)
foreach(ROBOT_DIRNAME ${ROBOTS_NAMES})
set(ROBOT_NAME ${ROBOT_DIRNAME})
Expand All @@ -65,6 +103,8 @@ foreach(ROBOT_DIRNAME ${ROBOTS_NAMES})
set(ROBOT_FEET_FIXED_MODEL_CONFIG_FILE "${ROBOT_FEET_FIXED_MODEL_FOLDER}/model.config")
set(ROBOT_FEET_FIXED_MODEL_SDF_FILE "${ROBOT_FEET_FIXED_MODEL_FOLDER}/${ROBOT_NAME}_feet_fixed.sdf")

list(APPEND INSTALLED_URDF_MODELS \"${ROBOT_NAME}\")

if(ROBOT_NAME IN_LIST CONFIGURED_MODELS)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/iCub/robots/${ROBOT_NAME}/model.config
${CMAKE_CURRENT_BINARY_DIR}/iCub/robots/${ROBOT_NAME}/model.config
Expand Down Expand Up @@ -123,15 +163,30 @@ endforeach()

SUBDIRLIST(ROBOTS_NAMES ${CMAKE_CURRENT_SOURCE_DIR}/iCub_manual/robots)
foreach(ROBOT_NAME ${ROBOTS_NAMES})

list(APPEND INSTALLED_URDF_MODELS \"${ROBOT_NAME}\")

if(ROBOT_NAME IN_LIST GAZEBO_SUPPORTED_MODELS)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/iCub_manual/robots/${ROBOT_NAME}/model.config
${CMAKE_CURRENT_BINARY_DIR}/iCub/robots/${ROBOT_NAME}/model.config
@ONLY)
endif()
endforeach()

string(REPLACE ";" "," INSTALLED_URDF_MODELS "${INSTALLED_URDF_MODELS}")

# Install the whole iCub directory
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/iCub DESTINATION share)

include(AddUninstallTarget.cmake)
add_subdirectory(cpp)
add_subdirectory(bindings)


include(InstallBasicPackageFiles)
install_basic_package_files(${PROJECT_NAME}
VERSION ${${PROJECT_NAME}_VERSION}
COMPATIBILITY AnyNewerVersion
VARS_PREFIX ${PROJECT_NAME}
NO_CHECK_REQUIRED_COMPONENTS_MACRO)

include(AddUninstallTarget)
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,65 @@ export GAZEBO_MODEL_PATH=${GAZEBO_MODEL_PATH}:<prefix>/share/iCub/robots:<prefix
Note that only the models that are known to work fine with the default physics engine settings of Gazebo (`iCubGazeboV2_5` and `iCubGazeboV2_5_plus`)
are installed. If you want to make available in Gazebo all the models, enable the `ICUB_MODELS_INSTALL_ALL_GAZEBO_MODELS` CMake option.

### Use the models from C++ helper library
In order to use these models in `c++` application you can exploit the `icub-models` library.
`icub-models` provides native `CMake` support which allows the library to be easily used in `CMake` projects.
**icub-models** exports a CMake target called `icub-models::icub-models` which can be imported using the `find_package` CMake command and used by calling `target_link_libraries` as in the following example:
```cmake
cmake_minimum_required(VERSION 3.0)
project(myproject)
find_package(icub-models REQUIRED)
add_executable(example example.cpp)
target_link_libraries(example icub-models::icub-models)
```

The `example.cpp` will contains
```cpp
#include <iCubModels/iCubModels.h>
#include <iostream>

int main()
{
std::cout << "Models have been installed in: " << iCubModels::getModelsPath() << std::endl;

std::cout << "Available robots: " << std::endl;
for (const auto& robot : iCubModels::getRobotNames())
{
std::cout << " - " << robot << ": " << iCubModels::getModelFile(robot) << std::endl;
}

return EXIT_SUCCESS;
}
```

### Use the models from Python helper library
In order to use these models in `python` application you can exploit the `icub-models` module.
`icub-models` provides `python` bindings support thanks to `pybind11` library. To compile the
bindings please make sure you have installed `pybind11` in your system. If you are in Ubuntu Linux,
you can install them with
```
sudo apt install python3-pybind11
```
Then you can enable the bindings compilation with
```
cmake -DCMAKE_INSTALL_PREFIX=<path/where/you/want/to/install> \
-DCMAKE_BUILD_TYPE=Release \
-DICUB_MODELS_COMPILE_PYTHON_BINDINGS:BOOL=ON ..
cmake --build . --config Release --target install
```

Then the following script can be used to locate the models
```python
import icub_models

print(f"Models have been installed in: {icub_models.get_models_path()}")

print(f"Available robots: {icub_models.get_robot_names()}")

for robot_name in icub_models.get_robot_names():
print(f"{robot_name}: {icub_models.get_model_file(robot_name)}")
```

## Change the orientation of the root frame
The iCub robot `root frame` is defined as [`x-backward`][1], meaning that the x-axis points behind the robot. Nevertheless, in the robotics community, sometimes the root frame of a robot is defined as [`x-forward`][2]. As a consequence, to use the iCub models with software developed for the `x-forward` configuration (e.g. [IHMC-ORS][3]), might be necessary to quickly update the root frame orientation.
For this purpose, locate the joint `<joint name="base_fixed_joint" type="fixed">` in the `URDF` model and perform the following substitution in the `origin` section:
Expand Down
73 changes: 73 additions & 0 deletions bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright (C) 2022 Istituto Italiano di Tecnologia (IIT). All rights reserved.
# This software may be modified and distributed under the terms of the
# GNU Lesser General Public License v2.1 or any later version.

if(ICUB_MODELS_COMPILE_PYTHON_BINDINGS)

find_package(pybind11 REQUIRED)
find_package(Python3 COMPONENTS Interpreter REQUIRED)


# define new line accordingly to the operating system
if (WIN32)
set(NEW_LINE "\n\r")
else()
set(NEW_LINE "\n")
endif()

option(ICUB_MODELS_DETECT_ACTIVE_PYTHON_SITEPACKAGES
"Do you want icub-models to detect and use the active site-package directory? (it could be a system dir)"
FALSE)

# Install the resulting Python package for the active interpreter
if(ICUB_MODELS_DETECT_ACTIVE_PYTHON_SITEPACKAGES)
set(PYTHON_INSTDIR ${Python3_SITELIB}/icub_models)
else()
execute_process(COMMAND ${Python3_EXECUTABLE} -c "from distutils import sysconfig; print(sysconfig.get_python_lib(1,0,prefix=''))"
OUTPUT_VARIABLE _PYTHON_INSTDIR)

string(STRIP ${_PYTHON_INSTDIR} _PYTHON_INSTDIR_CLEAN)
set(PYTHON_INSTDIR ${_PYTHON_INSTDIR_CLEAN}/icub_models)
endif()

# Folder of the Python package within the build tree.
# It is used for the Python tests.
set(ICUB_MODELS_PYTHON_PACKAGE "${CMAKE_BINARY_DIR}/icub_models")

# Add the bindings directory
add_subdirectory(python)

# Create the __init__.py file
file(GENERATE
OUTPUT "${ICUB_MODELS_PYTHON_PACKAGE}/__init__.py"
CONTENT "from icub_models.bindings import *")

# Install the __init__.py file
install(FILES "${ICUB_MODELS_PYTHON_PACKAGE}/__init__.py"
DESTINATION ${PYTHON_INSTDIR})

# Install pip metadata files to ensure that icub_models installed via CMake is listed by pip list
# See https://packaging.python.org/specifications/recording-installed-packages/
# and https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata
option(ICUB_MODELS_PYTHON_PIP_METADATA_INSTALL "Use CMake to install Python pip metadata. Set to off if some other tool already installs it." ON)
mark_as_advanced(ICUB_MODELS_PYTHON_PIP_METADATA_INSTALL)
set(ICUB_MODELS_PYTHON_PIP_METADATA_INSTALLER "cmake" CACHE STRING "Specify the string to identify the pip Installer. Default: cmake, change this if you are using another tool.")
mark_as_advanced(ICUB_MODELS_PYTHON_PIP_METADATA_INSTALLER)
if(ICUB_MODELS_PYTHON_PIP_METADATA_INSTALL)
get_filename_component(PYTHON_METADATA_PARENT_DIR ${PYTHON_INSTDIR} DIRECTORY)
if(WIN32)
set(NEW_LINE "\n\r")
else()
set(NEW_LINE "\n")
endif()
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/METADATA "")
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/METADATA "Metadata-Version: 2.1${NEW_LINE}")
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/METADATA "Name: icub-models${NEW_LINE}")
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/METADATA "Version: ${PROJECT_VERSION}${NEW_LINE}")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/INSTALLER "${ICUB_MODELS_PYTHON_PIP_METADATA_INSTALLER}${NEW_LINE}")
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/METADATA" "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER"
DESTINATION ${PYTHON_METADATA_PARENT_DIR}/icub_models-${PROJECT_VERSION}.dist-info)
endif()

endif()
27 changes: 27 additions & 0 deletions bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright (C) 2022 Istituto Italiano di Tecnologia (IIT). All rights reserved.
# This software may be modified and distributed under the terms of the
# GNU Lesser General Public License v2.1 or any later version.

pybind11_add_module(pybind11_icub_models MODULE
${CMAKE_CURRENT_SOURCE_DIR}/icub_models.cpp
)

target_include_directories(pybind11_icub_models PUBLIC "$<BUILD_INTERFACE:${pybind_include_dirs}>")

target_link_libraries(pybind11_icub_models PRIVATE
icub-models::icub-models)

# # The generated Python dynamic module must have the same name as the pybind11
# # module, i.e. `bindings`.
set_target_properties(pybind11_icub_models PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${ICUB_MODELS_PYTHON_PACKAGE}"
OUTPUT_NAME "bindings")


# Output package is:
#
# icub_models
# |-- __init__.py (generated from main bindings CMake file)
# `-- bindings.<cpython_extension>

install(TARGETS pybind11_icub_models DESTINATION ${PYTHON_INSTDIR})
32 changes: 32 additions & 0 deletions bindings/python/icub_models.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @file icub_models.cpp
* @authors Giulio Romualdi
* @copyright 2022 Istituto Italiano di Tecnologia Released under the terms of the Creative Commons Attribution Share Alike 4.0 International
*/

#include <iCubModels/iCubModels.h>

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace iCubModels
{
namespace
{

namespace py = ::pybind11;
PYBIND11_MODULE(bindings, m)
{
m.doc() = "Python bindings for the icub-models.";

m.def("get_models_path", &iCubModels::getModelsPath,
"Get the folder where the models are installed.")
.def("get_robot_names", &iCubModels::getRobotNames,
"Return a set containing the names of the robots installed.")
.def("get_model_file", &iCubModels::getModelFile, py::arg("model_name"),
"Return the path of the model given its name. If the 'model_name' is not in the list "
"of the installed robot an empty path is returned.");
}

} // namespace
} // namespace iCubModels
Loading

0 comments on commit f286488

Please sign in to comment.