Skip to content

Commit 7dfecb7

Browse files
committed
feat: inital version with compiling bindings
1 parent 40b0e6e commit 7dfecb7

File tree

13 files changed

+1147
-0
lines changed

13 files changed

+1147
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Patch Release
2+
on:
3+
workflow_dispatch:
4+
5+
jobs:
6+
release:
7+
runs-on: ubuntu-latest
8+
permissions:
9+
contents: write
10+
name: "Bump minor version and create changelog with commitizen"
11+
steps:
12+
- name: Check out
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
token: "${{ secrets.GITHUB_TOKEN }}"
17+
- id: cz
18+
name: Create bump and changelog
19+
uses: commitizen-tools/commitizen-action@master
20+
with:
21+
github_token: ${{ secrets.GITHUB_TOKEN }}
22+
changelog_increment_filename: body.md
23+
increment: PATCH
24+
- name: Release
25+
uses: ncipollo/release-action@v1
26+
with:
27+
tag: v${{ env.REVISION }}
28+
bodyFile: "body.md"
29+
skipIfReleaseExists: true
30+
- name: Print Version
31+
run: echo "Bumped to version ${{ steps.cz.outputs.version }}"
32+
# TODO: create pypi source distribution

.github/workflows/release.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Release
2+
on:
3+
workflow_dispatch:
4+
5+
jobs:
6+
release:
7+
runs-on: ubuntu-latest
8+
permissions:
9+
contents: write
10+
name: "Bump version and create changelog with commitizen"
11+
steps:
12+
- name: Check out
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
token: "${{ secrets.GITHUB_TOKEN }}"
17+
- id: cz
18+
name: Create bump and changelog
19+
uses: commitizen-tools/commitizen-action@master
20+
with:
21+
github_token: ${{ secrets.GITHUB_TOKEN }}
22+
changelog_increment_filename: body.md
23+
- name: Release
24+
uses: ncipollo/release-action@v1
25+
with:
26+
tag: v${{ env.REVISION }}
27+
bodyFile: "body.md"
28+
skipIfReleaseExists: true
29+
- name: Print Version
30+
run: echo "Bumped to version ${{ steps.cz.outputs.version }}"
31+
# TODO: create pypi source distribution

CMakeLists.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
3+
project(
4+
frankik
5+
LANGUAGES C CXX
6+
VERSION 0.1
7+
DESCRIPTION "Blazing fast, analytical Inverse Kinematics for Franka Emika Panda and FR3 robots. Lightweight Python bindings, no ROS required."
8+
)
9+
10+
11+
cmake_policy(SET CMP0048 NEW) # Set version in project
12+
# Allow target properties affecting visibility during linking in static libraries
13+
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
14+
cmake_policy(SET CMP0135 NEW) # Use timestamp of file extraction not download
15+
cmake_policy(SET CMP0140 NEW) # Check return arguments
16+
17+
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
18+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
19+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
20+
21+
set(CMAKE_CXX_STANDARD 20)
22+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
23+
set(CMAKE_CXX_EXTENSIONS OFF)
24+
25+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
26+
27+
set(BUILD_SHARED_LIBS OFF)
28+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
29+
30+
include(FetchContent)
31+
32+
find_package(Eigen3 REQUIRED)
33+
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
34+
35+
36+
FetchContent_Declare(pybind11
37+
GIT_REPOSITORY https://github.com/pybind/pybind11.git
38+
GIT_TAG v2.13.4
39+
GIT_PROGRESS TRUE
40+
EXCLUDE_FROM_ALL
41+
)
42+
43+
FetchContent_MakeAvailable(pybind11)
44+
45+
add_subdirectory(src)

Makefile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
PYSRC = src
2+
CPPSRC = src
3+
COMPILE_MODE = Release
4+
5+
# CPP
6+
cppcheckformat:
7+
clang-format --dry-run -Werror -i $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -o -name '*.h')
8+
9+
cppformat:
10+
clang-format -Werror -i $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -o -name '*.h')
11+
12+
cpplint:
13+
clang-tidy -p=build --warnings-as-errors='*' $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -name '*.h')
14+
15+
16+
gcccompile:
17+
cmake -DCMAKE_BUILD_TYPE=${COMPILE_MODE} -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -B build -G Ninja
18+
cmake --build build --target _core
19+
20+
clangcompile:
21+
cmake -DCMAKE_BUILD_TYPE=${COMPILE_MODE} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -B build -G Ninja
22+
cmake --build build --target _core
23+
24+
# Auto generation of CPP binding stub files
25+
stubgen:
26+
pybind11-stubgen -o src --numpy-array-use-type-var frankik
27+
find ./src -name '*.pyi' -print | xargs sed -i '1s/^/# ATTENTION: auto generated from C++ code, use `make stubgen` to update!\n/'
28+
find ./src -not -path "./src/frankik/_core.pyi" -name '*.pyi' -delete
29+
find ./src/frankik/_core.pyi -name '*.pyi' -print | xargs sed -i 's/tuple\[typing\.Literal\[\([0-9]\+\)\], typing\.Literal\[1\]\]/tuple\[typing\.Literal[\1]\]/g'
30+
find ./src/frankik/_core.pyi -name '*.pyi' -print | xargs sed -i 's/tuple\[\([M|N]\), typing\.Literal\[1\]\]/tuple\[\1\]/g'
31+
ruff check --fix src/frankik/_core.pyi
32+
isort src/frankik/_core.pyi
33+
black src/frankik/_core.pyi
34+
35+
36+
# Python
37+
pycheckformat:
38+
isort --check-only ${PYSRC}
39+
black --check ${PYSRC}
40+
41+
pyformat:
42+
isort ${PYSRC}
43+
black ${PYSRC}
44+
45+
pylint: ruff mypy
46+
47+
ruff:
48+
ruff check ${PYSRC}
49+
50+
mypy:
51+
mypy ${PYSRC} --install-types --non-interactive --no-namespace-packages --exclude 'build'
52+
53+
pytest:
54+
pytest -vv
55+
56+
bump:
57+
cz bump
58+
59+
commit:
60+
cz commit
61+
62+
.PHONY: cppcheckformat cppformat cpplint gcccompile clangcompile stubgen pycheckformat pyformat pylint ruff mypy pytest bump commit
63+
64+
65+

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frankik: Fast Analytical Inverse Kinematics Python Bindings for Franka Robots
2+
3+
**Blazing fast, analytical Inverse Kinematics for Franka Emika Panda and FR3 robots. Lightweight Python bindings, no ROS required.**
4+
5+
`frankik` is a standalone Python library that implements the analytical geometric IK solver proposed by **He & Liu (2021)**. It is designed for researchers and developers who need high-performance kinematics without the overhead of ROS, MoveIt, or hardware drivers.
6+
7+
## Installation from source
8+
```shell
9+
sudo apt install libeigen3-dev
10+
git clone https://github.com/juelg/frankik.git
11+
cd frankik
12+
pip install requirements.txt
13+
pip install -ve '.[dev]' --no-build-isolation
14+
```
15+
16+
## Installation from PyPI
17+
Coming soon...
18+
19+
20+
## Feature List
21+
- [x] basic bindings from he
22+
- [x] support for fr3
23+
- [ ] nice python interface
24+
- [ ] tests
25+
- [ ] performance benchmarks
26+

pyproject.toml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
[build-system]
2+
requires = []
3+
build-backend = "scikit_build_core.build"
4+
5+
[project]
6+
name = "frankik"
7+
version = "0.1"
8+
description="Blazing fast, analytical Inverse Kinematics for Franka Emika Panda and FR3 robots. Lightweight Python bindings, no ROS required."
9+
readme = "README.md"
10+
dependencies = [
11+
"numpy",
12+
]
13+
maintainers = [
14+
{ name = "Tobias Jülg", email = "tobias@juelg.net" },
15+
]
16+
authors = [
17+
{ name = "Tobias Jülg", email = "tobias@juelg.net" },
18+
]
19+
requires-python = ">=3.10"
20+
license = "MIT"
21+
22+
[project.optional-dependencies]
23+
dev = [
24+
"ruff==0.3.2",
25+
"black==24.1.1",
26+
"isort==5.13.2",
27+
"mypy==1.10.1",
28+
"types-requests~=2.31",
29+
# temporarily use fixed version until PR #275 is merged
30+
# pybind11-stubgen==2.5.5
31+
"pybind11-stubgen @ git+https://github.com/juelg/pybind11-stubgen@fix/class-sorting",
32+
"pytest==8.1.1",
33+
"commitizen~=3.28.0",
34+
"clang",
35+
"clang-format",
36+
"clang-tidy",
37+
]
38+
39+
[tool.scikit-build]
40+
build.verbose = true
41+
build.targets = ["_core"]
42+
logging.level = "INFO"
43+
build-dir = "build"
44+
wheel.packages = ["src/frankik"]
45+
install.components = ["python_package"]
46+
47+
[tool.black]
48+
line-length = 120
49+
target-version = ["py311"]
50+
51+
[tool.isort]
52+
profile = "black"
53+
54+
[tool.mypy]
55+
ignore_missing_imports = true
56+
check_untyped_defs = true
57+
pretty = true
58+
59+
[tool.pytest.ini_options]
60+
minversion = "6.0"
61+
addopts = "-vv"
62+
testpaths = [
63+
"src/tests",
64+
]
65+
66+
[tool.commitizen]
67+
name = "cz_conventional_commits"
68+
tag_format = "v$version"
69+
version_scheme = "pep440"
70+
version_provider = "pep621"
71+
update_changelog_on_bump = true
72+
major_version_zero = true
73+
version_files = [
74+
"CMakeLists.txt:VERSION",
75+
"python/frankik/_core/__init__.pyi:__version__",
76+
]

requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
build
2+
wheel
3+
scikit-build-core>=0.3.3
4+
pybind11
5+
cmake
6+
ninja

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(pybind)

src/frankik/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from frankik._core import __version__, ik, ik_full, fk
2+
3+
__all__ = ["__version__", "ik", "ik_full", "fk"]

src/frankik/_core.pyi

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# ATTENTION: auto generated from C++ code, use `make stubgen` to update!
2+
"""
3+
Python bindings for frankik IK/FK
4+
"""
5+
from __future__ import annotations
6+
7+
import typing
8+
9+
import numpy
10+
11+
__all__: list[str] = ["fk", "ik", "ik_full"]
12+
13+
def fk(
14+
q: numpy.ndarray[tuple[typing.Literal[7]], numpy.dtype[numpy.float64]]
15+
) -> numpy.ndarray[tuple[typing.Literal[4], typing.Literal[4]], numpy.dtype[numpy.float64]]:
16+
"""
17+
Compute forward kinematics for Franka Emika Panda robot.
18+
19+
Args:
20+
q (Vector7d): Joint angles.
21+
22+
Returns:
23+
Eigen::Matrix<double, 4, 4>: End-effector pose.
24+
"""
25+
26+
def ik(
27+
O_T_EE: numpy.ndarray[tuple[typing.Literal[4], typing.Literal[4]], numpy.dtype[numpy.float64]],
28+
q_actual_array: numpy.ndarray[tuple[typing.Literal[7]], numpy.dtype[numpy.float64]] = ...,
29+
q7: float = 0.7853981633974483,
30+
is_fr3: bool = False,
31+
) -> numpy.ndarray[tuple[typing.Literal[7]], numpy.dtype[numpy.float64]]:
32+
"""
33+
Compute one inverse kinematics solution for Franka Emika Panda robot.
34+
35+
Args:
36+
O_T_EE (Eigen::Matrix<double, 4, 4>): Desired end-effector pose.
37+
q_actual_array (Vector7d, optional): Current joint angles. Defaults to kQDefault.
38+
q7 (double, optional): Joint 7 angle. Defaults to M_PI_4.
39+
is_fr3 (bool, optional): Whether to use FR3 joint limits. Defaults to false.
40+
41+
Returns:
42+
Vector7d: One IK solution. NaN if no solution.
43+
"""
44+
45+
def ik_full(
46+
O_T_EE: numpy.ndarray[tuple[typing.Literal[4], typing.Literal[4]], numpy.dtype[numpy.float64]],
47+
q_actual_array: numpy.ndarray[tuple[typing.Literal[7]], numpy.dtype[numpy.float64]] = ...,
48+
q7: float = 0.7853981633974483,
49+
is_fr3: bool = False,
50+
) -> numpy.ndarray[tuple[typing.Literal[4], typing.Literal[7]], numpy.dtype[numpy.float64]]:
51+
"""
52+
Compute full inverse kinematics for Franka Emika Panda robot.
53+
54+
Args:
55+
O_T_EE (Eigen::Matrix<double, 4, 4>): Desired end-effector pose.
56+
q_actual_array (Vector7d, optional): Current joint angles. Defaults to kQDefault.
57+
q7 (double, optional): Joint 7 angle. Defaults to M_PI_4.
58+
is_fr3 (bool, optional): Whether to use FR3 joint limits. Defaults to false.
59+
60+
Returns:
61+
Eigen::Matrix<double, 4, 7>: All possible IK solutions (up to 4). NaN if no solution.
62+
"""
63+
64+
__version__: str = "0.1"

0 commit comments

Comments
 (0)