kmk is a utility repo for authoring and consuming keypoint-based multi-fingered hand kinematics data.
It has two main jobs:
- a staged GUI wizard that builds a hand config YAML
- runtime loaders and torch kinematics utilities that read that YAML
gripper_config_wizard- staged authoring flow for:
- global hand config
- per-link contact anchors
- grasp templates
- final preview/confirmation
- staged authoring flow for:
HandInfo- read-only runtime wrapper around the authored YAML
PointedHandInfoHandInfoplus precomputed surface/contact point samples
HandKinematics- torch forward kinematics for batched joint vectors and link-local point sets
- Prepare a gripper root directory containing at least a URDF.
- Run the wizard.
- Fill the global stage.
- palm pose
- palm points delta
- global
q_open - extra collision ignore pairs
- Fill the keypoint stage.
- one contact anchor per link
- point, radius, tags
- Fill the grasp template stage.
q_open,q_closegrasp_target_point- active contact anchors
- Check the final preview and press
Confirmed. - Use the saved YAML through
HandInfo,PointedHandInfo, orHandKinematics.
uv syncCreate a new config:
uv run gripper_config_wizard --gripper-root /path/to/gripper_rootEdit an existing config:
uv run gripper_config_wizard \
--gripper-root /path/to/gripper_root \
--from-config existing.yamlNotes:
--from-configrelative paths are resolved fromgripper_root.urdf_pathandxml_pathstored in YAML are typically relative paths.
Inspect a saved config and sampled points:
uv run python visualize/pointed_hand_info.py --config-path /path/to/hand.yamlInspect batched torch kinematics:
uv run python visualize/hand_kinematics_batch.py --config-path /path/to/hand.yamlfrom kmk import HandInfo
hand = HandInfo.from_config("hand.yaml")
q_open = hand.get_q_open()
q_close = hand.get_q_close("finger4_shallow")
palm_pose = hand.get_palm_pose()
anchor = hand.get_contact_anchor_by_link("right_1thumb_distal")
anchors = hand.get_contact_anchor_by_template("finger4_shallow")
target = hand.get_grasp_target_point("finger4_shallow")Main API:
HandInfo.from_config(config_path)joint_ordertemplate_namescontact_anchor_linksget_q_open(template="global")get_q_close(template_name)get_palm_pose()get_contact_anchor_by_link(link_name)get_contact_anchor_by_tag(includes=(), excludes=())get_contact_anchor_by_template(template_name)get_grasp_target_point(template_name)
from kmk import PointedHandInfo
hand = PointedHandInfo.from_config("hand.yaml", seed=0)
surface_points = hand.surface_points
contact_points = hand.get_contact_points("finger4_shallow")
keypoints = hand.get_keypoints("finger4_shallow", palm_aligned_points=True)Main additions:
surface_pointscontact_pointsget_contact_points(template_name=None)get_keypoints(template_name=None, palm_aligned_points=True, palm_points_delta=...)
import torch
from kmk import HandInfo, HandKinematics
hand = HandInfo.from_config("hand.yaml")
kin = HandKinematics(hand).to(device="cpu")
q = torch.zeros(16, len(hand.joint_order))
fk = kin.forward_kinematics(q)
world_points = kin.transform_link_points(
q,
{"right_1thumb_distal": torch.tensor([[0.0, 0.0, 0.02]])},
)Main API:
HandKinematics(hand_info_or_config_path)joint_orderlink_namesdofforward_kinematics(q)transform_link_points(q, points_by_link)get_palm_pose(batch_shape=None)
The detailed spec lives in SPEC.md.