mink 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mink/.mypy_cache/.gitignore +2 -0
- mink/.mypy_cache/3.12/@plugins_snapshot.json +1 -0
- mink/.mypy_cache/3.12/__future__.data.json +1 -0
- mink/.mypy_cache/3.12/__future__.meta.json +1 -0
- mink/.mypy_cache/3.12/_ast.data.json +1 -0
- mink/.mypy_cache/3.12/_ast.meta.json +1 -0
- mink/.mypy_cache/3.12/_codecs.data.json +1 -0
- mink/.mypy_cache/3.12/_codecs.meta.json +1 -0
- mink/.mypy_cache/3.12/_collections_abc.data.json +1 -0
- mink/.mypy_cache/3.12/_collections_abc.meta.json +1 -0
- mink/.mypy_cache/3.12/_ctypes.data.json +1 -0
- mink/.mypy_cache/3.12/_ctypes.meta.json +1 -0
- mink/.mypy_cache/3.12/_thread.data.json +1 -0
- mink/.mypy_cache/3.12/_thread.meta.json +1 -0
- mink/.mypy_cache/3.12/_typeshed/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/_typeshed/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/_warnings.data.json +1 -0
- mink/.mypy_cache/3.12/_warnings.meta.json +1 -0
- mink/.mypy_cache/3.12/abc.data.json +1 -0
- mink/.mypy_cache/3.12/abc.meta.json +1 -0
- mink/.mypy_cache/3.12/array.data.json +1 -0
- mink/.mypy_cache/3.12/array.meta.json +1 -0
- mink/.mypy_cache/3.12/ast.data.json +1 -0
- mink/.mypy_cache/3.12/ast.meta.json +1 -0
- mink/.mypy_cache/3.12/builtins.data.json +1 -0
- mink/.mypy_cache/3.12/builtins.meta.json +1 -0
- mink/.mypy_cache/3.12/codecs.data.json +1 -0
- mink/.mypy_cache/3.12/codecs.meta.json +1 -0
- mink/.mypy_cache/3.12/collections/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/collections/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/collections/abc.data.json +1 -0
- mink/.mypy_cache/3.12/collections/abc.meta.json +1 -0
- mink/.mypy_cache/3.12/contextlib.data.json +1 -0
- mink/.mypy_cache/3.12/contextlib.meta.json +1 -0
- mink/.mypy_cache/3.12/ctypes/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/ctypes/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/dataclasses.data.json +1 -0
- mink/.mypy_cache/3.12/dataclasses.meta.json +1 -0
- mink/.mypy_cache/3.12/datetime.data.json +1 -0
- mink/.mypy_cache/3.12/datetime.meta.json +1 -0
- mink/.mypy_cache/3.12/email/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/email/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/email/charset.data.json +1 -0
- mink/.mypy_cache/3.12/email/charset.meta.json +1 -0
- mink/.mypy_cache/3.12/email/contentmanager.data.json +1 -0
- mink/.mypy_cache/3.12/email/contentmanager.meta.json +1 -0
- mink/.mypy_cache/3.12/email/errors.data.json +1 -0
- mink/.mypy_cache/3.12/email/errors.meta.json +1 -0
- mink/.mypy_cache/3.12/email/header.data.json +1 -0
- mink/.mypy_cache/3.12/email/header.meta.json +1 -0
- mink/.mypy_cache/3.12/email/message.data.json +1 -0
- mink/.mypy_cache/3.12/email/message.meta.json +1 -0
- mink/.mypy_cache/3.12/email/policy.data.json +1 -0
- mink/.mypy_cache/3.12/email/policy.meta.json +1 -0
- mink/.mypy_cache/3.12/enum.data.json +1 -0
- mink/.mypy_cache/3.12/enum.meta.json +1 -0
- mink/.mypy_cache/3.12/genericpath.data.json +1 -0
- mink/.mypy_cache/3.12/genericpath.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/abc.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/abc.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/machinery.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/machinery.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/metadata/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/metadata/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/metadata/_meta.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/metadata/_meta.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/readers.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/readers.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/resources/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/resources/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/importlib/resources/abc.data.json +1 -0
- mink/.mypy_cache/3.12/importlib/resources/abc.meta.json +1 -0
- mink/.mypy_cache/3.12/io.data.json +1 -0
- mink/.mypy_cache/3.12/io.meta.json +1 -0
- mink/.mypy_cache/3.12/logging/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/logging/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/math.data.json +1 -0
- mink/.mypy_cache/3.12/math.meta.json +1 -0
- mink/.mypy_cache/3.12/mink/limits/limit.data.json +1 -0
- mink/.mypy_cache/3.12/mink/limits/limit.meta.json +1 -0
- mink/.mypy_cache/3.12/mmap.data.json +1 -0
- mink/.mypy_cache/3.12/mmap.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_pytesttester.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_pytesttester.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_add_docstring.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_add_docstring.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_array_like.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_array_like.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_callable.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_callable.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_char_codes.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_char_codes.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_dtype_like.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_dtype_like.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_extended_precision.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_extended_precision.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_nbit.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_nbit.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_nested_sequence.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_nested_sequence.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_scalars.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_scalars.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_shape.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_shape.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_ufunc.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_typing/_ufunc.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_utils/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_utils/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/_utils/_convertions.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/_utils/_convertions.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_asarray.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_asarray.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_internal.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_internal.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_type_aliases.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_type_aliases.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_ufunc_config.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/_ufunc_config.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/arrayprint.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/arrayprint.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/defchararray.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/defchararray.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/einsumfunc.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/einsumfunc.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/fromnumeric.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/fromnumeric.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/function_base.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/function_base.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/multiarray.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/multiarray.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/numeric.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/numeric.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/numerictypes.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/numerictypes.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/records.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/records.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/shape_base.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/shape_base.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/umath.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/core/umath.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/ctypeslib.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/ctypeslib.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/dtypes.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/dtypes.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/exceptions.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/exceptions.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/_pocketfft.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/_pocketfft.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/helper.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/fft/helper.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/_version.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/_version.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arraypad.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arraypad.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arraysetops.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arraysetops.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arrayterator.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/arrayterator.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/format.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/format.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/function_base.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/function_base.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/histograms.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/histograms.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/index_tricks.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/index_tricks.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/mixins.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/mixins.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/nanfunctions.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/nanfunctions.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/npyio.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/npyio.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/polynomial.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/polynomial.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/scimath.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/scimath.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/shape_base.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/shape_base.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/stride_tricks.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/stride_tricks.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/twodim_base.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/twodim_base.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/type_check.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/type_check.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/ufunclike.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/ufunclike.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/utils.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/lib/utils.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/linalg/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/linalg/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/linalg/linalg.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/linalg/linalg.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/core.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/core.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/extras.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/extras.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/mrecords.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/ma/mrecords.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/matrixlib/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/matrixlib/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/matrixlib/defmatrix.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/matrixlib/defmatrix.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/_polybase.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/_polybase.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/chebyshev.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/chebyshev.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/hermite.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/hermite.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/hermite_e.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/hermite_e.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/laguerre.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/laguerre.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/legendre.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/legendre.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/polynomial.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/polynomial.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/polyutils.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/polynomial/polyutils.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_generator.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_generator.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_mt19937.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_mt19937.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_pcg64.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_pcg64.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_philox.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_philox.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_sfc64.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/_sfc64.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/bit_generator.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/bit_generator.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/mtrand.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/random/mtrand.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/_private/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/_private/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/_private/utils.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/testing/_private/utils.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/typing/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/typing/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/numpy/version.data.json +1 -0
- mink/.mypy_cache/3.12/numpy/version.meta.json +1 -0
- mink/.mypy_cache/3.12/os/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/os/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/os/path.data.json +1 -0
- mink/.mypy_cache/3.12/os/path.meta.json +1 -0
- mink/.mypy_cache/3.12/pathlib.data.json +1 -0
- mink/.mypy_cache/3.12/pathlib.meta.json +1 -0
- mink/.mypy_cache/3.12/posixpath.data.json +1 -0
- mink/.mypy_cache/3.12/posixpath.meta.json +1 -0
- mink/.mypy_cache/3.12/re.data.json +1 -0
- mink/.mypy_cache/3.12/re.meta.json +1 -0
- mink/.mypy_cache/3.12/sre_compile.data.json +1 -0
- mink/.mypy_cache/3.12/sre_compile.meta.json +1 -0
- mink/.mypy_cache/3.12/sre_constants.data.json +1 -0
- mink/.mypy_cache/3.12/sre_constants.meta.json +1 -0
- mink/.mypy_cache/3.12/sre_parse.data.json +1 -0
- mink/.mypy_cache/3.12/sre_parse.meta.json +1 -0
- mink/.mypy_cache/3.12/string.data.json +1 -0
- mink/.mypy_cache/3.12/string.meta.json +1 -0
- mink/.mypy_cache/3.12/subprocess.data.json +1 -0
- mink/.mypy_cache/3.12/subprocess.meta.json +1 -0
- mink/.mypy_cache/3.12/sys/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/sys/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/sys/_monitoring.data.json +1 -0
- mink/.mypy_cache/3.12/sys/_monitoring.meta.json +1 -0
- mink/.mypy_cache/3.12/textwrap.data.json +1 -0
- mink/.mypy_cache/3.12/textwrap.meta.json +1 -0
- mink/.mypy_cache/3.12/threading.data.json +1 -0
- mink/.mypy_cache/3.12/threading.meta.json +1 -0
- mink/.mypy_cache/3.12/time.data.json +1 -0
- mink/.mypy_cache/3.12/time.meta.json +1 -0
- mink/.mypy_cache/3.12/types.data.json +1 -0
- mink/.mypy_cache/3.12/types.meta.json +1 -0
- mink/.mypy_cache/3.12/typing.data.json +1 -0
- mink/.mypy_cache/3.12/typing.meta.json +1 -0
- mink/.mypy_cache/3.12/typing_extensions.data.json +1 -0
- mink/.mypy_cache/3.12/typing_extensions.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/__init__.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/__init__.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/_log.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/_log.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/async_case.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/async_case.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/case.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/case.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/loader.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/loader.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/main.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/main.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/result.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/result.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/runner.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/runner.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/signals.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/signals.meta.json +1 -0
- mink/.mypy_cache/3.12/unittest/suite.data.json +1 -0
- mink/.mypy_cache/3.12/unittest/suite.meta.json +1 -0
- mink/.mypy_cache/3.12/warnings.data.json +1 -0
- mink/.mypy_cache/3.12/warnings.meta.json +1 -0
- mink/.mypy_cache/3.12/zipfile.data.json +1 -0
- mink/.mypy_cache/3.12/zipfile.meta.json +1 -0
- mink/.mypy_cache/CACHEDIR.TAG +3 -0
- mink/__init__.py +75 -0
- mink/configuration.py +215 -0
- mink/constants.py +34 -0
- mink/exceptions.py +98 -0
- mink/lie/__init__.py +13 -0
- mink/lie/base.py +156 -0
- mink/lie/se3.py +249 -0
- mink/lie/so3.py +224 -0
- mink/lie/tests/__init__.py +0 -0
- mink/lie/tests/test_axioms.py +51 -0
- mink/lie/tests/test_operations.py +77 -0
- mink/lie/tests/utils.py +20 -0
- mink/lie/utils.py +29 -0
- mink/limits/__init__.py +16 -0
- mink/limits/collision_avoidance_limit.py +274 -0
- mink/limits/configuration_limit.py +126 -0
- mink/limits/exceptions.py +7 -0
- mink/limits/limit.py +41 -0
- mink/limits/velocity_limit.py +92 -0
- mink/py.typed +0 -0
- mink/solve_ik.py +105 -0
- mink/tasks/__init__.py +26 -0
- mink/tasks/com_task.py +82 -0
- mink/tasks/exceptions.py +27 -0
- mink/tasks/frame_task.py +114 -0
- mink/tasks/posture_task.py +103 -0
- mink/tasks/task.py +105 -0
- mink/utils.py +128 -0
- mink-0.0.1.dist-info/METADATA +71 -0
- mink-0.0.1.dist-info/RECORD +352 -0
- mink-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,274 @@
|
|
1
|
+
"""Collision avoidance limit.
|
2
|
+
|
3
|
+
Derivation
|
4
|
+
==========
|
5
|
+
|
6
|
+
p1, p2: closest points between g1 and g2
|
7
|
+
d: distance between g1 and g2 (d = ||p1 - p2||)
|
8
|
+
n: normal vector from g1 to g2 (n = (p2 - p1) / ||p2 - p1||)
|
9
|
+
|
10
|
+
The relative velocity constraint between g1 and g2 is given by:
|
11
|
+
|
12
|
+
n^T [J2(p2) - J1(p1)] dq <= -k * (d - d_min) / dt
|
13
|
+
V_n <= -k * (d - d_min)
|
14
|
+
|
15
|
+
where [J2(p2) - J1(p1)] dq denotes the relative velocity between g1 and g2, and
|
16
|
+
n^T [J2(p2) - J1(p1)] dq projects this velocity onto the direction connecting the
|
17
|
+
closest points p1 and p2.
|
18
|
+
|
19
|
+
We have three cases for the distance d:
|
20
|
+
|
21
|
+
1. V_n <= R_b if dist_c < dist_m
|
22
|
+
2. V_n <= -k * (dist_c - dist_m) / dt + R_b if dist_m <= d <= dist_d
|
23
|
+
3. V_n <= inf otherwise
|
24
|
+
|
25
|
+
where:
|
26
|
+
- dist_c: current normal distance between g1 and g2
|
27
|
+
- dist_m: minimum allowed distance between g1 and g2
|
28
|
+
- dist_d: collision detection distance
|
29
|
+
"""
|
30
|
+
|
31
|
+
import itertools
|
32
|
+
from dataclasses import dataclass
|
33
|
+
from typing import List, Sequence, Union
|
34
|
+
|
35
|
+
import mujoco
|
36
|
+
import numpy as np
|
37
|
+
|
38
|
+
from ..configuration import Configuration
|
39
|
+
from .limit import Constraint, Limit
|
40
|
+
|
41
|
+
# Type aliases.
|
42
|
+
Geom = Union[int, str]
|
43
|
+
GeomSequence = Sequence[Geom]
|
44
|
+
CollisionPair = tuple[GeomSequence, GeomSequence]
|
45
|
+
CollisionPairs = Sequence[CollisionPair]
|
46
|
+
|
47
|
+
|
48
|
+
@dataclass(frozen=True)
|
49
|
+
class Contact:
|
50
|
+
dist: float
|
51
|
+
fromto: np.ndarray
|
52
|
+
geom1: int
|
53
|
+
geom2: int
|
54
|
+
distmax: float
|
55
|
+
|
56
|
+
@property
|
57
|
+
def normal(self) -> np.ndarray:
|
58
|
+
normal = self.fromto[3:] - self.fromto[:3]
|
59
|
+
return normal / (np.linalg.norm(normal) + 1e-9)
|
60
|
+
|
61
|
+
@property
|
62
|
+
def inactive(self) -> bool:
|
63
|
+
return self.dist == self.distmax and not self.fromto.any()
|
64
|
+
|
65
|
+
|
66
|
+
def _is_welded_together(model: mujoco.MjModel, geom_id1: int, geom_id2: int) -> bool:
|
67
|
+
"""Returns true if the geoms are part of the same body, or if their bodies are
|
68
|
+
welded together."""
|
69
|
+
body1 = model.geom_bodyid[geom_id1]
|
70
|
+
body2 = model.geom_bodyid[geom_id2]
|
71
|
+
weld1 = model.body_weldid[body1]
|
72
|
+
weld2 = model.body_weldid[body2]
|
73
|
+
return weld1 == weld2
|
74
|
+
|
75
|
+
|
76
|
+
def _are_geom_bodies_parent_child(
|
77
|
+
model: mujoco.MjModel, geom_id1: int, geom_id2: int
|
78
|
+
) -> bool:
|
79
|
+
"""Returns true if the geom bodies have a parent-child relationship."""
|
80
|
+
body_id1 = model.geom_bodyid[geom_id1]
|
81
|
+
body_id2 = model.geom_bodyid[geom_id2]
|
82
|
+
|
83
|
+
# body_weldid is the ID of the body's weld.
|
84
|
+
body_weldid1 = model.body_weldid[body_id1]
|
85
|
+
body_weldid2 = model.body_weldid[body_id2]
|
86
|
+
|
87
|
+
# weld_parent_id is the ID of the parent of the body's weld.
|
88
|
+
weld_parent_id1 = model.body_parentid[body_weldid1]
|
89
|
+
weld_parent_id2 = model.body_parentid[body_weldid2]
|
90
|
+
|
91
|
+
# weld_parent_weldid is the weld ID of the parent of the body's weld.
|
92
|
+
weld_parent_weldid1 = model.body_weldid[weld_parent_id1]
|
93
|
+
weld_parent_weldid2 = model.body_weldid[weld_parent_id2]
|
94
|
+
|
95
|
+
cond1 = body_weldid1 == weld_parent_weldid2
|
96
|
+
cond2 = body_weldid2 == weld_parent_weldid1
|
97
|
+
return cond1 or cond2
|
98
|
+
|
99
|
+
|
100
|
+
def _is_pass_contype_conaffinity_check(
|
101
|
+
model: mujoco.MjModel, geom_id1: int, geom_id2: int
|
102
|
+
) -> bool:
|
103
|
+
"""Returns true if the geoms pass the contype/conaffinity check."""
|
104
|
+
cond1 = bool(model.geom_contype[geom_id1] & model.geom_conaffinity[geom_id2])
|
105
|
+
cond2 = bool(model.geom_contype[geom_id2] & model.geom_conaffinity[geom_id1])
|
106
|
+
return cond1 or cond2
|
107
|
+
|
108
|
+
|
109
|
+
class CollisionAvoidanceLimit(Limit):
|
110
|
+
"""Normal velocity limit between geom pairs."""
|
111
|
+
|
112
|
+
def __init__(
|
113
|
+
self,
|
114
|
+
model: mujoco.MjModel,
|
115
|
+
geom_pairs: CollisionPairs,
|
116
|
+
gain: float = 0.85,
|
117
|
+
minimum_distance_from_collisions: float = 0.005,
|
118
|
+
collision_detection_distance: float = 0.01,
|
119
|
+
bound_relaxation: float = 0.0,
|
120
|
+
):
|
121
|
+
"""Initialize collision avoidance limit.
|
122
|
+
|
123
|
+
Args:
|
124
|
+
model: MuJoCo model.
|
125
|
+
geom_pairs: Set of collision pairs in which to perform active collision
|
126
|
+
avoidance. A collision pair is defined as a pair of geom groups. A geom
|
127
|
+
group is a set of geom names. For each collision pair, the mapper will
|
128
|
+
attempt to compute joint velocities that avoid collisions between every
|
129
|
+
geom in the first geom group with every geom in the second geom group.
|
130
|
+
Self collision is achieved by adding a collision pair with the same
|
131
|
+
geom group in both pair fields.
|
132
|
+
gain: Gain factor in (0, 1] that determines how fast the geoms are
|
133
|
+
allowed to move towards each other at each iteration. Smaller values
|
134
|
+
are safer but may make the geoms move slower towards each other.
|
135
|
+
minimum_distance_from_collisions: The minimum distance to leave between
|
136
|
+
any two geoms. A negative distance allows the geoms to penetrate by
|
137
|
+
the specified amount.
|
138
|
+
collision_detection_distance: The distance between two geoms at which the
|
139
|
+
active collision avoidance limit will be active. A large value will
|
140
|
+
cause collisions to be detected early, but may incur high computational
|
141
|
+
cost. A negative value will cause the geoms to be detected only after
|
142
|
+
they penetrate by the specified amount.
|
143
|
+
bound_relaxation: An offset on the upper bound of each collision avoidance
|
144
|
+
constraint to tighten or relax
|
145
|
+
"""
|
146
|
+
self.model = model
|
147
|
+
self.gain = gain
|
148
|
+
self.minimum_distance_from_collisions = minimum_distance_from_collisions
|
149
|
+
self.collision_detection_distance = collision_detection_distance
|
150
|
+
self.bound_relaxation = bound_relaxation
|
151
|
+
self.geom_id_pairs = self._construct_geom_id_pairs(geom_pairs)
|
152
|
+
self.max_num_contacts = len(self.geom_id_pairs)
|
153
|
+
|
154
|
+
def compute_qp_inequalities(
|
155
|
+
self,
|
156
|
+
configuration: Configuration,
|
157
|
+
dt: float,
|
158
|
+
) -> Constraint:
|
159
|
+
upper_bound = np.full((self.max_num_contacts,), np.inf)
|
160
|
+
coefficient_matrix = np.zeros((self.max_num_contacts, self.model.nv))
|
161
|
+
for idx, (geom1_id, geom2_id) in enumerate(self.geom_id_pairs):
|
162
|
+
contact = self._compute_contact_with_minimum_distance(
|
163
|
+
configuration.data, geom1_id, geom2_id
|
164
|
+
)
|
165
|
+
if contact.inactive:
|
166
|
+
continue
|
167
|
+
hi_bound_dist = contact.dist
|
168
|
+
if hi_bound_dist > self.minimum_distance_from_collisions:
|
169
|
+
dist = hi_bound_dist - self.minimum_distance_from_collisions
|
170
|
+
upper_bound[idx] = (self.gain * dist / dt) + self.bound_relaxation
|
171
|
+
else:
|
172
|
+
upper_bound[idx] = self.bound_relaxation
|
173
|
+
jac = self._compute_contact_normal_jacobian(configuration.data, contact)
|
174
|
+
coefficient_matrix[idx] = -jac
|
175
|
+
return Constraint(G=coefficient_matrix, h=upper_bound)
|
176
|
+
|
177
|
+
# Private methods.
|
178
|
+
|
179
|
+
def _compute_contact_with_minimum_distance(
|
180
|
+
self, data: mujoco.MjData, geom1_id: int, geom2_id: int
|
181
|
+
) -> Contact:
|
182
|
+
"""Returns the smallest signed distance between a geom pair."""
|
183
|
+
fromto = np.empty(6)
|
184
|
+
dist = mujoco.mj_geomDistance(
|
185
|
+
self.model,
|
186
|
+
data,
|
187
|
+
geom1_id,
|
188
|
+
geom2_id,
|
189
|
+
self.collision_detection_distance,
|
190
|
+
fromto,
|
191
|
+
)
|
192
|
+
return Contact(
|
193
|
+
dist, fromto, geom1_id, geom2_id, self.collision_detection_distance
|
194
|
+
)
|
195
|
+
|
196
|
+
def _compute_contact_normal_jacobian(
|
197
|
+
self, data: mujoco.MjData, contact: Contact
|
198
|
+
) -> np.ndarray:
|
199
|
+
"""Computes the Jacobian mapping joint velocities to the normal component of
|
200
|
+
the relative Cartesian linear velocity between the geom pair.
|
201
|
+
|
202
|
+
The Jacobian-velocity relationship is given as:
|
203
|
+
|
204
|
+
J dq = n^T (v_2 - v_1)
|
205
|
+
|
206
|
+
where:
|
207
|
+
* J is the computed Jacobian.
|
208
|
+
* dq is the joint velocity vector.
|
209
|
+
* n^T is the transpose of the normal pointing from contact.geom1 to
|
210
|
+
contact.geom2.
|
211
|
+
* v_1, v_2 are the linear components of the Cartesian velocity of the two
|
212
|
+
closest points in contact.geom1 and contact.geom2.
|
213
|
+
|
214
|
+
Note: n^T (v_2 - v_1) is a scalar that is positive if the geoms are moving away
|
215
|
+
from each other, and negative if they are moving towards each other.
|
216
|
+
"""
|
217
|
+
geom1_body = self.model.geom_bodyid[contact.geom1]
|
218
|
+
geom2_body = self.model.geom_bodyid[contact.geom2]
|
219
|
+
geom1_contact_pos = contact.fromto[:3]
|
220
|
+
geom2_contact_pos = contact.fromto[3:]
|
221
|
+
jac2 = np.empty((3, self.model.nv))
|
222
|
+
mujoco.mj_jac(self.model, data, jac2, None, geom2_contact_pos, geom2_body)
|
223
|
+
jac1 = np.empty((3, self.model.nv))
|
224
|
+
mujoco.mj_jac(self.model, data, jac1, None, geom1_contact_pos, geom1_body)
|
225
|
+
return contact.normal @ (jac2 - jac1)
|
226
|
+
|
227
|
+
def _homogenize_geom_id_list(self, geom_list: GeomSequence) -> List[int]:
|
228
|
+
"""Take a heterogeneous list of geoms (specified via ID or name) and return
|
229
|
+
a homogenous list of IDs (int)."""
|
230
|
+
list_of_int: list[int] = []
|
231
|
+
for g in geom_list:
|
232
|
+
if isinstance(g, int):
|
233
|
+
list_of_int.append(g)
|
234
|
+
else:
|
235
|
+
assert isinstance(g, str)
|
236
|
+
list_of_int.append(self.model.geom(g).id)
|
237
|
+
return list_of_int
|
238
|
+
|
239
|
+
def _collision_pairs_to_geom_id_pairs(self, collision_pairs: CollisionPairs):
|
240
|
+
geom_id_pairs = []
|
241
|
+
for collision_pair in collision_pairs:
|
242
|
+
id_pair_A = self._homogenize_geom_id_list(collision_pair[0])
|
243
|
+
id_pair_B = self._homogenize_geom_id_list(collision_pair[1])
|
244
|
+
id_pair_A = list(set(id_pair_A))
|
245
|
+
id_pair_B = list(set(id_pair_B))
|
246
|
+
geom_id_pairs.append((id_pair_A, id_pair_B))
|
247
|
+
return geom_id_pairs
|
248
|
+
|
249
|
+
def _construct_geom_id_pairs(self, geom_pairs):
|
250
|
+
"""Returns a set of geom ID pairs for all possible geom-geom collisions.
|
251
|
+
|
252
|
+
The contacts are added based on the following heuristics:
|
253
|
+
1) Geoms that are part of the same body or weld are not included.
|
254
|
+
2) Geoms where the body of one geom is a parent of the body of the other
|
255
|
+
geom are not included.
|
256
|
+
3) Geoms that fail the contype-conaffinity check are ignored.
|
257
|
+
|
258
|
+
Note:
|
259
|
+
1) If two bodies are kinematically welded together (no joints between them)
|
260
|
+
they are considered to be the same body within this function.
|
261
|
+
"""
|
262
|
+
geom_id_pairs = []
|
263
|
+
for id_pair in self._collision_pairs_to_geom_id_pairs(geom_pairs):
|
264
|
+
for geom_a, geom_b in itertools.product(*id_pair):
|
265
|
+
weld_body_cond = not _is_welded_together(self.model, geom_a, geom_b)
|
266
|
+
parent_child_cond = not _are_geom_bodies_parent_child(
|
267
|
+
self.model, geom_a, geom_b
|
268
|
+
)
|
269
|
+
contype_conaffinity_cond = _is_pass_contype_conaffinity_check(
|
270
|
+
self.model, geom_a, geom_b
|
271
|
+
)
|
272
|
+
if weld_body_cond and parent_child_cond and contype_conaffinity_cond:
|
273
|
+
geom_id_pairs.append((min(geom_a, geom_b), max(geom_a, geom_b)))
|
274
|
+
return geom_id_pairs
|
@@ -0,0 +1,126 @@
|
|
1
|
+
"""Limit on joint positions.
|
2
|
+
|
3
|
+
Derivation
|
4
|
+
==========
|
5
|
+
|
6
|
+
Using a first order Taylor expansion on the configuration, we can write the limit as:
|
7
|
+
|
8
|
+
q_min <= q + v * dt <= q_max
|
9
|
+
q_min <= q + dq <= q_max
|
10
|
+
q_min - q <= dq <= q_max - q
|
11
|
+
|
12
|
+
Rewriting as G dq <= h:
|
13
|
+
|
14
|
+
+I * dq <= q_max - q
|
15
|
+
-I * dq <= q - q_min
|
16
|
+
|
17
|
+
Stacking them together, we get:
|
18
|
+
|
19
|
+
G = [+I, -I]
|
20
|
+
h = [q_max - q, q - q_min]
|
21
|
+
"""
|
22
|
+
|
23
|
+
import mujoco
|
24
|
+
import numpy as np
|
25
|
+
|
26
|
+
from ..configuration import Configuration
|
27
|
+
from ..constants import qpos_width
|
28
|
+
from .exceptions import LimitDefinitionError
|
29
|
+
from .limit import Constraint, Limit
|
30
|
+
|
31
|
+
|
32
|
+
class ConfigurationLimit(Limit):
|
33
|
+
"""Limit for joint positions in a model.
|
34
|
+
|
35
|
+
Floating base joints (joint type="free") are ignored.
|
36
|
+
|
37
|
+
Attributes:
|
38
|
+
indices: Tangent indices corresponding to configuration-limited joints.
|
39
|
+
projection_matrix: Projection from tangent space to subspace with
|
40
|
+
configuration-limited joints.
|
41
|
+
lower: Lower configuration limit.
|
42
|
+
upper: Upper configuration limit.
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
model: mujoco.MjModel,
|
48
|
+
gain: float = 0.95,
|
49
|
+
min_distance_from_limits: float = 0.0,
|
50
|
+
):
|
51
|
+
"""Initialize configuration limits.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
model: MuJoCo model.
|
55
|
+
gain: Gain factor in (0, 1] that determines how fast each joint is
|
56
|
+
allowed to move towards the joint limits at each timestep. Values lower
|
57
|
+
ttan 1 are safer but may make the joints move slowly.
|
58
|
+
min_distance_from_limits: Offset in meters (slide joints) or radians
|
59
|
+
(hinge joints) to be added to the limits. Positive values decrease the
|
60
|
+
range of motion, negative values increase it (i.e. negative values
|
61
|
+
allow penetration).
|
62
|
+
"""
|
63
|
+
if not 0.0 < gain <= 1.0:
|
64
|
+
raise LimitDefinitionError(
|
65
|
+
f"{self.__class__.__name__} gain must be in the range (0, 1]"
|
66
|
+
)
|
67
|
+
|
68
|
+
index_list: list[int] = []
|
69
|
+
lower = np.full(model.nq, -np.inf)
|
70
|
+
upper = np.full(model.nq, np.inf)
|
71
|
+
for jnt in range(model.njnt):
|
72
|
+
jnt_type = model.jnt_type[jnt]
|
73
|
+
qpos_dim = qpos_width(jnt_type)
|
74
|
+
jnt_range = model.jnt_range[jnt]
|
75
|
+
padr = model.jnt_qposadr[jnt]
|
76
|
+
if jnt_type == mujoco.mjtJoint.mjJNT_FREE or not model.jnt_limited[jnt]:
|
77
|
+
continue
|
78
|
+
lower[padr : padr + qpos_dim] = jnt_range[0] + min_distance_from_limits
|
79
|
+
upper[padr : padr + qpos_dim] = jnt_range[1] - min_distance_from_limits
|
80
|
+
index_list.append(jnt)
|
81
|
+
|
82
|
+
self.indices = np.array(index_list)
|
83
|
+
self.indices.setflags(write=False)
|
84
|
+
|
85
|
+
dim = len(self.indices)
|
86
|
+
self.projection_matrix = np.eye(model.nv)[self.indices] if dim > 0 else None
|
87
|
+
|
88
|
+
self.lower = lower
|
89
|
+
self.upper = upper
|
90
|
+
self.model = model
|
91
|
+
self.gain = gain
|
92
|
+
|
93
|
+
def compute_qp_inequalities(
|
94
|
+
self,
|
95
|
+
configuration: Configuration,
|
96
|
+
dt: float,
|
97
|
+
) -> Constraint:
|
98
|
+
del dt # Unused.
|
99
|
+
|
100
|
+
# Upper.
|
101
|
+
delta_q_max = np.zeros(self.model.nv)
|
102
|
+
mujoco.mj_differentiatePos(
|
103
|
+
m=self.model,
|
104
|
+
qvel=delta_q_max,
|
105
|
+
dt=1.0,
|
106
|
+
qpos1=configuration.q,
|
107
|
+
qpos2=self.upper,
|
108
|
+
)
|
109
|
+
|
110
|
+
# Lower.
|
111
|
+
delta_q_min = np.zeros(self.model.nv)
|
112
|
+
mujoco.mj_differentiatePos(
|
113
|
+
m=self.model,
|
114
|
+
qvel=delta_q_min,
|
115
|
+
dt=1.0,
|
116
|
+
# NOTE: mujoco.mj_differentiatePos does `qpos2 - qpos1` so notice the order
|
117
|
+
# swap here compared to above.
|
118
|
+
qpos1=self.lower,
|
119
|
+
qpos2=configuration.q,
|
120
|
+
)
|
121
|
+
|
122
|
+
p_min = self.gain * delta_q_min[self.indices]
|
123
|
+
p_max = self.gain * delta_q_max[self.indices]
|
124
|
+
G = np.vstack([self.projection_matrix, -self.projection_matrix])
|
125
|
+
h = np.hstack([p_max, p_min])
|
126
|
+
return Constraint(G=G, h=h)
|
mink/limits/limit.py
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
import abc
|
2
|
+
from typing import NamedTuple, Optional
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
from ..configuration import Configuration
|
7
|
+
|
8
|
+
|
9
|
+
class Constraint(NamedTuple):
|
10
|
+
"""Linear inequalities in the form G(q) dq <= h(q).
|
11
|
+
|
12
|
+
The limit is considered inactive when G or h are None.
|
13
|
+
"""
|
14
|
+
|
15
|
+
G: Optional[np.ndarray] = None # (nv, nv)
|
16
|
+
h: Optional[np.ndarray] = None # (nv,)
|
17
|
+
|
18
|
+
@property
|
19
|
+
def inactive(self) -> bool:
|
20
|
+
return self.G is None or self.h is None
|
21
|
+
|
22
|
+
|
23
|
+
class Limit(abc.ABC):
|
24
|
+
"""Abstract base class for kinematic limits."""
|
25
|
+
|
26
|
+
@abc.abstractmethod
|
27
|
+
def compute_qp_inequalities(
|
28
|
+
self,
|
29
|
+
configuration: Configuration,
|
30
|
+
dt: float,
|
31
|
+
) -> Constraint:
|
32
|
+
"""Compute limit as linearized QP inequalities.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
configuration: Current configuration.
|
36
|
+
dt: Integration time step in [s].
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
Pair (G, h) representing the inequality constraint.
|
40
|
+
"""
|
41
|
+
raise NotImplementedError
|
@@ -0,0 +1,92 @@
|
|
1
|
+
"""Limit on joint velocities.
|
2
|
+
|
3
|
+
Derivation
|
4
|
+
==========
|
5
|
+
|
6
|
+
Given maximum joint velocity magnitudes v_max, we can express joint velocity limits as:
|
7
|
+
|
8
|
+
-v_max <= v <= v_max
|
9
|
+
-v_max <= dq / dt <= v_max
|
10
|
+
-v_max * dt <= dq <= v_max * dt
|
11
|
+
|
12
|
+
Rewriting as G dq <= h:
|
13
|
+
|
14
|
+
+I * dq <= v_max * dt
|
15
|
+
-I * dq <= v_max * dt
|
16
|
+
|
17
|
+
Stacking them together, we get:
|
18
|
+
|
19
|
+
G = [+I, -I]
|
20
|
+
h = [v_max * dt, v_max * dt]
|
21
|
+
"""
|
22
|
+
|
23
|
+
from typing import Mapping
|
24
|
+
|
25
|
+
import mujoco
|
26
|
+
import numpy as np
|
27
|
+
import numpy.typing as npt
|
28
|
+
|
29
|
+
from ..configuration import Configuration
|
30
|
+
from ..constants import dof_width
|
31
|
+
from .exceptions import LimitDefinitionError
|
32
|
+
from .limit import Constraint, Limit
|
33
|
+
|
34
|
+
|
35
|
+
class VelocityLimit(Limit):
|
36
|
+
"""Limit for joint velocities in a model.
|
37
|
+
|
38
|
+
Floating base joints are ignored.
|
39
|
+
|
40
|
+
Attributes:
|
41
|
+
indices: Tangent indices corresponding to velocity-limited joints.
|
42
|
+
limit: Maximum allowed velocity magnitude for velocity-limited joints.
|
43
|
+
projection_matrix: Projection from tangent space to subspace with
|
44
|
+
velocity-limited joints.
|
45
|
+
"""
|
46
|
+
|
47
|
+
def __init__(
|
48
|
+
self,
|
49
|
+
model: mujoco.MjModel,
|
50
|
+
velocities: Mapping[str, npt.ArrayLike] = {},
|
51
|
+
):
|
52
|
+
"""Initialize velocity limits.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
velocities: Dictionary mapping joint name to maximum allowed magnitude in
|
56
|
+
[m]/[s] for slide joints and [rad]/[s] for hinge joints.
|
57
|
+
"""
|
58
|
+
limit_list: list[float] = []
|
59
|
+
index_list: list[int] = []
|
60
|
+
for joint_name, max_vel in velocities.items():
|
61
|
+
jid = model.joint(joint_name).id
|
62
|
+
jnt_type = model.jnt_type[jid]
|
63
|
+
jnt_dim = dof_width(jnt_type)
|
64
|
+
jnt_id = model.jnt_dofadr[jid]
|
65
|
+
if jnt_type == mujoco.mjtJoint.mjJNT_FREE:
|
66
|
+
raise LimitDefinitionError(f"Free joint {joint_name} is not supported")
|
67
|
+
max_vel = np.atleast_1d(max_vel)
|
68
|
+
if max_vel.shape != (jnt_dim,):
|
69
|
+
raise LimitDefinitionError(
|
70
|
+
f"Joint {joint_name} must have a limit of shape ({jnt_dim},). "
|
71
|
+
f"Got: {max_vel.shape}"
|
72
|
+
)
|
73
|
+
index_list.extend(range(jnt_id, jnt_id + jnt_dim))
|
74
|
+
limit_list.extend(max_vel.tolist())
|
75
|
+
|
76
|
+
self.indices = np.array(index_list)
|
77
|
+
self.indices.setflags(write=False)
|
78
|
+
self.limit = np.array(limit_list)
|
79
|
+
self.limit.setflags(write=False)
|
80
|
+
|
81
|
+
dim = len(self.indices)
|
82
|
+
self.projection_matrix = np.eye(model.nv)[self.indices] if dim > 0 else None
|
83
|
+
|
84
|
+
def compute_qp_inequalities(
|
85
|
+
self, configuration: Configuration, dt: float
|
86
|
+
) -> Constraint:
|
87
|
+
del configuration # Unused.
|
88
|
+
if self.projection_matrix is None:
|
89
|
+
return Constraint()
|
90
|
+
G = np.vstack([self.projection_matrix, -self.projection_matrix])
|
91
|
+
h = np.hstack([dt * self.limit, dt * self.limit])
|
92
|
+
return Constraint(G=G, h=h)
|
mink/py.typed
ADDED
File without changes
|
mink/solve_ik.py
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
"""Build and solve the inverse kinematics problem."""
|
2
|
+
|
3
|
+
from typing import Optional, Sequence
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
import qpsolvers
|
7
|
+
|
8
|
+
from .configuration import Configuration
|
9
|
+
from .limits import ConfigurationLimit, Limit
|
10
|
+
from .tasks import Objective, Task
|
11
|
+
|
12
|
+
|
13
|
+
def _compute_qp_objective(
|
14
|
+
configuration: Configuration, tasks: Sequence[Task], damping: float
|
15
|
+
) -> Objective:
|
16
|
+
H = np.eye(configuration.model.nv) * damping
|
17
|
+
c = np.zeros(configuration.model.nv)
|
18
|
+
for task in tasks:
|
19
|
+
H_task, c_task = task.compute_qp_objective(configuration)
|
20
|
+
H += H_task
|
21
|
+
c += c_task
|
22
|
+
return Objective(H, c)
|
23
|
+
|
24
|
+
|
25
|
+
def _compute_qp_inequalities(
|
26
|
+
configuration: Configuration, limits: Optional[Sequence[Limit]], dt: float
|
27
|
+
) -> tuple[Optional[np.ndarray], Optional[np.ndarray]]:
|
28
|
+
if limits is None:
|
29
|
+
limits = [ConfigurationLimit(configuration.model)]
|
30
|
+
G_list = []
|
31
|
+
h_list = []
|
32
|
+
for limit in limits:
|
33
|
+
inequality = limit.compute_qp_inequalities(configuration, dt)
|
34
|
+
if not inequality.inactive:
|
35
|
+
assert inequality.G is not None and inequality.h is not None # mypy.
|
36
|
+
G_list.append(inequality.G)
|
37
|
+
h_list.append(inequality.h)
|
38
|
+
if not G_list:
|
39
|
+
return None, None
|
40
|
+
return np.vstack(G_list), np.hstack(h_list)
|
41
|
+
|
42
|
+
|
43
|
+
def build_ik(
|
44
|
+
configuration: Configuration,
|
45
|
+
tasks: Sequence[Task],
|
46
|
+
dt: float,
|
47
|
+
damping: float = 1e-12,
|
48
|
+
limits: Optional[Sequence[Limit]] = None,
|
49
|
+
) -> qpsolvers.Problem:
|
50
|
+
"""Build quadratic program from current configuration and tasks.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
configuration: Robot configuration.
|
54
|
+
tasks: List of kinematic tasks.
|
55
|
+
dt: Integration timestep in [s].
|
56
|
+
damping: Levenberg-Marquardt damping.
|
57
|
+
limits: List of limits to enforce. Set to empty list to disable. If None,
|
58
|
+
defaults to a configuration limit.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Quadratic program of the inverse kinematics problem.
|
62
|
+
"""
|
63
|
+
P, q = _compute_qp_objective(configuration, tasks, damping)
|
64
|
+
G, h = _compute_qp_inequalities(configuration, limits, dt)
|
65
|
+
return qpsolvers.Problem(P, q, G, h)
|
66
|
+
|
67
|
+
|
68
|
+
def solve_ik(
|
69
|
+
configuration: Configuration,
|
70
|
+
tasks: Sequence[Task],
|
71
|
+
dt: float,
|
72
|
+
solver: str,
|
73
|
+
damping: float = 1e-12,
|
74
|
+
safety_break: bool = False,
|
75
|
+
limits: Optional[Sequence[Limit]] = None,
|
76
|
+
**kwargs,
|
77
|
+
) -> np.ndarray:
|
78
|
+
"""Solve the differential inverse kinematics problem.
|
79
|
+
|
80
|
+
Computes a velocity tangent to the current robot configuration. The computed
|
81
|
+
velocity satisfies at (weighted) best the set of provided kinematic tasks.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
configuration: Robot configuration.
|
85
|
+
tasks: List of kinematic tasks.
|
86
|
+
dt: Integration timestep in [s].
|
87
|
+
solver: Backend quadratic programming (QP) solver.
|
88
|
+
damping: Levenberg-Marquardt damping.
|
89
|
+
safety_break: If True, stop execution and raise an exception if
|
90
|
+
the current configuration is outside limits. If False, print a
|
91
|
+
warning and continue execution.
|
92
|
+
limits: List of limits to enforce. Set to empty list to disable. If None,
|
93
|
+
defaults to a configuration limit.
|
94
|
+
kwargs: Keyword arguments to forward to the backend QP solver.
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
Velocity `v` in tangent space.
|
98
|
+
"""
|
99
|
+
configuration.check_limits(safety_break=safety_break)
|
100
|
+
problem = build_ik(configuration, tasks, dt, damping, limits)
|
101
|
+
result = qpsolvers.solve_problem(problem, solver=solver, **kwargs)
|
102
|
+
dq = result.x
|
103
|
+
assert dq is not None
|
104
|
+
v: np.ndarray = dq / dt
|
105
|
+
return v
|
mink/tasks/__init__.py
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
"""Kinematic tasks."""
|
2
|
+
|
3
|
+
from .com_task import ComTask
|
4
|
+
from .exceptions import (
|
5
|
+
InvalidDamping,
|
6
|
+
InvalidGain,
|
7
|
+
InvalidTarget,
|
8
|
+
TargetNotSet,
|
9
|
+
TaskDefinitionError,
|
10
|
+
)
|
11
|
+
from .frame_task import FrameTask
|
12
|
+
from .posture_task import PostureTask
|
13
|
+
from .task import Objective, Task
|
14
|
+
|
15
|
+
__all__ = (
|
16
|
+
"ComTask",
|
17
|
+
"FrameTask",
|
18
|
+
"Objective",
|
19
|
+
"PostureTask",
|
20
|
+
"Task",
|
21
|
+
"TargetNotSet",
|
22
|
+
"InvalidTarget",
|
23
|
+
"TaskDefinitionError",
|
24
|
+
"InvalidGain",
|
25
|
+
"InvalidDamping",
|
26
|
+
)
|