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

Add Python bindings for human state-related data structures #379

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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()
Loading