Skip to content

Commit

Permalink
Add Python bindings for human state-related data structures (#379)
Browse files Browse the repository at this point in the history
Co-authored-by: Silvio Traversaro <[email protected]>
  • Loading branch information
carloscp3009 and traversaro authored Aug 2, 2024
1 parent 8909f4b commit d78593a
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ find_package(YCM REQUIRED)
# Import cmake utilities
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(AddWarningsConfigurationToTarget)
include(AddHDEPythonModule)

# To build shared libraries in Windows, we set CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to TRUE
# See https://cmake.org/cmake/help/v3.4/variable/CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS.html
Expand All @@ -51,6 +52,9 @@ endif()

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Flag to enable python bindings
option(HDE_COMPILE_PYTHON_BINDINGS "Flag that enables building the bindings" OFF)

# Plugins are force to be Shared/Dynamic Library
set(YARP_FORCE_DYNAMIC_PLUGINS ON)

Expand Down Expand Up @@ -86,6 +90,7 @@ add_subdirectory(modules)
add_subdirectory(servers)
add_subdirectory(clients)
add_subdirectory(publishers)
add_subdirectory(bindings)
add_subdirectory(HumanDynamicsEstimationLibrary)

include(InstallBasicPackageFiles)
Expand Down
69 changes: 69 additions & 0 deletions bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
# SPDX-License-Identifier: BSD-3-Clause

if(HDE_COMPILE_PYTHON_BINDINGS)

find_package(Python3 3.6 COMPONENTS Interpreter Development REQUIRED)

find_package(pybind11 2.4.3 CONFIG REQUIRED)

set(NEW_LINE "\n")

option(HDE_DETECT_ACTIVE_PYTHON_SITEPACKAGES
"Do you want HDE 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(NOT DEFINED HDE_PYTHON_INSTALL_DIR)
if(HDE_DETECT_ACTIVE_PYTHON_SITEPACKAGES)
set(HDE_PYTHON_INSTALL_DIR ${Python3_SITELIB})
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(HDE_PYTHON_INSTALL_DIR ${_PYTHON_INSTDIR_CLEAN})
endif()
endif()
set(PYTHON_INSTDIR ${HDE_PYTHON_INSTALL_DIR}/hde)

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

# Add the bindings directory
add_subdirectory(python)

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

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

# Install pip metadata files to ensure that HDE 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(HDE_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(HDE_PYTHON_PIP_METADATA_INSTALL)
set(HDE_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(HDE_PYTHON_PIP_METADATA_INSTALLER)
if(HDE_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: hde${NEW_LINE}")
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/METADATA "Version: ${hde_VERSION}${NEW_LINE}")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/INSTALLER "${HDE_PYTHON_PIP_METADATA_INSTALLER}${NEW_LINE}")
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/METADATA" "${CMAKE_CURRENT_BINARY_DIR}/INSTALLER"
DESTINATION ${PYTHON_METADATA_PARENT_DIR}/hde-${hde_VERSION}.dist-info)
endif()

endif()
34 changes: 34 additions & 0 deletions bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
# SPDX-License-Identifier: BSD-3-Clause

add_subdirectory(msgs)

get_property(pybind_headers GLOBAL PROPERTY pybind_headers)
get_property(pybind_sources GLOBAL PROPERTY pybind_sources)
get_property(pybind_include_dirs GLOBAL PROPERTY pybind_include_dirs)
get_property(pybind_link_libraries GLOBAL PROPERTY pybind_link_libraries)

pybind11_add_module(pybind11_hde MODULE
hde.cpp
${pybind_sources}
${pybind_headers}
)

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

target_link_libraries(pybind11_hde PRIVATE
${pybind_link_libraries})

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

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

install(TARGETS pybind11_hde DESTINATION ${PYTHON_INSTDIR})
18 changes: 18 additions & 0 deletions bindings/python/hde.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause


#include <pybind11/pybind11.h>

#include <hde/bindings/msgs/Module.h>

// Create the Python module
PYBIND11_MODULE(bindings, m)
{
namespace py = ::pybind11;

m.doc() = "Human dynamics estimation bindings";

py::module msgModule = m.def_submodule("msg");
hde::bindings::msgs::CreateModule(msgModule);
}
11 changes: 11 additions & 0 deletions bindings/python/msgs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
# SPDX-License-Identifier: BSD-3-Clause


set(H_PREFIX include/hde/bindings/msgs)

add_hde_python_module(
NAME MsgsBindings
SOURCES src/HumanState.cpp src/Module.cpp
HEADERS ${H_PREFIX}/HumanState.h ${H_PREFIX}/BufferedPort.h ${H_PREFIX}/Module.h
LINK_LIBRARIES HumanDynamicsEstimation::HumanStateMsg YARP::YARP_os)
44 changes: 44 additions & 0 deletions bindings/python/msgs/include/hde/bindings/msgs/BufferedPort.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause

#ifndef HDE_BINDINGS_MSGS_BUFFERED_PORT_H
#define HDE_BINDINGS_MSGS_BUFFERED_PORT_H

#include <string>

#include <pybind11/detail/common.h>
#include <pybind11/pybind11.h>

#include <yarp/os/BufferedPort.h>

namespace hde {
namespace bindings {
namespace msgs {

template <typename T>
void CreateBufferedPort(pybind11::module& module, const std::string& name)
{
namespace py = ::pybind11;
py::class_<::yarp::os::BufferedPort<T>>(module, name.c_str())
.def(py::init())
.def("open",
py::overload_cast<const std::string&>(&::yarp::os::BufferedPort<T>::open),
py::arg("name"))
.def("close", &::yarp::os::BufferedPort<T>::close)
.def("isClosed", &::yarp::os::BufferedPort<T>::isClosed)
.def("prepare",
&::yarp::os::BufferedPort<T>::prepare,
py::return_value_policy::reference_internal)
.def("write",
&::yarp::os::BufferedPort<T>::write,
py::arg("forceStrict") = false)
.def("read",
&::yarp::os::BufferedPort<T>::read,
py::arg("shouldWait") = true,
py::return_value_policy::reference_internal);
}
} // namespace msgs
} // namespace bindings
} // namespace hde

#endif // HDE_BINDINGS_MSGS_BUFFERED_PORT_H
19 changes: 19 additions & 0 deletions bindings/python/msgs/include/hde/bindings/msgs/HumanState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause

#ifndef HDE_BINDINGS_MSGS_HUMAN_STATE_H
#define HDE_BINDINGS_MSGS_HUMAN_STATE_H

#include <pybind11/pybind11.h>

namespace hde {
namespace bindings {
namespace msgs {

void CreateHumanState(pybind11::module& module);

} // namespace msgs
} // namespace bindings
} // namespace hde

#endif // HDE_BINDINGS_MSGS_HUMAN_STATE_H
19 changes: 19 additions & 0 deletions bindings/python/msgs/include/hde/bindings/msgs/Module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause

#ifndef HDE_BINDINGS_MSGS_MODULE_H
#define HDE_BINDINGS_MSGS_MODULE_H

#include <pybind11/pybind11.h>

namespace hde {
namespace bindings {
namespace msgs {

void CreateModule(pybind11::module& module);

} // namespace msgs
} // namespace bindings
} // namespace hde

#endif // HDE_BINDINGS_MSGS_MODULE_H
38 changes: 38 additions & 0 deletions bindings/python/msgs/src/HumanState.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause

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

#include <string>
#include <hde/msgs/HumanState.h>

#include <hde/bindings/msgs/BufferedPort.h>
#include <hde/bindings/msgs/HumanState.h>

namespace hde {
namespace bindings {
namespace msgs {

void CreateHumanState(pybind11::module& module)
{
namespace py = ::pybind11;
using namespace ::hde::msgs;

py::class_<HumanState>(module, "HumanState")
.def(py::init())
.def_readwrite("jointNames", &HumanState::jointNames)
.def_readwrite("positions", &HumanState::positions)
.def_readwrite("velocities", &HumanState::velocities)
.def_readwrite("baseName", &HumanState::baseName)
.def_readwrite("baseOriginWRTGlobal", &HumanState::baseOriginWRTGlobal)
.def_readwrite("baseOrientationWRTGlobal", &HumanState::baseOrientationWRTGlobal)
.def_readwrite("baseVelocityWRTGlobal", &HumanState::baseVelocityWRTGlobal)
.def_readwrite("CoMPositionWRTGlobal", &HumanState::CoMPositionWRTGlobal)
.def_readwrite("CoMVelocityWRTGlobal", &HumanState::CoMVelocityWRTGlobal);

CreateBufferedPort<HumanState>(module, "BufferedPortHumanState");
}
} // namespace msgs
} // namespace bindings
} // namespace hde
20 changes: 20 additions & 0 deletions bindings/python/msgs/src/Module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
// SPDX-License-Identifier: BSD-3-Clause

#include <pybind11/pybind11.h>

#include <hde/bindings/msgs/HumanState.h>

namespace hde {
namespace bindings {
namespace msgs {

void CreateModule(pybind11::module& module)
{
module.doc() = "YarpUtilities module.";

CreateHumanState(module);
}
} // namespace msgs
} // namespace bindings
} // namespace hde
46 changes: 46 additions & 0 deletions cmake/AddHDEPythonModule.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
# SPDX-License-Identifier: BSD-3-Clause

function(add_hde_python_module)

set(options )
set(oneValueArgs NAME)
set(multiValueArgs
SOURCES
HEADERS
LINK_LIBRARIES
TESTS
TESTS_RUNTIME_CONDITIONS)

set(prefix "hde")

cmake_parse_arguments(${prefix}
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN})

set(name ${${prefix}_NAME})
set(is_interface ${${prefix}_IS_INTERFACE})
set(sources ${${prefix}_SOURCES})
set(headers ${${prefix}_HEADERS})
set(link_libraries ${${prefix}_LINK_LIBRARIES})
set(subdirectories ${${prefix}_SUBDIRECTORIES})
set(tests ${${prefix}_TESTS})
set(tests_runtime_conditions ${${prefix}_TESTS_RUNTIME_CONDITIONS})

foreach(file ${headers})
set_property(GLOBAL APPEND PROPERTY pybind_headers ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()

foreach(file ${sources})
set_property(GLOBAL APPEND PROPERTY pybind_sources ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()

set_property(GLOBAL APPEND PROPERTY pybind_include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/include)

set_property(GLOBAL APPEND PROPERTY pybind_link_libraries ${link_libraries})

message(STATUS "Added files for bindings named ${name} in ${PROJECT_NAME}.")

endfunction()

0 comments on commit d78593a

Please sign in to comment.