From 59bfe444f572574ce39e3a8712736059dd6599fe Mon Sep 17 00:00:00 2001 From: "Murilo M. Marinho" Date: Mon, 28 Sep 2026 13:57:21 +0100 Subject: [PATCH] Fixing stubs and a few wrong py::args. --- .github/workflows/python_package.yml | 4 - .gitignore | 1 + dqrobotics-stubs/README.md | 1 - dqrobotics/__init__.py | 2 +- dqrobotics/interfaces/__init__.py | 2 +- dqrobotics/interfaces/coppeliasim/__init__.py | 26 -- dqrobotics/interfaces/json11/__init__.py | 2 +- dqrobotics/interfaces/vrep/__init__.py | 26 -- dqrobotics/interfaces/vrep/robots/__init__.py | 26 -- dqrobotics/py.typed | 0 dqrobotics/robot_control/__init__.py | 2 +- dqrobotics/robot_modeling/__init__.py | 2 +- dqrobotics/robots/__init__.py | 2 +- dqrobotics/solvers/__init__.py | 6 +- dqrobotics/solvers/_dq_cplex_solver.py | 15 +- dqrobotics/solvers/_dq_quadprog_solver.py | 45 ++- dqrobotics/utils/DQ_LinearAlgebra/__init__.py | 2 +- dqrobotics/utils/DQ_Math/__init__.py | 2 +- dqrobotics/utils/__init__.py | 2 +- pyproject.toml | 8 +- regenerate_stubs.py | 163 ++++++--- setup.py | 8 +- src/DQ_py.cpp | 4 +- src/dqrobotics_module.h | 4 - src/interfaces/vrep/DQ_SerialVrepRobot_py.cpp | 75 ---- src/interfaces/vrep/DQ_VrepInterface_py.cpp | 329 ------------------ src/interfaces/vrep/DQ_VrepRobot_py.cpp | 58 --- .../DQ_DifferentialDriveRobot_py.cpp | 10 +- src/robot_modeling/DQ_Kinematics_py.cpp | 4 +- .../DQ_SerialManipulatorDH_py.cpp | 16 + .../DQ_SerialManipulatorDenso_py.cpp | 16 + .../DQ_SerialManipulatorMDH_py.cpp | 16 + .../DQ_SerialManipulator_py.cpp | 19 + src/utils/DQ_Geometry_py.cpp | 10 - 34 files changed, 251 insertions(+), 657 deletions(-) create mode 100644 .gitignore delete mode 100644 dqrobotics-stubs/README.md delete mode 100644 dqrobotics/interfaces/coppeliasim/__init__.py delete mode 100644 dqrobotics/interfaces/vrep/__init__.py delete mode 100644 dqrobotics/interfaces/vrep/robots/__init__.py create mode 100644 dqrobotics/py.typed delete mode 100644 src/interfaces/vrep/DQ_SerialVrepRobot_py.cpp delete mode 100644 src/interfaces/vrep/DQ_VrepInterface_py.cpp delete mode 100644 src/interfaces/vrep/DQ_VrepRobot_py.cpp diff --git a/.github/workflows/python_package.yml b/.github/workflows/python_package.yml index 4456455..2e51d45 100644 --- a/.github/workflows/python_package.yml +++ b/.github/workflows/python_package.yml @@ -59,11 +59,7 @@ jobs: run: | pip install pybind11-stubgen pip install . - mkdir -p stubs_temp - pybind11-stubgen dqrobotics --output-dir stubs_temp python regenerate_stubs.py - cp -r stubs_temp/dqrobotics/dqrobotics/ dqrobotics-stubs - rm -r stubs_temp pip uninstall dqrobotics -y - name: Compile run: | diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e33c7b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dqrobotics/_dqrobotics/ diff --git a/dqrobotics-stubs/README.md b/dqrobotics-stubs/README.md deleted file mode 100644 index ebb1447..0000000 --- a/dqrobotics-stubs/README.md +++ /dev/null @@ -1 +0,0 @@ -Adding this file as a placeholder otherwise the package cannot build before the stubs are generated. \ No newline at end of file diff --git a/dqrobotics/__init__.py b/dqrobotics/__init__.py index c5647c1..36514c3 100644 --- a/dqrobotics/__init__.py +++ b/dqrobotics/__init__.py @@ -23,5 +23,5 @@ # # ################################################################ """ -from dqrobotics._dqrobotics import * +from ._dqrobotics import * diff --git a/dqrobotics/interfaces/__init__.py b/dqrobotics/interfaces/__init__.py index f400db5..eacbed5 100644 --- a/dqrobotics/interfaces/__init__.py +++ b/dqrobotics/interfaces/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._interfaces import * +from .._dqrobotics._interfaces import * diff --git a/dqrobotics/interfaces/coppeliasim/__init__.py b/dqrobotics/interfaces/coppeliasim/__init__.py deleted file mode 100644 index 213e860..0000000 --- a/dqrobotics/interfaces/coppeliasim/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -# Copyright (c) 2019-2022 DQ Robotics Developers -# -# This file is part of DQ Robotics. -# -# DQ Robotics is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# DQ Robotics is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with DQ Robotics. If not, see . -# -# ################################################################ -# -# Contributors: -# - Murilo M. Marinho, email: murilo@g.u-tokyo.ac.jp -# -# ################################################################ -""" -from dqrobotics._dqrobotics._interfaces._coppeliasim import * diff --git a/dqrobotics/interfaces/json11/__init__.py b/dqrobotics/interfaces/json11/__init__.py index 701df31..86f3cfa 100644 --- a/dqrobotics/interfaces/json11/__init__.py +++ b/dqrobotics/interfaces/json11/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._interfaces._json11 import * +from ..._dqrobotics._interfaces._json11 import * diff --git a/dqrobotics/interfaces/vrep/__init__.py b/dqrobotics/interfaces/vrep/__init__.py deleted file mode 100644 index 925e80d..0000000 --- a/dqrobotics/interfaces/vrep/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -# Copyright (c) 2019-2022 DQ Robotics Developers -# -# This file is part of DQ Robotics. -# -# DQ Robotics is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# DQ Robotics is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with DQ Robotics. If not, see . -# -# ################################################################ -# -# Contributors: -# - Murilo M. Marinho, email: murilo@g.u-tokyo.ac.jp -# -# ################################################################ -""" -from dqrobotics._dqrobotics._interfaces._vrep import * diff --git a/dqrobotics/interfaces/vrep/robots/__init__.py b/dqrobotics/interfaces/vrep/robots/__init__.py deleted file mode 100644 index 1e2a8c9..0000000 --- a/dqrobotics/interfaces/vrep/robots/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -# Copyright (c) 2019-2022 DQ Robotics Developers -# -# This file is part of DQ Robotics. -# -# DQ Robotics is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# DQ Robotics is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with DQ Robotics. If not, see . -# -# ################################################################ -# -# Contributors: -# - Murilo M. Marinho, email: murilo@g.u-tokyo.ac.jp -# -# ################################################################ -""" -from dqrobotics._dqrobotics._interfaces._vrep._robots import * diff --git a/dqrobotics/py.typed b/dqrobotics/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/dqrobotics/robot_control/__init__.py b/dqrobotics/robot_control/__init__.py index 1801af8..dbcaab8 100644 --- a/dqrobotics/robot_control/__init__.py +++ b/dqrobotics/robot_control/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._robot_control import * +from .._dqrobotics._robot_control import * diff --git a/dqrobotics/robot_modeling/__init__.py b/dqrobotics/robot_modeling/__init__.py index 024d8e4..9d16054 100644 --- a/dqrobotics/robot_modeling/__init__.py +++ b/dqrobotics/robot_modeling/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._robot_modeling import * +from .._dqrobotics._robot_modeling import * diff --git a/dqrobotics/robots/__init__.py b/dqrobotics/robots/__init__.py index 7118062..031aea4 100644 --- a/dqrobotics/robots/__init__.py +++ b/dqrobotics/robots/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._robots import * +from .._dqrobotics._robots import * diff --git a/dqrobotics/solvers/__init__.py b/dqrobotics/solvers/__init__.py index 606231d..fae3d6e 100644 --- a/dqrobotics/solvers/__init__.py +++ b/dqrobotics/solvers/__init__.py @@ -23,14 +23,14 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._solvers import * +from .._dqrobotics._solvers import * try: - from dqrobotics.solvers._dq_quadprog_solver import DQ_QuadprogSolver + from dqrobotics.solvers._dq_quadprog_solver import DQ_QuadprogSolver as DQ_QuadprogSolver except: pass try: - from dqrobotics.solvers._dq_cplex_solver import DQ_CPLEXSolver + from dqrobotics.solvers._dq_cplex_solver import DQ_CPLEXSolver as DQ_CPLEXSolver except: pass diff --git a/dqrobotics/solvers/_dq_cplex_solver.py b/dqrobotics/solvers/_dq_cplex_solver.py index 5eedb4b..64e5082 100644 --- a/dqrobotics/solvers/_dq_cplex_solver.py +++ b/dqrobotics/solvers/_dq_cplex_solver.py @@ -23,21 +23,30 @@ # # ################################################################ """ +from __future__ import annotations from dqrobotics._dqrobotics._solvers import DQ_QuadraticProgrammingSolver import numpy as np +from numpy.typing import NDArray import cplex # https://github.com/dqrobotics/python/issues/24 class DQ_CPLEXSolver(DQ_QuadraticProgrammingSolver): - def __init__(self): + def __init__(self) -> None: DQ_QuadraticProgrammingSolver.__init__(self) # Make and set a solver instance - self.P = cplex.Cplex() + self.P: cplex.Cplex = cplex.Cplex() self.P.objective.set_sense(self.P.objective.sense.minimize) self.P.set_problem_type(self.P.problem_type.QP) self.P.set_results_stream(results_file=None) - def solve_quadratic_program(self, H, f, A, b, Aeq, beq): + # Narrower than the base class's ArrayLike: this implementation requires numpy arrays. + def solve_quadratic_program(self, # type: ignore[override] + H: NDArray[np.float64], + f: NDArray[np.float64], + A: NDArray[np.float64], + b: NDArray[np.float64], + Aeq: NDArray[np.float64], + beq: NDArray[np.float64]) -> NDArray[np.float64]: problem_size = H.shape[0] inequality_constraint_size = b.shape[0] equality_constraint_size = beq.shape[0] diff --git a/dqrobotics/solvers/_dq_quadprog_solver.py b/dqrobotics/solvers/_dq_quadprog_solver.py index 06000b1..0401e82 100644 --- a/dqrobotics/solvers/_dq_quadprog_solver.py +++ b/dqrobotics/solvers/_dq_quadprog_solver.py @@ -23,31 +23,41 @@ # # ################################################################ """ +from __future__ import annotations +from typing import Optional from dqrobotics._dqrobotics._solvers import DQ_QuadraticProgrammingSolver import numpy as np +from numpy.typing import NDArray import quadprog class DQ_QuadprogSolver(DQ_QuadraticProgrammingSolver): - def __init__(self): + def __init__(self) -> None: DQ_QuadraticProgrammingSolver.__init__(self) - self.equality_constraints_tolerance = 0 # default of np.finfo(np.float64).eps is already included in the solver + self.equality_constraints_tolerance: float = 0 # default of np.finfo(np.float64).eps is already included in the solver pass - def set_equality_constraints_tolerance(self, tolerance): + def set_equality_constraints_tolerance(self, tolerance: float) -> None: """ Set allowed tolerance for the equality constraints :param tolerance: Tolerance allowed for equality constraints """ self.equality_constraints_tolerance = tolerance - def get_equality_constraints_tolerance(self): + def get_equality_constraints_tolerance(self) -> float: """ Get allowed tolerance for the equality constraints :return: Current tolerance """ return self.equality_constraints_tolerance - def solve_quadratic_program(self, H, f, A, b, Aeq, beq): + # Narrower than the base class's ArrayLike: this implementation requires numpy arrays. + def solve_quadratic_program(self, # type: ignore[override] + H: NDArray[np.float64], + f: NDArray[np.float64], + A: Optional[NDArray[np.float64]], + b: Optional[NDArray[np.float64]], + Aeq: Optional[NDArray[np.float64]], + beq: Optional[NDArray[np.float64]]) -> NDArray[np.float64]: """ Solves the following quadratic program min(x) 0.5*x'Hx + f'x @@ -81,24 +91,29 @@ def solve_quadratic_program(self, H, f, A, b, Aeq, beq): # Turn equality into a bounded inequality ## Aeq.x <= beq + delta ## Aeq.x >= beq - delta ==> -Aeq.x <= -beq + delta - if Aeq is not None: # beq is None already checked by the ValueError + if Aeq is not None: + assert beq is not None # Already checked by the ValueError Aeq = np.vstack([Aeq, -Aeq]) beq = beq.reshape(-1) beq = np.concatenate([beq + self.equality_constraints_tolerance, -beq + self.equality_constraints_tolerance]) - # Use (A,b), (Aeq,beq), or both. - if Aeq is None: + # Use (A,b), (Aeq,beq), or both. The asserts were already checked by the ValueError. + A_internal: NDArray[np.float64] + b_internal: NDArray[np.float64] + if A is not None and Aeq is not None: + assert b is not None and beq is not None + A_internal = np.vstack([A, Aeq]) + b_internal = np.concatenate([b.reshape(-1), beq]) + elif A is not None: + assert b is not None A_internal = A b_internal = b - if A is None: + elif Aeq is not None: + assert beq is not None A_internal = Aeq b_internal = beq - if Aeq is not None and A is not None: - A_internal = np.vstack([A, Aeq]) - b_internal = np.concatenate([b.reshape(-1), beq]) - - # Solve for the unconstrained case. quadprog does not accept None, so we add a dummy constraint. - if A is None and b is None and Aeq is None and beq is None: + else: + # Solve for the unconstrained case. quadprog does not accept None, so we add a dummy constraint. A_internal = np.zeros((1, H.shape[0])) b_internal = np.zeros(1) diff --git a/dqrobotics/utils/DQ_LinearAlgebra/__init__.py b/dqrobotics/utils/DQ_LinearAlgebra/__init__.py index 3a127db..74f8dec 100644 --- a/dqrobotics/utils/DQ_LinearAlgebra/__init__.py +++ b/dqrobotics/utils/DQ_LinearAlgebra/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._utils._DQ_LinearAlgebra import * +from ..._dqrobotics._utils._DQ_LinearAlgebra import * diff --git a/dqrobotics/utils/DQ_Math/__init__.py b/dqrobotics/utils/DQ_Math/__init__.py index da924d0..69ce290 100644 --- a/dqrobotics/utils/DQ_Math/__init__.py +++ b/dqrobotics/utils/DQ_Math/__init__.py @@ -1 +1 @@ -from dqrobotics._dqrobotics._utils._DQ_Math import * +from ..._dqrobotics._utils._DQ_Math import * diff --git a/dqrobotics/utils/__init__.py b/dqrobotics/utils/__init__.py index 273d3d8..ff6b598 100644 --- a/dqrobotics/utils/__init__.py +++ b/dqrobotics/utils/__init__.py @@ -23,4 +23,4 @@ # # ################################################################ """ -from dqrobotics._dqrobotics._utils import * +from .._dqrobotics._utils import * diff --git a/pyproject.toml b/pyproject.toml index 89cf6d6..9973bbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,4 +35,10 @@ Issues = "https://github.com/dqrobotics/python/issues" enabled = true # https://stackoverflow.com/questions/73605607/how-to-use-setuptools-scm dev_template = "{tag}.a{ccount}" -dirty_template = "{tag}.a{ccount}" \ No newline at end of file +dirty_template = "{tag}.a{ccount}" + +[tool.mypy] +# The optional solver backends of dqrobotics.solvers ship no type information. +[[tool.mypy.overrides]] +module = ["quadprog", "cplex"] +ignore_missing_imports = true diff --git a/regenerate_stubs.py b/regenerate_stubs.py index a0fd2f5..14784e7 100644 --- a/regenerate_stubs.py +++ b/regenerate_stubs.py @@ -1,67 +1,122 @@ """ -Fix the stubs created by pybind11-stubgen +Generate the stubs of the compiled `dqrobotics._dqrobotics` module, to be shipped inside the `dqrobotics` package. -The default version of pybind11 stubgen will generate something like +The public modules of dqrobotics (e.g. `dqrobotics.robot_modeling`) are Python files that do +`from .._dqrobotics._robot_modeling import *`, so type checkers read them directly. Only the compiled module +needs stubs, which pybind11-stubgen writes next to it, as below. Together with `dqrobotics/py.typed`, this makes +dqrobotics an inline-typed package (PEP 561). -. -└── dqrobotics - ├── __init__.pyi - └── _dqrobotics - ├── __init__.pyi - ├── _interfaces - │   ├── __init__.pyi - │   └── _json11.pyi - ├── _robot_control.pyi - ├── _robot_modeling.pyi - ├── _robots.pyi - ├── _solvers.pyi - └── _utils - ├── _DQ_LinearAlgebra.pyi - ├── _DQ_Math.pyi - └── __init__.pyi - -which is not compatible with dqrobotics' structure. It's not clear to me why that's the case. -This script will adjust it to something like below, which make the stubs quite useful. - -. -├── README.md -├── __init__.pyi -├── interfaces -│   ├── __init__.pyi -│   └── json11 -│   └── __init__.py -├── robot_control -│   └── __init__.py +dqrobotics +├── py.typed +├── __init__.py ├── robot_modeling -│   └── __init__.py -├── robots -│   └── __init__.py -├── solvers -│   └── __init__.py -└── utils - ├── DQ_LinearAlgebra - │   └── __init__.py - ├── DQ_Math - │   └── __init__.py - └── __init__.pyi +│ └── __init__.py +├── ... +├── _dqrobotics..so (compiled module) +└── _dqrobotics (generated stubs) + ├── __init__.pyi + ├── _robot_modeling.pyi + └── ... + +Usage (with dqrobotics and pybind11-stubgen installed in the current environment) + python regenerate_stubs.py Author: Murilo M. Marinho """ import os +import re +import shutil +import subprocess +import sys +import tempfile + +OUTPUT_DIR: str = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dqrobotics", "_dqrobotics") +MODULE: str = "dqrobotics._dqrobotics" + + +def remove_keyword_identifiers(stub_file: str) -> None: + """ + Remove the attributes named `None` from a stub file. + `ControlObjective.None` is valid at runtime, through getattr(), but `None: ...` is a syntax error in a stub. + :param stub_file: The path to the stub file. + """ + with open(stub_file) as f: + content = f.read() + content = re.sub(r"^[ \t]*None: .*\n", "", content, flags=re.MULTILINE) + content = content.replace("'None', ", "") + with open(stub_file, "w") as f: + f.write(content) + -def main(): - cwd = os.path.join(os.getcwd(),"stubs_temp") - for root, dirs, files in os.walk(cwd, topdown=False): +def replace_once(content: str, old: str, new: str) -> str: + """ + Replace a snippet of generated stub that must appear exactly once, so that changes in pybind11-stubgen's output are + noticed instead of silently ignored. + """ + if content.count(old) != 1: + raise RuntimeError(f"Expected exactly one occurrence of:\n{old}") + return content.replace(old, new) + + +def complete_dq_comparison(stub_file: str) -> None: + """ + Make the stub of `DQ` consistent with `object`, as expected by type checkers. + - `__eq__` and `__ne__` also accept any other object at runtime, for which pybind11 returns NotImplemented and Python + falls back to the default comparison. That last overload cannot be expressed in the bindings with a `bool` result. + - `__hash__` is None because `DQ` defines `__eq__`. This is declared as in typeshed (e.g. `list`), which needs + the ignore because `object.__hash__` is a method. + :param stub_file: The path to the stub file of `dqrobotics._dqrobotics`. + """ + with open(stub_file) as f: + content = f.read() + content = replace_once(content, + " __hash__: typing.ClassVar[None] = None\n", + " __hash__: typing.ClassVar[None] = None # type: ignore[assignment]\n") + for method, result in (("__eq__", "False"), ("__ne__", "True")): + last_overload = re.findall(rf' def {method}\(self, arg0: typing\.SupportsFloat \| typing\.SupportsIndex\) -> bool:\n' + rf' """\n.*?\n """\n', content, flags=re.DOTALL) + if len(last_overload) != 1: + raise RuntimeError(f"Expected exactly one scalar overload of DQ.{method}") + content = replace_once(content, last_overload[0], last_overload[0] + + " @typing.overload\n" + f" def {method}(self, other: object) -> bool:\n" + ' """\n' + f" Returns {result} for any other object, through Python's default comparison.\n" + ' """\n') + with open(stub_file, "w") as f: + f.write(content) + + +def normalize_empty_all(stub_dir: str) -> None: + """ + Write the empty `__all__` of the generated stubs as `[]`. pybind11-stubgen writes `list()`, which type checkers + do not evaluate, so they cannot tell which names the module exports. + :param stub_dir: The directory with the stub files. + """ + for root, _, files in os.walk(stub_dir): for name in files: - if name.startswith('__'): - continue - elif name.startswith('_') and name.endswith('.pyi'): - os.makedirs(os.path.join(root, name[1:-4]), exist_ok=True) - os.rename(os.path.join(root, name), os.path.join(root, name[1:-4], "__init__.py")) - for name in dirs: - if name.startswith('_'): - os.rename(os.path.join(root, name), os.path.join(root, name[1:])) + if name.endswith(".pyi"): + stub_file = os.path.join(root, name) + with open(stub_file) as f: + content = f.read() + with open(stub_file, "w") as f: + f.write(content.replace("__all__: list[str] = list()", "__all__: list[str] = []")) + + +def main() -> None: + with tempfile.TemporaryDirectory() as temp_dir: + # Run from temp_dir so that the source folder `dqrobotics`, which lacks the compiled module, does not shadow + # the installed package. + subprocess.check_call([sys.executable, "-m", "pybind11_stubgen", MODULE, "--output-dir", temp_dir, + "--exit-code"], + cwd=temp_dir) + shutil.rmtree(OUTPUT_DIR, ignore_errors=True) + shutil.copytree(os.path.join(temp_dir, *MODULE.split(".")), OUTPUT_DIR) + normalize_empty_all(OUTPUT_DIR) + remove_keyword_identifiers(os.path.join(OUTPUT_DIR, "_robot_control.pyi")) + complete_dq_comparison(os.path.join(OUTPUT_DIR, "__init__.pyi")) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/setup.py b/setup.py index 8f9dd9e..3e60827 100644 --- a/setup.py +++ b/setup.py @@ -11,13 +11,13 @@ class CMakeExtension(Extension): - def __init__(self, name, sourcedir=''): + def __init__(self, name: str, sourcedir: str = '') -> None: Extension.__init__(self, name, sources=[]) self.sourcedir = os.path.abspath(sourcedir) class CMakeBuild(build_ext): - def run(self): + def run(self) -> None: try: out = subprocess.check_output(['cmake', '--version']) except OSError: @@ -32,7 +32,7 @@ def run(self): for ext in self.extensions: self.build_extension(ext) - def build_extension(self, ext): + def build_extension(self, ext: CMakeExtension) -> None: extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, '-DPYTHON_EXECUTABLE=' + sys.executable] @@ -71,7 +71,7 @@ def build_extension(self, ext): zip_safe=False, packages=find_namespace_packages(where='.', exclude=['*pybind11*', '*tests*']), package_data={ - 'dqrobotics-stubs': ["**/*.pyi"], + 'dqrobotics': ["py.typed", "_dqrobotics/**/*.pyi"], }, classifiers=[ "Programming Language :: Python :: 3.10", diff --git a/src/DQ_py.cpp b/src/DQ_py.cpp index 5dcddb4..e09319c 100644 --- a/src/DQ_py.cpp +++ b/src/DQ_py.cpp @@ -107,9 +107,9 @@ void init_DQ_py(py::module& m) dq.def(py::self + double(), "Returns the addition between this dual quaternion and a scalar."); dq.def(double() - py::self, "Returns the subtraction between a scalar and this dual quaternion."); dq.def(py::self - double(), "Returns the subtraction between this dual quaternion and a scalar."); - dq.def(double() == py::self, "Returns true if a scalar and this dual quaternion are equal, up to a numerical threshold."); + // No `double() == py::self` or `double() != py::self`: they would bind a second, unreachable `__eq__`/`__ne__`, + // since Python already evaluates `scalar == dq` as the reflected `dq == scalar`. dq.def(py::self == double(), "Returns true if this dual quaternion and a scalar are equal, up to a numerical threshold."); - dq.def(double() != py::self, "Returns true if a scalar and this dual quaternion are different, up to a numerical threshold."); dq.def(py::self != double(), "Returns true if this dual quaternion and a scalar are different, up to a numerical threshold."); ///Namespace Functions diff --git a/src/dqrobotics_module.h b/src/dqrobotics_module.h index 8fceb5b..dd9b65c 100644 --- a/src/dqrobotics_module.h +++ b/src/dqrobotics_module.h @@ -98,10 +98,6 @@ void init_DQ_QuadraticProgrammingController_py(py::module& m); //dqrobotics/solvers void init_DQ_QuadraticProgrammingSolver_py(py::module& m); -//dqrobotics/interfaces/coppeliasim -void init_DQ_CoppeliaSimInterface_py(py::module& m); -void init_DQ_CoppeliaSimInterfaceZMQ_py(py::module& m); - //dqrobotics/interfaces/json11 void init_DQ_JsonReader_py(py::module& m); diff --git a/src/interfaces/vrep/DQ_SerialVrepRobot_py.cpp b/src/interfaces/vrep/DQ_SerialVrepRobot_py.cpp deleted file mode 100644 index 75bd376..0000000 --- a/src/interfaces/vrep/DQ_SerialVrepRobot_py.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/** -(C) Copyright 2023 DQ Robotics Developers - -This file is part of DQ Robotics. - - DQ Robotics is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - DQ Robotics is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with DQ Robotics. If not, see . - -Contributors: -- Murilo M. Marinho (murilomarinho@ieee.org) -*/ - -#include "../../dqrobotics_module.h" - -/** - * @brief Binds `DQ_SerialVrepRobot`, a serial-robot wrapper that exposes joint - * names, target commands, velocities, and torques in CoppeliaSim, to the - * Python module @p m. - */ -void init_DQ_SerialVrepRobot_py(py::module& m) -{ - /***************************************************** - * SerialVrepRobot - * **************************************************/ - py::class_< - DQ_SerialVrepRobot, - std::shared_ptr, - DQ_VrepRobot - > dqsv_robot( - m, - "DQ_SerialVrepRobot", - "Serial robot wrapper for exchanging joint names, target commands, velocities, and torques with CoppeliaSim."); - - - dqsv_robot.def("get_joint_names", - &DQ_SerialVrepRobot::get_joint_names, - "Gets the joint names used in CoppeliaSim."); - - dqsv_robot.def("set_target_configuration_space_positions", - &DQ_SerialVrepRobot::set_target_configuration_space_positions, - py::arg("q"), - "Sets the target configuration-space positions in CoppeliaSim."); - - dqsv_robot.def("get_configuration_space_velocities", - &DQ_SerialVrepRobot::get_configuration_space_velocities, - "Gets the configuration-space velocities from CoppeliaSim."); - dqsv_robot.def("set_target_configuration_space_velocities", - &DQ_SerialVrepRobot::set_target_configuration_space_velocities, - py::arg("q_dot"), - "Sets the target configuration-space velocities in CoppeliaSim."); - - dqsv_robot.def("set_configuration_space_torques", - &DQ_SerialVrepRobot::set_configuration_space_torques, - py::arg("torques"), - "Sets the configuration-space torques in CoppeliaSim."); - dqsv_robot.def("get_configuration_space_torques", - &DQ_SerialVrepRobot::get_configuration_space_torques, - "Gets the configuration-space torques from CoppeliaSim."); - - //Deprecated - dqsv_robot.def("send_q_target_to_vrep", - &DQ_SerialVrepRobot::send_q_target_to_vrep, - py::arg("q"), - "Deprecated alias for setting the target configuration-space positions in CoppeliaSim."); -} diff --git a/src/interfaces/vrep/DQ_VrepInterface_py.cpp b/src/interfaces/vrep/DQ_VrepInterface_py.cpp deleted file mode 100644 index 243639f..0000000 --- a/src/interfaces/vrep/DQ_VrepInterface_py.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/** -(C) Copyright 2019-2023 DQ Robotics Developers - -This file is part of DQ Robotics. - - DQ Robotics is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - DQ Robotics is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with DQ Robotics. If not, see . - -Contributors: -1. Murilo M. Marinho (murilomarinho@ieee.org) - - Initial implementation. - -2. Juan Jose Quiroz Omana (juanjqo@g.ecc.u-tokyo.ac.jp) - -Added the method wait_for_simulation_step_to_end() -*/ - -#include "../../dqrobotics_module.h" - -//Default arguments added with: -//https://pybind11.readthedocs.io/en/stable/basics.html#default-args - -/** - * @brief Binds `DQ_VrepInterface`, an interface for connecting to - * V-REP/CoppeliaSim and exchanging simulation, object, joint, and inertial - * data, to the Python module @p m. - */ -void init_DQ_VrepInterface_py(py::module& m) -{ - /***************************************************** - * VrepInterface - * **************************************************/ - py::class_< - DQ_VrepInterface, - std::shared_ptr - > dqvrepinterface_py( - m, - "DQ_VrepInterface", - "Interface for connecting to V-REP/CoppeliaSim and exchanging simulation, object, joint, and inertial data."); - dqvrepinterface_py.def(py::init<>(), "Constructs a V-REP/CoppeliaSim interface."); - dqvrepinterface_py.def(py::init(), - py::arg("termination_flag"), - "Constructs a V-REP/CoppeliaSim interface that uses an external atomic termination flag."); - - py::enum_(dqvrepinterface_py, "OP_MODES") - .value("OP_BUFFER", DQ_VrepInterface::OP_MODES::OP_BUFFER) - .value("OP_ONESHOT", DQ_VrepInterface::OP_MODES::OP_ONESHOT) - .value("OP_BLOCKING", DQ_VrepInterface::OP_MODES::OP_BLOCKING) - .value("OP_STREAMING", DQ_VrepInterface::OP_MODES::OP_STREAMING) - .value("OP_AUTOMATIC", DQ_VrepInterface::OP_MODES::OP_AUTOMATIC) - .export_values(); - - py::enum_(dqvrepinterface_py, "SCRIPT_TYPES") - .value("ST_CHILD", DQ_VrepInterface::SCRIPT_TYPES::ST_CHILD) - .value("ST_MAIN", DQ_VrepInterface::SCRIPT_TYPES::ST_MAIN) - .value("ST_CUSTOMIZATION", DQ_VrepInterface::SCRIPT_TYPES::ST_CUSTOMIZATION) - .export_values(); - - py::enum_(dqvrepinterface_py, "REFERENCE_FRAMES") - .value("BODY_FRAME", DQ_VrepInterface::REFERENCE_FRAMES::BODY_FRAME) - .value("ABSOLUTE_FRAME", DQ_VrepInterface::REFERENCE_FRAMES::ABSOLUTE_FRAME) - .export_values(); - - dqvrepinterface_py.def("connect", - (bool (DQ_VrepInterface::*) (const int&, const int&, const int&))&DQ_VrepInterface::connect, - py::arg("port"), - py::arg("max_attempts"), - py::arg("attempt_interval_in_ms"), - "Attempts to connect to a V-REP/CoppeliaSim server on the local machine."); - dqvrepinterface_py.def("connect", - (bool (DQ_VrepInterface::*) (const std::string&, const int&, const int&, const int&))&DQ_VrepInterface::connect, - py::arg("ip"), - py::arg("port"), - py::arg("max_attempts"), - py::arg("attempt_interval_in_ms"), - "Attempts to connect to a V-REP/CoppeliaSim server at the given IP address."); - - dqvrepinterface_py.def("disconnect", &DQ_VrepInterface::disconnect, "Disconnects from the V-REP/CoppeliaSim server."); - dqvrepinterface_py.def("disconnect_all", &DQ_VrepInterface::disconnect_all, "Disconnects all active V-REP/CoppeliaSim connections."); - - dqvrepinterface_py.def("start_simulation", &DQ_VrepInterface::start_simulation, "Starts the simulation."); - dqvrepinterface_py.def("stop_simulation", &DQ_VrepInterface::stop_simulation, "Stops the simulation."); - - dqvrepinterface_py.def("is_simulation_running", &DQ_VrepInterface::is_simulation_running, "Returns whether the simulation is currently running."); - - // void set_synchronous(const bool& flag); - dqvrepinterface_py.def("set_synchronous", - (void (DQ_VrepInterface::*) (const bool&))&DQ_VrepInterface::set_synchronous, - py::arg("flag"), - "Enables or disables synchronous simulation mode."); - - //void trigger_next_simulation_step(); - dqvrepinterface_py.def("trigger_next_simulation_step", &DQ_VrepInterface::trigger_next_simulation_step, "Sends the synchronization trigger for the next simulation step."); - - //void wait_for_simulation_step_to_end(); - dqvrepinterface_py.def("wait_for_simulation_step_to_end", &DQ_VrepInterface::wait_for_simulation_step_to_end, "Waits until the current simulation step finishes."); - - //dqvrepinterface_py.def("get_object_handle", &DQ_VrepInterface::get_object_handle,"Gets an object handle"); - //dqvrepinterface_py.def("get_object_handles",&DQ_VrepInterface::get_object_handles,"Get object handles"); - - //dqvrepinterface_py.def("get_object_translation",(DQ (DQ_VrepInterface::*) (const int&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_translation,"Gets object translation."); - //dqvrepinterface_py.def("get_object_translation",(DQ (DQ_VrepInterface::*) (const std::string&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_translation,"Gets object translation."); - //dqvrepinterface_py.def("get_object_translation",(DQ (DQ_VrepInterface::*) (const int&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_translation,"Gets object translation."); - dqvrepinterface_py.def("get_object_translation", - (DQ (DQ_VrepInterface::*) (const std::string&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_translation, - py::arg("object_name") = std::string(""), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets an object's translation, optionally relative to another object."); - - //dqvrepinterface_py.def("set_object_translation",(void (DQ_VrepInterface::*) (const int&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&) const)&DQ_VrepInterface::set_object_translation,"Sets object translation."); - //dqvrepinterface_py.def("set_object_translation",(void (DQ_VrepInterface::*) (const std::string&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_translation,"Sets object translation."); - //dqvrepinterface_py.def("set_object_translation",(void (DQ_VrepInterface::*) (const int&, const std::string&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_translation,"Sets object translation."); - dqvrepinterface_py.def("set_object_translation", - (void (DQ_VrepInterface::*) (const std::string&, const DQ&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_translation, - py::arg("object_name") = std::string(""), - py::arg("translation") = DQ(0), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets an object's translation, optionally relative to another object."); - - //dqvrepinterface_py.def("get_object_rotation",(DQ (DQ_VrepInterface::*) (const int&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_rotation,"Gets object rotation."); - //dqvrepinterface_py.def("get_object_rotation",(DQ (DQ_VrepInterface::*) (const std::string&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_rotation,"Gets object rotation."); - //dqvrepinterface_py.def("get_object_rotation",(DQ (DQ_VrepInterface::*) (const int&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_rotation,"Gets object rotation."); - dqvrepinterface_py.def("get_object_rotation", - (DQ (DQ_VrepInterface::*) (const std::string&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_rotation, - py::arg("object_name") = std::string(""), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets an object's rotation, optionally relative to another object."); - - //dqvrepinterface_py.def("set_object_rotation",(void (DQ_VrepInterface::*) (const int&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&) const)&DQ_VrepInterface::set_object_rotation,"Sets object rotation."); - //dqvrepinterface_py.def("set_object_rotation",(void (DQ_VrepInterface::*) (const std::string&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_rotation,"Sets object rotation."); - //dqvrepinterface_py.def("set_object_rotation",(void (DQ_VrepInterface::*) (const int&, const std::string&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_rotation,"Sets object rotation."); - dqvrepinterface_py.def("set_object_rotation", - (void (DQ_VrepInterface::*) (const std::string&, const DQ&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_rotation, - py::arg("object_name") = std::string(""), - py::arg("rotation") = DQ(1), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets an object's rotation, optionally relative to another object."); - - //dqvrepinterface_py.def("get_object_pose",(DQ (DQ_VrepInterface::*) (const int&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_pose,"Gets object pose."); - //dqvrepinterface_py.def("get_object_pose",(DQ (DQ_VrepInterface::*) (const std::string&, const int&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_pose,"Gets object pose."); - //dqvrepinterface_py.def("get_object_pose",(DQ (DQ_VrepInterface::*) (const int&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_pose,"Gets object pose."); - dqvrepinterface_py.def("get_object_pose", - (DQ (DQ_VrepInterface::*) (const std::string&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_object_pose, - py::arg("object_name") = std::string(""), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets an object's pose, optionally relative to another object."); - - //dqvrepinterface_py.def("set_object_pose",(void (DQ_VrepInterface::*) (const int&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&) const)&DQ_VrepInterface::set_object_pose,"Sets object pose."); - //dqvrepinterface_py.def("set_object_pose",(void (DQ_VrepInterface::*) (const std::string&, const int&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_pose,"Sets object pose."); - //dqvrepinterface_py.def("set_object_pose",(void (DQ_VrepInterface::*) (const int&, const std::string&, const DQ&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_pose,"Sets object pose."); - dqvrepinterface_py.def("set_object_pose", - (void (DQ_VrepInterface::*) (const std::string&, const DQ&, const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_object_pose, - py::arg("object_name") = std::string(""), - py::arg("pose") = DQ(1), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets an object's pose, optionally relative to another object."); - - dqvrepinterface_py.def("get_object_poses", - &DQ_VrepInterface::get_object_poses, - py::arg("object_names"), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets the poses of multiple objects, optionally relative to another object."); - dqvrepinterface_py.def("set_object_poses", - &DQ_VrepInterface::set_object_poses, - py::arg("object_names"), - py::arg("poses"), - py::arg("relative_to_object_name") = VREP_OBJECTNAME_ABSOLUTE, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets the poses of multiple objects, optionally relative to another object."); - - //dqvrepinterface_py.def("set_joint_position",(void (DQ_VrepInterface::*) (const int&, const double&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::set_joint_position,"Set joint position"); - dqvrepinterface_py.def("set_joint_position", - (void (DQ_VrepInterface::*) (const std::string&, const double&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_position, - py::arg("joint_name") = std::string(""), - py::arg("angle_rad") = 0.0, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets one joint position in radians."); - - //dqvrepinterface_py.def("set_joint_target_position",(void (DQ_VrepInterface::*) (const int&, const double&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::set_joint_target_position,"Set joint position"); - dqvrepinterface_py.def("set_joint_target_position", - (void (DQ_VrepInterface::*) (const std::string&, const double&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_target_position, - py::arg("joint_name") = std::string(""), - py::arg("angle_rad") = 0.0, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets one joint target position in radians."); - - //dqvrepinterface_py.def("get_joint_position",(double (DQ_VrepInterface::*) (const int&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::get_joint_position,"Get joint position"); - dqvrepinterface_py.def("get_joint_position", - (double (DQ_VrepInterface::*) (const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_position, - py::arg("joint_name") = std::string(""), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets one joint position in radians."); - - //dqvrepinterface_py.def("set_joint_positions",(void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::set_joint_positions,"Set joint positions"); - dqvrepinterface_py.def("set_joint_positions", - (void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_positions, - py::arg("joint_names") = std::vector(), - py::arg("angles_rad") = VectorXd::Zero(1), - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets multiple joint positions in radians."); - - //dqvrepinterface_py.def("set_joint_target_positions",(void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::set_joint_target_positions,"Set joint positions"); - dqvrepinterface_py.def("set_joint_target_positions", - (void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_target_positions, - py::arg("joint_names") = std::vector(), - py::arg("angles_rad") = VectorXd::Zero(1), - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets multiple joint target positions in radians."); - - //dqvrepinterface_py.def("get_joint_positions",(VectorXd (DQ_VrepInterface::*) (const std::vector&, const DQ_VrepInterface::OP_MODES&) const) &DQ_VrepInterface::get_joint_positions,"Get joint positions"); - dqvrepinterface_py.def("get_joint_positions", - (VectorXd (DQ_VrepInterface::*) (const std::vector&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_positions, - py::arg("joint_names") = std::vector(), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets multiple joint positions in radians."); - - - //void set_joint_target_velocity(const std::string& jointname, const double& angle_dot_rad, const OP_MODES& opmode=OP_ONESHOT); - dqvrepinterface_py.def("set_joint_target_velocity", - (void (DQ_VrepInterface::*) (const std::string&, const double&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_target_velocity, - py::arg("joint_name") = std::string(""), - py::arg("angle_dot_rad") = 0.0, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets one joint target velocity in radians per second."); - - //void set_joint_target_velocities(const std::vector& jointnames, const VectorXd& angles_dot_rad, const OP_MODES& opmode=OP_ONESHOT); - dqvrepinterface_py.def("set_joint_target_velocities", - (void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_target_velocities, - py::arg("joint_names") = std::vector(), - py::arg("angles_dot_rad") = VectorXd::Zero(1), - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets multiple joint target velocities in radians per second."); - - //double get_joint_velocity(const std::string& jointname, const OP_MODES& opmode=OP_AUTOMATIC); - dqvrepinterface_py.def("get_joint_velocity", - (double (DQ_VrepInterface::*) (const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_velocity, - py::arg("joint_name") = std::string(""), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets one joint velocity in radians per second."); - - //VectorXd get_joint_velocities(const std::vector& jointnames, const OP_MODES& opmode=OP_AUTOMATIC); - dqvrepinterface_py.def("get_joint_velocities", - (VectorXd (DQ_VrepInterface::*) (const std::vector&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_velocities, - py::arg("joint_names") = std::vector(), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets multiple joint velocities in radians per second."); - - - //void set_joint_torque(const std::string& jointname, const double& torque, const OP_MODES& opmode=OP_ONESHOT); - dqvrepinterface_py.def("set_joint_torque", - (void (DQ_VrepInterface::*) (const std::string&, const double&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_torque, - py::arg("joint_name") = std::string(""), - py::arg("torque") = 0.0, - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets one joint torque."); - - //void set_joint_torques(const std::vector& jointnames, const VectorXd& torques, const OP_MODES& opmode=OP_ONESHOT); - dqvrepinterface_py.def("set_joint_torques", - (void (DQ_VrepInterface::*) (const std::vector&, const VectorXd&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::set_joint_torques, - py::arg("joint_names") = std::vector(), - py::arg("torques") = VectorXd::Zero(1), - py::arg("op_mode") = DQ_VrepInterface::OP_ONESHOT, - "Sets multiple joint torques."); - - //double get_joint_torque(const std::string& jointname, const OP_MODES& opmode=OP_AUTOMATIC); - dqvrepinterface_py.def("get_joint_torque", - (double (DQ_VrepInterface::*) (const std::string&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_torque, - py::arg("joint_name") = std::string(""), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets one joint torque."); - - //VectorXd get_joint_torques(const std::vector& jointnames, const OP_MODES& opmode=OP_AUTOMATIC); - dqvrepinterface_py.def("get_joint_torques", - (VectorXd (DQ_VrepInterface::*) (const std::vector&, const DQ_VrepInterface::OP_MODES&))&DQ_VrepInterface::get_joint_torques, - py::arg("joint_names") = std::vector(), - py::arg("op_mode") = DQ_VrepInterface::OP_AUTOMATIC, - "Gets multiple joint torques."); - - //double get_mass(const std::string& link_name, const std::string& function_name = "get_mass", const std::string& obj_name= "DQRoboticsApiCommandServer"); - dqvrepinterface_py.def("get_mass", - (double (DQ_VrepInterface::*) (const std::string&, const std::string&, const std::string&))&DQ_VrepInterface::get_mass, - py::arg("link_name") = std::string(""), - py::arg("function_name") = std::string("get_mass"), - py::arg("obj_name") = std::string("DQRoboticsApiCommandServer"), - "Gets the mass of an object from CoppeliaSim."); - - //DQ get_center_of_mass(const std::string& link_name, - // const REFERENCE_FRAMES& reference_frame=BODY_FRAME, - // const std::string& function_name = "get_center_of_mass", - // const std::string& obj_name= "DQRoboticsApiCommandServer"); - dqvrepinterface_py.def("get_center_of_mass", - (DQ (DQ_VrepInterface::*) (const std::string&, const DQ_VrepInterface::REFERENCE_FRAMES&, - const std::string&, const std::string&))&DQ_VrepInterface::get_center_of_mass, - py::arg("link_name") = std::string(""), - py::arg("reference_frame") = DQ_VrepInterface::BODY_FRAME, - py::arg("function_name") = std::string("get_center_of_mass"), - py::arg("obj_name") = std::string("DQRoboticsApiCommandServer"), - "Gets the center of mass of an object from CoppeliaSim in the requested reference frame."); - - //MatrixXd get_inertia_matrix(const std::string& link_name, - // const REFERENCE_FRAMES& reference_frame=BODY_FRAME, - // const std::string& function_name = "get_inertia", - // const std::string& obj_name= "DQRoboticsApiCommandServer"); - dqvrepinterface_py.def("get_inertia_matrix", - (MatrixXd (DQ_VrepInterface::*) (const std::string&, const DQ_VrepInterface::REFERENCE_FRAMES&, - const std::string&, const std::string&))&DQ_VrepInterface::get_inertia_matrix, - py::arg("link_name") = std::string(""), - py::arg("reference_frame") = DQ_VrepInterface::BODY_FRAME, - py::arg("function_name") = std::string("get_inertia"), - py::arg("obj_name") = std::string("DQRoboticsApiCommandServer"), - "Gets the inertia matrix of an object from CoppeliaSim in the requested reference frame."); - -} diff --git a/src/interfaces/vrep/DQ_VrepRobot_py.cpp b/src/interfaces/vrep/DQ_VrepRobot_py.cpp deleted file mode 100644 index 481fb8e..0000000 --- a/src/interfaces/vrep/DQ_VrepRobot_py.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/** -(C) Copyright 2019 DQ Robotics Developers - -This file is part of DQ Robotics. - - DQ Robotics is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - DQ Robotics is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with DQ Robotics. If not, see . - -Contributors: -- Murilo M. Marinho (murilomarinho@ieee.org) -*/ - -#include "../../dqrobotics_module.h" - -/** - * @brief Binds `DQ_VrepRobot`, a base robot wrapper that exposes - * configuration-space exchanges with CoppeliaSim, to the Python module @p m. - */ -void init_DQ_VrepRobot_py(py::module& m) -{ - /***************************************************** - * VrepRobot - * **************************************************/ - py::class_< - DQ_VrepRobot, - std::shared_ptr - > dqvreprobot_py( - m, - "DQ_VrepRobot", - "Base robot wrapper for reading and writing configuration-space values in CoppeliaSim."); - - dqvreprobot_py.def("set_configuration_space_positions", - &DQ_VrepRobot::set_configuration_space_positions, - py::arg("q"), - "Sets the robot configuration-space positions in CoppeliaSim."); - dqvreprobot_py.def("get_configuration_space_positions", - &DQ_VrepRobot::get_configuration_space_positions, - "Gets the robot configuration-space positions from CoppeliaSim."); - - //Deprecated - dqvreprobot_py.def("send_q_to_vrep", - &DQ_VrepRobot::send_q_to_vrep, - py::arg("q"), - "Deprecated alias for setting the robot configuration-space positions in CoppeliaSim."); - dqvreprobot_py.def("get_q_from_vrep", - &DQ_VrepRobot::get_q_from_vrep, - "Deprecated alias for getting the robot configuration-space positions from CoppeliaSim."); -} diff --git a/src/robot_modeling/DQ_DifferentialDriveRobot_py.cpp b/src/robot_modeling/DQ_DifferentialDriveRobot_py.cpp index 214ba69..1ff4690 100644 --- a/src/robot_modeling/DQ_DifferentialDriveRobot_py.cpp +++ b/src/robot_modeling/DQ_DifferentialDriveRobot_py.cpp @@ -60,15 +60,15 @@ void init_DQ_DifferentialDriveRobot_py(py::module& m) "Computes the full constrained pose Jacobian."); dqdifferentialdriverobot_py.def( "pose_jacobian_derivative", - (MatrixXd (DQ_DifferentialDriveRobot::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_DifferentialDriveRobot::pose_jacobian_derivative, + (MatrixXd (DQ_DifferentialDriveRobot::*)(const VectorXd&, const VectorXd&) const)&DQ_DifferentialDriveRobot::pose_jacobian_derivative, py::arg("q"), py::arg("q_dot"), - py::arg("to_link"), - "Computes the time derivative of the constrained pose Jacobian up to the requested column."); + "Computes the full time derivative of the constrained pose Jacobian."); dqdifferentialdriverobot_py.def( "pose_jacobian_derivative", - (MatrixXd (DQ_DifferentialDriveRobot::*)(const VectorXd&, const VectorXd&) const)&DQ_DifferentialDriveRobot::pose_jacobian_derivative, + (MatrixXd (DQ_DifferentialDriveRobot::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_DifferentialDriveRobot::pose_jacobian_derivative, py::arg("q"), py::arg("q_dot"), - "Computes the full time derivative of the constrained pose Jacobian."); + py::arg("to_link"), + "Computes the time derivative of the constrained pose Jacobian up to the requested column."); } diff --git a/src/robot_modeling/DQ_Kinematics_py.cpp b/src/robot_modeling/DQ_Kinematics_py.cpp index 50a4d7f..b72fa18 100644 --- a/src/robot_modeling/DQ_Kinematics_py.cpp +++ b/src/robot_modeling/DQ_Kinematics_py.cpp @@ -50,7 +50,7 @@ void init_DQ_Kinematics_py(py::module& m) dqkinematics_py.def( "set_reference_frame", &DQ_Kinematics::set_reference_frame, - py::arg("get_reference_frame"), + py::arg("reference_frame"), "Sets the reference frame used by the forward kinematics and Jacobian methods."); dqkinematics_py.def( "get_base_frame", @@ -59,7 +59,7 @@ void init_DQ_Kinematics_py(py::module& m) dqkinematics_py.def( "set_base_frame", &DQ_Kinematics::set_base_frame, - py::arg("get_base_frame"), + py::arg("base_frame"), "Sets the physical base frame of the robot in the workspace."); dqkinematics_py.def_static( diff --git a/src/robot_modeling/DQ_SerialManipulatorDH_py.cpp b/src/robot_modeling/DQ_SerialManipulatorDH_py.cpp index da375f0..0320514 100644 --- a/src/robot_modeling/DQ_SerialManipulatorDH_py.cpp +++ b/src/robot_modeling/DQ_SerialManipulatorDH_py.cpp @@ -62,18 +62,34 @@ void init_DQ_SerialManipulatorDH_py(py::module& m) &DQ_SerialManipulatorDH::get_types, "Returns the joint-type row of the stored DH matrix as encoded joint types."); + dqserialmanipulatordh_py.def( + "raw_pose_jacobian", + (MatrixXd (DQ_SerialManipulatorDH::*)(const VectorXd&) const)&DQ_SerialManipulatorDH::raw_pose_jacobian, + py::arg("q_vec"), + "Computes the raw pose Jacobian up to the last link."); dqserialmanipulatordh_py.def( "raw_pose_jacobian", (MatrixXd (DQ_SerialManipulatorDH::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorDH::raw_pose_jacobian, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw pose Jacobian under the standard DH convention up to the requested link."); + dqserialmanipulatordh_py.def( + "raw_fkm", + (DQ (DQ_SerialManipulatorDH::*)(const VectorXd&) const)&DQ_SerialManipulatorDH::raw_fkm, + py::arg("q_vec"), + "Computes the raw forward kinematics up to the last link."); dqserialmanipulatordh_py.def( "raw_fkm", (DQ (DQ_SerialManipulatorDH::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorDH::raw_fkm, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw forward kinematics under the standard DH convention up to the requested link."); + dqserialmanipulatordh_py.def( + "raw_pose_jacobian_derivative", + (MatrixXd (DQ_SerialManipulatorDH::*)(const VectorXd&, const VectorXd&) const)&DQ_SerialManipulatorDH::raw_pose_jacobian_derivative, + py::arg("q"), + py::arg("q_dot"), + "Computes the time derivative of the raw pose Jacobian up to the last link."); dqserialmanipulatordh_py.def( "raw_pose_jacobian_derivative", (MatrixXd (DQ_SerialManipulatorDH::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_SerialManipulatorDH::raw_pose_jacobian_derivative, diff --git a/src/robot_modeling/DQ_SerialManipulatorDenso_py.cpp b/src/robot_modeling/DQ_SerialManipulatorDenso_py.cpp index 0aaf873..9a86203 100644 --- a/src/robot_modeling/DQ_SerialManipulatorDenso_py.cpp +++ b/src/robot_modeling/DQ_SerialManipulatorDenso_py.cpp @@ -65,18 +65,34 @@ void init_DQ_SerialManipulatorDenso_py(py::module& m) &DQ_SerialManipulatorDenso::get_gammas, "Returns the gamma row of the stored DENSO matrix."); + dqserialmanipulatordh_py.def( + "raw_pose_jacobian", + (MatrixXd (DQ_SerialManipulatorDenso::*)(const VectorXd&) const)&DQ_SerialManipulatorDenso::raw_pose_jacobian, + py::arg("q_vec"), + "Computes the raw pose Jacobian up to the last link."); dqserialmanipulatordh_py.def( "raw_pose_jacobian", (MatrixXd (DQ_SerialManipulatorDenso::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorDenso::raw_pose_jacobian, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw pose Jacobian under the DENSO convention up to the requested link."); + dqserialmanipulatordh_py.def( + "raw_fkm", + (DQ (DQ_SerialManipulatorDenso::*)(const VectorXd&) const)&DQ_SerialManipulatorDenso::raw_fkm, + py::arg("q_vec"), + "Computes the raw forward kinematics up to the last link."); dqserialmanipulatordh_py.def( "raw_fkm", (DQ (DQ_SerialManipulatorDenso::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorDenso::raw_fkm, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw forward kinematics under the DENSO convention up to the requested link."); + dqserialmanipulatordh_py.def( + "raw_pose_jacobian_derivative", + (MatrixXd (DQ_SerialManipulatorDenso::*)(const VectorXd&, const VectorXd&) const)&DQ_SerialManipulatorDenso::raw_pose_jacobian_derivative, + py::arg("q"), + py::arg("q_dot"), + "Computes the time derivative of the raw pose Jacobian up to the last link."); dqserialmanipulatordh_py.def( "raw_pose_jacobian_derivative", (MatrixXd (DQ_SerialManipulatorDenso::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_SerialManipulatorDenso::raw_pose_jacobian_derivative, diff --git a/src/robot_modeling/DQ_SerialManipulatorMDH_py.cpp b/src/robot_modeling/DQ_SerialManipulatorMDH_py.cpp index dd05839..d7d8d97 100644 --- a/src/robot_modeling/DQ_SerialManipulatorMDH_py.cpp +++ b/src/robot_modeling/DQ_SerialManipulatorMDH_py.cpp @@ -63,18 +63,34 @@ void init_DQ_SerialManipulatorMDH_py(py::module& m) &DQ_SerialManipulatorMDH::get_types, "Returns the joint-type row of the stored modified DH matrix as encoded joint types."); + dqserialmanipulatormdh_py.def( + "raw_pose_jacobian", + (MatrixXd (DQ_SerialManipulatorMDH::*)(const VectorXd&) const)&DQ_SerialManipulatorMDH::raw_pose_jacobian, + py::arg("q_vec"), + "Computes the raw pose Jacobian up to the last link."); dqserialmanipulatormdh_py.def( "raw_pose_jacobian", (MatrixXd (DQ_SerialManipulatorMDH::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorMDH::raw_pose_jacobian, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw pose Jacobian under the modified DH convention up to the requested link."); + dqserialmanipulatormdh_py.def( + "raw_fkm", + (DQ (DQ_SerialManipulatorMDH::*)(const VectorXd&) const)&DQ_SerialManipulatorMDH::raw_fkm, + py::arg("q_vec"), + "Computes the raw forward kinematics up to the last link."); dqserialmanipulatormdh_py.def( "raw_fkm", (DQ (DQ_SerialManipulatorMDH::*)(const VectorXd&, const int&) const)&DQ_SerialManipulatorMDH::raw_fkm, py::arg("q_vec"), py::arg("to_ith_link"), "Computes the raw forward kinematics under the modified DH convention up to the requested link."); + dqserialmanipulatormdh_py.def( + "raw_pose_jacobian_derivative", + (MatrixXd (DQ_SerialManipulatorMDH::*)(const VectorXd&, const VectorXd&) const)&DQ_SerialManipulatorMDH::raw_pose_jacobian_derivative, + py::arg("q"), + py::arg("q_dot"), + "Computes the time derivative of the raw pose Jacobian up to the last link."); dqserialmanipulatormdh_py.def( "raw_pose_jacobian_derivative", (MatrixXd (DQ_SerialManipulatorMDH::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_SerialManipulatorMDH::raw_pose_jacobian_derivative, diff --git a/src/robot_modeling/DQ_SerialManipulator_py.cpp b/src/robot_modeling/DQ_SerialManipulator_py.cpp index da15b39..953e7d4 100644 --- a/src/robot_modeling/DQ_SerialManipulator_py.cpp +++ b/src/robot_modeling/DQ_SerialManipulator_py.cpp @@ -89,17 +89,36 @@ void init_DQ_SerialManipulator_py(py::module& m) (DQ (DQ_SerialManipulator::*)(const VectorXd&) const)&DQ_SerialManipulator::raw_fkm, py::arg("q_vec"), "Computes the raw forward kinematics up to the last link and returns the pose before applying the reference frame and the end effector."); + dqserialmanipulator_py.def( + "raw_fkm", + (DQ (DQ_SerialManipulator::*)(const VectorXd&, const int&) const)&DQ_SerialManipulator::raw_fkm, + py::arg("q_vec"), + py::arg("to_ith_link"), + "Computes the raw forward kinematics up to the requested link and returns the pose before applying the reference frame and the end effector."); dqserialmanipulator_py.def( "raw_pose_jacobian", (MatrixXd (DQ_SerialManipulator::*)(const VectorXd&) const)&DQ_SerialManipulator::raw_pose_jacobian, py::arg("q_vec"), "Computes the raw pose Jacobian up to the last link, without reference-frame or end-effector transformations."); + dqserialmanipulator_py.def( + "raw_pose_jacobian", + (MatrixXd (DQ_SerialManipulator::*)(const VectorXd&, const int&) const)&DQ_SerialManipulator::raw_pose_jacobian, + py::arg("q_vec"), + py::arg("to_ith_link"), + "Computes the raw pose Jacobian up to the requested link, without reference-frame or end-effector transformations."); dqserialmanipulator_py.def( "raw_pose_jacobian_derivative", (MatrixXd (DQ_SerialManipulator::*)(const VectorXd&, const VectorXd&) const)&DQ_SerialManipulator::raw_pose_jacobian_derivative, py::arg("q"), py::arg("q_dot"), "Computes the time derivative of the raw pose Jacobian up to the last link."); + dqserialmanipulator_py.def( + "raw_pose_jacobian_derivative", + (MatrixXd (DQ_SerialManipulator::*)(const VectorXd&, const VectorXd&, const int&) const)&DQ_SerialManipulator::raw_pose_jacobian_derivative, + py::arg("q"), + py::arg("q_dot"), + py::arg("to_ith_link"), + "Computes the time derivative of the raw pose Jacobian up to the requested link."); dqserialmanipulator_py.def( "fkm", diff --git a/src/utils/DQ_Geometry_py.cpp b/src/utils/DQ_Geometry_py.cpp index dfb312a..6bf6b18 100644 --- a/src/utils/DQ_Geometry_py.cpp +++ b/src/utils/DQ_Geometry_py.cpp @@ -97,14 +97,4 @@ void init_DQ_Geometry_py(py::module& m) py::arg("line_point_2"), py::arg("threshold") = DQ_threshold, "Checks whether a line and two endpoints define a valid line segment within the given threshold."); - //Overload with the default threshold - geometry_py.def_static("is_line_segment", - [](const DQ& line, const DQ& line_point_1, const DQ& line_point_2) - { - return DQ_Geometry::is_line_segment(line,line_point_1,line_point_2); - }, - py::arg("line"), - py::arg("line_point_1"), - py::arg("line_point_2"), - "Checks whether a line and two endpoints define a valid line segment using the library default threshold."); }