Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]

### Added
- Add core single-precision Python bindings through the `pinocchio.float32` module
- Add `DataTpl::lastChild` deprecation notice in Python binding
- Fix `addFrame` to ignore frame without inertial to preserse parent body's CoM

Expand Down
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ cmake_dependent_option(
BUILD_PYTHON_INTERFACE
OFF
)
cmake_dependent_option(
BUILD_PYTHON_BINDINGS_WITH_FLOAT32_SUPPORT
"Build the Python interface with single-precision support"
ON
BUILD_PYTHON_INTERFACE
OFF
)
cmake_dependent_option(
BUILD_WITH_LIBPYTHON
"Build the library with Python format support"
Expand Down
16 changes: 16 additions & 0 deletions bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ if(BUILD_PYTHON_INTERFACE)
set(PYTHON_LIB_NAME "${PYWRAP}_default")
set(STUBGEN_DEPENDENCIES "${PYWRAP}_default")

if(BUILD_PYTHON_BINDINGS_WITH_FLOAT32_SUPPORT)
pinocchio_python_bindings_specific_type(float32)
list(APPEND STUBGEN_DEPENDENCIES "${PYWRAP}_float32")

if(PINOCCHIO_BUILD_BINDING_WITH_PCH)
target_precompile_headers(
${PYWRAP}_float32
PRIVATE
${PROJECT_SOURCE_DIR}/include/pinocchio/bindings/python/pch.hpp
)
endif()

# --- INSTALL SCRIPTS
install_python_files(MODULE float32 FILES __init__.py explog.py utils.py)
endif()

if(BUILD_WITH_AUTODIFF_SUPPORT)
pinocchio_python_bindings_specific_type(cppad cppad)
# CPPAD_DEBUG_AND_RELEASE allow to mix debug and release versions of CppAD in the same program.
Expand Down
4 changes: 2 additions & 2 deletions bindings/python/algorithm/expose-frames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ namespace pinocchio

bp::def(
"computeSupportedInertiaByFrame",
&computeSupportedInertiaByFrame<double, 0, JointCollectionDefaultTpl>,
&computeSupportedInertiaByFrame<Scalar, Options, JointCollectionDefaultTpl>,
bp::args("model", "data", "frame_id", "with_subtree"),
"Computes the supported inertia by the frame (given by frame_id) and returns it.\n"
"The supported inertia corresponds to the sum of the inertias of all the child frames "
Expand All @@ -296,7 +296,7 @@ namespace pinocchio

bp::def(
"computeSupportedForceByFrame",
&computeSupportedForceByFrame<double, 0, JointCollectionDefaultTpl>,
&computeSupportedForceByFrame<Scalar, Options, JointCollectionDefaultTpl>,
bp::args("model", "data", "frame_id"),
"Computes the supported force of the frame (given by frame_id) and returns it.\n"
"The supported force corresponds to the sum of all the forces experienced after the given "
Expand Down
31 changes: 17 additions & 14 deletions bindings/python/algorithm/expose-model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,17 @@ namespace pinocchio
void exposeModelAlgo()
{
using namespace Eigen;
typedef context::Scalar Scalar;
using context::Options;

typedef std::vector<GeometryModel> GeometryModelVector;
StdVectorPythonVisitor<GeometryModelVector>::expose("StdVec_GeometryModel");

bp::def(
"appendModel",
(Model (*)(
const Model &, const Model &, const FrameIndex,
const SE3 &))&appendModel<double, 0, JointCollectionDefaultTpl>,
(context::Model (*)(
const context::Model &, const context::Model &, const FrameIndex,
const context::SE3 &))&appendModel<Scalar, Options, JointCollectionDefaultTpl>,
bp::args("modelA", "modelB", "frame_in_modelA", "aMb"),
"Append a child model into a parent model, after a specific frame given by its index.\n\n"
"Parameters:\n"
Expand All @@ -119,7 +121,7 @@ namespace pinocchio
"\taMb: pose of modelB universe joint (index 0) in frameInModelA\n");

bp::def(
"appendModel", &appendModel_proxy<double, 0, JointCollectionDefaultTpl>,
"appendModel", &appendModel_proxy<Scalar, Options, JointCollectionDefaultTpl>,
bp::args("modelA", "modelB", "geomModelA", "geomModelB", "frame_in_modelA", "aMb"),
"Append a child (geometry) model into a parent (geometry) model, after a specific frame "
"given by its index.\n\n"
Expand All @@ -133,10 +135,10 @@ namespace pinocchio

bp::def(
"buildReducedModel",
(Model (*)(
const Model &, const std::vector<JointIndex> &,
const Eigen::MatrixBase<VectorXd> &))&pinocchio::
buildReducedModel<double, 0, JointCollectionDefaultTpl, VectorXd>,
(context::Model (*)(
const context::Model &, const std::vector<JointIndex> &,
const Eigen::MatrixBase<context::VectorXs> &))&pinocchio::
buildReducedModel<Scalar, Options, JointCollectionDefaultTpl, context::VectorXs>,
bp::args("model", "list_of_joints_to_lock", "reference_configuration"),
"Build a reduce model from a given input model and a list of joint to lock.\n\n"
"Parameters:\n"
Expand All @@ -148,9 +150,10 @@ namespace pinocchio
bp::def(
"buildReducedModel",
(bp::tuple (*)(
const Model &, const GeometryModel &, const std::vector<JointIndex> &,
const context::Model &, const GeometryModel &, const std::vector<JointIndex> &,
const Eigen::MatrixBase<
VectorXd> &))&buildReducedModel<double, 0, JointCollectionDefaultTpl, VectorXd>,
context::
VectorXs> &))&buildReducedModel<Scalar, Options, JointCollectionDefaultTpl, context::VectorXs>,
bp::args("model", "geom_model", "list_of_joints_to_lock", "reference_configuration"),
"Build a reduced model and a reduced geometry model from a given input model,"
"an input geometry model and a list of joints to lock.\n\n"
Expand All @@ -164,9 +167,9 @@ namespace pinocchio
bp::def(
"buildReducedModel",
(bp::tuple (*)(
const Model &, const std::vector<GeometryModel> &, const std::vector<JointIndex> &,
const Eigen::MatrixBase<VectorXd> &))
buildReducedModel<double, 0, JointCollectionDefaultTpl, VectorXd>,
const context::Model &, const std::vector<GeometryModel> &,
const std::vector<JointIndex> &, const Eigen::MatrixBase<context::VectorXs> &))
buildReducedModel<Scalar, Options, JointCollectionDefaultTpl, context::VectorXs>,
bp::args(
"model", "list_of_geom_models", "list_of_joints_to_lock", "reference_configuration"),
"Build a reduced model and the related reduced geometry models from a given "
Expand All @@ -191,7 +194,7 @@ namespace pinocchio

bp::def(
"transformJointIntoMimic",
transformJointIntoMimic_proxy<double, 0, JointCollectionDefaultTpl>,
transformJointIntoMimic_proxy<Scalar, Options, JointCollectionDefaultTpl>,
bp::args("input_model", "index_mimicked", "index_mimicking", "scaling", "offset"),
"Transform of a joint of the model into a mimic joint. Keep the type of the joint as it "
"was previously. \n\n"
Expand Down
28 changes: 15 additions & 13 deletions bindings/python/multibody/sample-models.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

#include "pinocchio/multibody/sample-models.hpp"
#include "pinocchio/bindings/python/fwd.hpp"

#include <boost/python.hpp>

Expand All @@ -12,38 +13,38 @@ namespace pinocchio
{
namespace bp = boost::python;

Model buildSampleModelHumanoidRandom(bool usingFF, bool mimic)
context::Model buildSampleModelHumanoidRandom(bool usingFF, bool mimic)
{
Model model;
context::Model model;
buildModels::humanoidRandom(model, usingFF, mimic);
return model;
}

Model buildSampleModelManipulator(bool mimic)
context::Model buildSampleModelManipulator(bool mimic)
{
Model model;
context::Model model;
buildModels::manipulator(model, mimic);
return model;
}

#ifdef PINOCCHIO_WITH_COLLISION
GeometryModel buildSampleGeometryModelManipulator(const Model & model)
GeometryModel buildSampleGeometryModelManipulator(const context::Model & model)
{
GeometryModel geom;
buildModels::manipulatorGeometries(model, geom);
return geom;
}
#endif

Model buildSampleModelHumanoid(bool usingFF)
context::Model buildSampleModelHumanoid(bool usingFF)
{
Model model;
context::Model model;
buildModels::humanoid(model, usingFF);
return model;
}

#ifdef PINOCCHIO_WITH_COLLISION
GeometryModel buildSampleGeometryModelHumanoid(const Model & model)
GeometryModel buildSampleGeometryModelHumanoid(const context::Model & model)
{
GeometryModel geom;
buildModels::humanoidGeometries(model, geom);
Expand All @@ -55,34 +56,35 @@ namespace pinocchio
{
bp::def(
"buildSampleModelHumanoidRandom",
static_cast<Model (*)(bool, bool)>(pinocchio::python::buildSampleModelHumanoidRandom),
static_cast<context::Model (*)(bool, bool)>(
pinocchio::python::buildSampleModelHumanoidRandom),
(bp::arg("using_free_flyer") = true, bp::arg("mimic") = false),
"Generate a (hard-coded) model of a humanoid robot with 6-DOF limbs and random joint "
"placements.\nOnly meant for unit tests.");

bp::def(
"buildSampleModelManipulator",
static_cast<Model (*)(bool)>(pinocchio::python::buildSampleModelManipulator),
static_cast<context::Model (*)(bool)>(pinocchio::python::buildSampleModelManipulator),
(bp::arg("mimic") = false), "Generate a (hard-coded) model of a simple manipulator.");

#ifdef PINOCCHIO_WITH_COLLISION
bp::def(
"buildSampleGeometryModelManipulator",
static_cast<GeometryModel (*)(const Model &)>(
static_cast<GeometryModel (*)(const context::Model &)>(
pinocchio::python::buildSampleGeometryModelManipulator),
bp::args("model"), "Generate a (hard-coded) geometry model of a simple manipulator.");
#endif

bp::def(
"buildSampleModelHumanoid",
static_cast<Model (*)(bool)>(pinocchio::python::buildSampleModelHumanoid),
static_cast<context::Model (*)(bool)>(pinocchio::python::buildSampleModelHumanoid),
(bp::arg("using_free_flyer") = true),
"Generate a (hard-coded) model of a simple humanoid.");

#ifdef PINOCCHIO_WITH_COLLISION
bp::def(
"buildSampleGeometryModelHumanoid",
static_cast<GeometryModel (*)(const Model &)>(
static_cast<GeometryModel (*)(const context::Model &)>(
pinocchio::python::buildSampleGeometryModelHumanoid),
bp::args("model"), "Generate a (hard-coded) geometry model of a simple humanoid.");
#endif
Expand Down
47 changes: 47 additions & 0 deletions bindings/python/pinocchio/float32/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#
# Copyright (c) 2026 Heriot-Watt University
#
"""Single-precision bindings for Pinocchio's kinematics and dynamics API.

Models, spatial types, algorithms, and NumPy results exposed here use ``float32``.
File parsers, parallel algorithms, and reachable-workspace helpers remain available
only from the main module. Geometry and collision objects keep their existing scalar
representation when used with a float32 model.
"""

# ruff: noqa: F401, F403, F405
# Manually register submodules
import inspect
import sys

from .. import pinocchio_pywrap_float32 as _pinocchio_pywrap_float32
from ..pinocchio_pywrap_float32 import *
from ..pinocchio_pywrap_float32 import __raw_version__, __version__
from . import utils
from .explog import exp, log

submodules = inspect.getmembers(_pinocchio_pywrap_float32, inspect.ismodule)
for module_info in submodules:
sys.modules[__name__ + "." + module_info[0]] = module_info[1]

if WITH_COLLISION:
import coal
from coal import (
CachedMeshLoader,
CollisionGeometry,
CollisionResult,
Contact,
DistanceResult,
MeshLoader,
StdVec_CollisionResult,
StdVec_Contact,
StdVec_DistanceResult,
)

# Pickling support becauso Vec3s is registered by
# coal and pinocchio (see pinocchio/binding/python/multibody/data.hpp)
coal.StdVec_Vec3s.__safe_for_unpickling__ = True
coal.StdVec_Vec3s.__getstate_manages_dict__ = True

# Deprecated, should be removed in next major release
hppfcl = coal
46 changes: 46 additions & 0 deletions bindings/python/pinocchio/float32/explog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#
# Copyright (c) 2015-2018 CNRS INRIA
# Copyright (c) 2015 Wandercraft, 86 rue de Paris 91400 Orsay, France.
# Copyright (c) 2026 Heriot-Watt University
#

import math

import numpy as np

from .. import pinocchio_pywrap_float32 as pin


def exp(x):
if isinstance(x, pin.Motion):
return pin.exp6(x)
if np.isscalar(x):
return math.exp(x)
if isinstance(x, np.ndarray):
if x.shape == (6, 1) or x.shape == (6,):
return pin.exp6(pin.Motion(x))
if x.shape == (3, 1) or x.shape == (3,):
return pin.exp3(x)
raise ValueError("Error only 3 and 6 vectors are allowed.")
raise ValueError(
"Error exp is only defined for real, vector3, vector6 and pin.Motion objects."
)


def log(x):
if isinstance(x, pin.SE3):
return pin.log6(x)
if np.isscalar(x):
return math.log(x)
if isinstance(x, np.ndarray):
if x.shape == (4, 4):
return pin.log6(x)
if x.shape == (3, 3):
return pin.log3(x)
raise ValueError("Error only 3 and 4 matrices are allowed.")
raise ValueError(
"Error log is only defined for real, matrix3, matrix4 and pin.SE3 objects."
)


__all__ = ["exp", "log"]
Loading