kinfer 0.4.3__cp311-cp311-macosx_11_0_arm64.whl → 0.5.2__cp311-cp311-macosx_11_0_arm64.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.
kinfer/rust_bindings.pyi CHANGED
@@ -8,14 +8,21 @@ import typing
8
8
 
9
9
  class ModelProviderABC:
10
10
  def __new__(cls) -> ModelProviderABC: ...
11
- def get_joint_angles(self, joint_names:typing.Sequence[builtins.str]) -> numpy.typing.NDArray[numpy.float32]: ...
12
- def get_joint_angular_velocities(self, joint_names:typing.Sequence[builtins.str]) -> numpy.typing.NDArray[numpy.float32]: ...
13
- def get_projected_gravity(self) -> numpy.typing.NDArray[numpy.float32]: ...
14
- def get_accelerometer(self) -> numpy.typing.NDArray[numpy.float32]: ...
15
- def get_gyroscope(self) -> numpy.typing.NDArray[numpy.float32]: ...
16
- def get_command(self) -> numpy.typing.NDArray[numpy.float32]: ...
17
- def get_time(self) -> numpy.typing.NDArray[numpy.float32]: ...
18
- def take_action(self, joint_names:typing.Sequence[builtins.str], action:numpy.typing.NDArray[numpy.float32]) -> None: ...
11
+ def get_inputs(self, input_types:typing.Sequence[builtins.str], metadata:PyModelMetadata) -> builtins.dict[builtins.str, numpy.typing.NDArray[numpy.float32]]: ...
12
+ def take_action(self, action:numpy.typing.NDArray[numpy.float32], metadata:PyModelMetadata) -> None: ...
13
+
14
+ class PyInputType:
15
+ def __new__(cls, input_type:builtins.str) -> PyInputType: ...
16
+ def get_name(self) -> builtins.str: ...
17
+ def get_shape(self, metadata:PyModelMetadata) -> builtins.list[builtins.int]: ...
18
+ def __repr__(self) -> builtins.str: ...
19
+ def __eq__(self, other:typing.Any) -> builtins.bool: ...
20
+
21
+ class PyModelMetadata:
22
+ def __new__(self, joint_names:typing.Sequence[builtins.str], num_commands:typing.Optional[builtins.int], carry_size:typing.Sequence[builtins.int]) -> PyModelMetadata: ...
23
+ def to_json(self) -> builtins.str: ...
24
+ def __repr__(self) -> builtins.str: ...
25
+ def __eq__(self, other:typing.Any) -> builtins.bool: ...
19
26
 
20
27
  class PyModelProvider:
21
28
  ...
@@ -35,3 +42,5 @@ class PyModelRuntime:
35
42
 
36
43
  def get_version() -> builtins.str: ...
37
44
 
45
+ def metadata_from_json(json:builtins.str) -> PyModelMetadata: ...
46
+
@@ -0,0 +1,177 @@
1
+ """Plot NDJSON logs saved by kinfer."""
2
+
3
+ import argparse
4
+ import json
5
+ import logging
6
+ from pathlib import Path
7
+ from typing import Optional, Union
8
+
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+
12
+ # Set up logger
13
+ logger = logging.getLogger(__name__)
14
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
15
+
16
+
17
+ def read_ndjson(filepath: str) -> list[dict]:
18
+ """Read NDJSON file and return list of parsed objects."""
19
+ data = []
20
+ with open(filepath, "r") as f:
21
+ for line in f:
22
+ line = line.strip()
23
+ if line:
24
+ data.append(json.loads(line))
25
+ return data
26
+
27
+
28
+ def skip_initial_data(data: list[dict], skip_seconds: float) -> list[dict]:
29
+ """Skip the first n seconds of data based on timestamps."""
30
+ if not data or skip_seconds <= 0.0:
31
+ return data
32
+
33
+ # Extract timestamps and convert to seconds relative to first timestamp
34
+ timestamps = [d["t_us"] for d in data]
35
+ t_start = timestamps[0]
36
+ times = [(t - t_start) / 1e6 for t in timestamps] # Convert to seconds
37
+
38
+ # Find indices where time >= skip_seconds
39
+ skip_indices = [i for i, t in enumerate(times) if t >= skip_seconds]
40
+ if not skip_indices:
41
+ logger.info("All data points are within the skip period (%.2f seconds). No data to plot.", skip_seconds)
42
+ return []
43
+
44
+ # Filter data
45
+ start_idx = skip_indices[0]
46
+ filtered_data = data[start_idx:]
47
+ logger.info("Skipped first %.2f seconds (%d data points)", skip_seconds, start_idx)
48
+
49
+ return filtered_data
50
+
51
+
52
+ def plot_data(data: list[dict], save_path: Optional[Union[str, Path]] = None) -> None:
53
+ """Plot all data fields from the NDJSON."""
54
+ if not data:
55
+ logger.info("No data to plot")
56
+ return
57
+
58
+ # Extract timestamps and convert to seconds relative to first timestamp
59
+ timestamps = [d["t_us"] for d in data]
60
+ t_start = timestamps[0]
61
+ times = [(t - t_start) / 1e6 for t in timestamps] # Convert to seconds
62
+
63
+ # Extract data arrays
64
+ joint_angles = np.array([d["joint_angles"] for d in data if d["joint_angles"] is not None])
65
+ joint_vels = np.array([d["joint_vels"] for d in data if d["joint_vels"] is not None])
66
+ projected_g = np.array([d["projected_g"] for d in data if d["projected_g"] is not None])
67
+ accel = np.array([d["accel"] for d in data if d["accel"] is not None])
68
+ command = np.array([d["command"] for d in data if d["command"] is not None])
69
+ output = np.array([d["output"] for d in data if d["output"] is not None])
70
+
71
+ # Create subplots
72
+ fig, axes = plt.subplots(3, 2, figsize=(15, 12))
73
+ fig.suptitle("Robot Data Over Time", fontsize=16)
74
+
75
+ if len(joint_angles) > 0:
76
+ ax = axes[0, 0]
77
+ for i in range(joint_angles.shape[1]):
78
+ ax.plot(times[: len(joint_angles)], joint_angles[:, i], alpha=0.7, linewidth=0.8)
79
+ ax.set_title("Joint Angles")
80
+ ax.set_xlabel("Time (s)")
81
+ ax.set_ylabel("Angle (rad)")
82
+ ax.grid(True, alpha=0.3)
83
+
84
+ if len(joint_vels) > 0:
85
+ ax = axes[0, 1]
86
+ for i in range(joint_vels.shape[1]):
87
+ ax.plot(times[: len(joint_vels)], joint_vels[:, i], alpha=0.7, linewidth=0.8)
88
+ ax.set_title("Joint Velocities")
89
+ ax.set_xlabel("Time (s)")
90
+ ax.set_ylabel("Velocity (rad/s)")
91
+ ax.grid(True, alpha=0.3)
92
+
93
+ if len(projected_g) > 0:
94
+ ax = axes[1, 0]
95
+ labels = ["X", "Y", "Z"]
96
+ for i in range(projected_g.shape[1]):
97
+ ax.plot(times[: len(projected_g)], projected_g[:, i], label=labels[i], linewidth=1.5)
98
+ ax.set_title("Projected Gravity")
99
+ ax.set_xlabel("Time (s)")
100
+ ax.set_ylabel("Acceleration (m/s²)")
101
+ ax.legend()
102
+ ax.grid(True, alpha=0.3)
103
+
104
+ if len(accel) > 0:
105
+ ax = axes[1, 1]
106
+ labels = ["X", "Y", "Z"]
107
+ for i in range(accel.shape[1]):
108
+ ax.plot(times[: len(accel)], accel[:, i], label=labels[i], linewidth=1.5)
109
+ ax.set_title("Acceleration")
110
+ ax.set_xlabel("Time (s)")
111
+ ax.set_ylabel("Acceleration (m/s²)")
112
+ ax.legend()
113
+ ax.grid(True, alpha=0.3)
114
+
115
+ if len(command) > 0:
116
+ ax = axes[2, 0]
117
+ for i in range(command.shape[1]):
118
+ ax.plot(times[: len(command)], command[:, i], label=f"Cmd {i}", linewidth=1.2)
119
+ ax.set_title("Command")
120
+ ax.set_xlabel("Time (s)")
121
+ ax.set_ylabel("Command Value")
122
+ ax.legend()
123
+ ax.grid(True, alpha=0.3)
124
+
125
+ if len(output) > 0:
126
+ ax = axes[2, 1]
127
+ for i in range(output.shape[1]):
128
+ ax.plot(times[: len(output)], output[:, i], alpha=0.7, linewidth=0.8)
129
+ ax.set_title("Output")
130
+ ax.set_xlabel("Time (s)")
131
+ ax.set_ylabel("Output Value")
132
+ ax.grid(True, alpha=0.3)
133
+
134
+ plt.tight_layout()
135
+
136
+ if save_path:
137
+ plt.savefig(save_path, dpi=300, bbox_inches="tight")
138
+ logger.info("Plot saved to: %s", save_path)
139
+ plt.close()
140
+ else:
141
+ plt.show()
142
+
143
+
144
+ def main() -> None:
145
+ parser = argparse.ArgumentParser(description="Plot NDJSON logs saved by kinfer")
146
+ parser.add_argument("filepath", help="Path to the NDJSON file to plot")
147
+ parser.add_argument("--skip", type=float, default=0.0, help="Skip the first n seconds of data")
148
+ parser.add_argument("--save", action="store_true", help="Save the plot to a PNG file in a plots folder")
149
+ args = parser.parse_args()
150
+
151
+ filepath = args.filepath
152
+ if not Path(filepath).exists():
153
+ logger.info("File not found: %s", filepath)
154
+ return
155
+
156
+ logger.info("Reading data from %s...", filepath)
157
+ data = read_ndjson(filepath)
158
+ logger.info("Loaded %d data points", len(data))
159
+
160
+ filtered_data = skip_initial_data(data, args.skip)
161
+
162
+ save_path = None
163
+ if args.save:
164
+ # Create save path in plots folder with same name but .png extension
165
+ input_path = Path(filepath)
166
+ plots_dir = input_path.parent / "plots"
167
+ plots_dir.mkdir(exist_ok=True)
168
+
169
+ # Change extension from .ndjson to .png
170
+ filename = input_path.stem + ".png"
171
+ save_path = str(plots_dir / filename)
172
+
173
+ plot_data(filtered_data, save_path)
174
+
175
+
176
+ if __name__ == "__main__":
177
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kinfer
3
- Version: 0.4.3
3
+ Version: 0.5.2
4
4
  Summary: Tool to make it easier to run a model on a real robot
5
5
  Home-page: https://github.com/kscalelabs/kinfer.git
6
6
  Author: K-Scale Labs
@@ -10,6 +10,9 @@ License-File: LICENSE
10
10
  Requires-Dist: onnx
11
11
  Requires-Dist: onnxruntime==1.20.0
12
12
  Requires-Dist: pydantic
13
+ Requires-Dist: matplotlib
14
+ Requires-Dist: numpy
15
+ Requires-Dist: pathlib
13
16
  Provides-Extra: dev
14
17
  Requires-Dist: black; extra == "dev"
15
18
  Requires-Dist: darglint; extra == "dev"
@@ -0,0 +1,26 @@
1
+ kinfer/rust_bindings.cpython-311-darwin.so,sha256=BuoTMXGqu9a6LNcthv_SxZ1BWZvkU_pIAliqHTSK0XM,2090064
2
+ kinfer/requirements.txt,sha256=e8RzCNVZGJ9Jb874Eom0psUCOqp0wGf8LJZ86ysto0w,127
3
+ kinfer/__init__.py,sha256=i5da6ND827Cgn8PFKzDCmEBk14ptQasLa_9fdof4Y9c,398
4
+ kinfer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kinfer/rust_bindings.pyi,sha256=lPGHMDsydr4Ue-CNHRjQtJWmq5sE5daVN8PbxJgOZY4,2048
6
+ kinfer/rust/Cargo.toml,sha256=NoWaLluRJHV6mOCeVXk8q8Pb4028Z3ZPqLspBMHAPTQ,703
7
+ kinfer/rust/src/runtime.rs,sha256=7BRZmyMoV6FvYs2_XnLk0DFrxt7qgz35loCUJRSsuxM,3448
8
+ kinfer/rust/src/logger.rs,sha256=iDb8hiRjxnH8l-Qzth5XcLk2ICnnX1JlBxyOzaZ4t64,3919
9
+ kinfer/rust/src/types.rs,sha256=KNIM6sHvX1gnQZc74uRTgmzbgS4FZjxll5jcuNGIDc8,2768
10
+ kinfer/rust/src/lib.rs,sha256=25LuAdONp9c3lI0_g2kAf73yrtzbYc9GtJjm1zl9IwU,120
11
+ kinfer/rust/src/model.rs,sha256=RQR0y7u9lPoSBNPnBWYesfxDkBSBBJpkyiH6qqCeQRI,11504
12
+ kinfer/scripts/plot_ndjson.py,sha256=eg-wwuDLRDcuVdr1Jc5W4yeJG5Bf--UjaK6kk9XoC4w,6179
13
+ kinfer/export/serialize.py,sha256=QjsvTn7cIRroZXXONXQUrEd1tR69oLrszojuKoK-Xuk,3208
14
+ kinfer/export/__init__.py,sha256=6a8cClA2H3AAjAhY6ucmxEnfCRZ30FDn2L7SArCu7nY,56
15
+ kinfer/export/jax.py,sha256=zB3HJ_Ao-UtBKpFddywHUQstBpvc5OG4DOJk79VTczE,1423
16
+ kinfer/export/pytorch.py,sha256=nRo9TKClRhPcLJQDR3oHNCbTYjf_3gLBxGfzZBScPxc,1303
17
+ kinfer/rust_bindings/Cargo.toml,sha256=i1RGB9VNd9Q4FJ6gGwjZJQYo8DBBvpVWf3GJ95EfVgM,637
18
+ kinfer/rust_bindings/pyproject.toml,sha256=jLcJuHCnQRh9HWR_R7a9qLHwj6LMBgnHyeKK_DruO1Y,135
19
+ kinfer/rust_bindings/rust_bindings.pyi,sha256=lPGHMDsydr4Ue-CNHRjQtJWmq5sE5daVN8PbxJgOZY4,2048
20
+ kinfer/rust_bindings/src/lib.rs,sha256=Uqi76Na_fGiecMeW5mSSmtYdz2wU8clLxYawbYomNOc,12662
21
+ kinfer/rust_bindings/src/bin/stub_gen.rs,sha256=hhoVGnaSfazbSfj5a4x6mPicGPOgWQAfsDmiPej0B6Y,133
22
+ kinfer-0.5.2.dist-info/RECORD,,
23
+ kinfer-0.5.2.dist-info/WHEEL,sha256=sunMa2yiYbrNLGeMVDqEA0ayyJbHlex7SCn1TZrEq60,136
24
+ kinfer-0.5.2.dist-info/top_level.txt,sha256=6mY_t3PYr3Dm0dpqMk80uSnArbvGfCFkxOh1QWtgDEo,7
25
+ kinfer-0.5.2.dist-info/METADATA,sha256=VbHb0kFyouhwwF4mQ0ajRJQScLpdGGBYNOn_RiBsMFc,1831
26
+ kinfer-0.5.2.dist-info/licenses/LICENSE,sha256=Qw-Z0XTwS-diSW91e_jLeBPX9zZbAatOJTBLdPHPaC0,1069
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp311-cp311-macosx_11_0_arm64
5
5
  Generator: delocate 0.13.0
kinfer/common/__init__.py DELETED
File without changes
kinfer/common/types.py DELETED
@@ -1,12 +0,0 @@
1
- """Defines common types."""
2
-
3
- __all__ = [
4
- "Metadata",
5
- ]
6
-
7
- from pydantic import BaseModel
8
-
9
-
10
- class Metadata(BaseModel):
11
- joint_names: list[str]
12
- num_commands: int | None
kinfer/export/common.py DELETED
@@ -1,44 +0,0 @@
1
- """Defines common utilities for exporting models."""
2
-
3
-
4
- def get_shape(
5
- name: str,
6
- num_joints: int | None = None,
7
- num_commands: int | None = None,
8
- carry_shape: tuple[int, ...] | None = None,
9
- ) -> tuple[int, ...]:
10
- match name:
11
- case "joint_angles":
12
- if num_joints is None:
13
- raise ValueError("`num_joints` must be provided when using `joint_angles`")
14
- return (num_joints,)
15
-
16
- case "joint_angular_velocities":
17
- if num_joints is None:
18
- raise ValueError("`num_joints` must be provided when using `joint_angular_velocities`")
19
- return (num_joints,)
20
-
21
- case "projected_gravity":
22
- return (3,)
23
-
24
- case "accelerometer":
25
- return (3,)
26
-
27
- case "gyroscope":
28
- return (3,)
29
-
30
- case "command":
31
- if num_commands is None:
32
- raise ValueError("`num_commands` must be provided when using `command`")
33
- return (num_commands,)
34
-
35
- case "carry":
36
- if carry_shape is None:
37
- raise ValueError("`carry_shape` must be provided for `carry`")
38
- return carry_shape
39
-
40
- case "time":
41
- return (1,)
42
-
43
- case _:
44
- raise ValueError(f"Unknown tensor name: {name}")
@@ -1,26 +0,0 @@
1
- kinfer/rust_bindings.cpython-311-darwin.so,sha256=ZUWCReCTDKhzqBteg3k4nJ-15ubYFMCSBYQdADqcvd0,1953904
2
- kinfer/requirements.txt,sha256=j08HO4ptA5afuj99j8FlAP2qla5Zf4_OiEBtgAmF7Jg,90
3
- kinfer/__init__.py,sha256=i5da6ND827Cgn8PFKzDCmEBk14ptQasLa_9fdof4Y9c,398
4
- kinfer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- kinfer/rust_bindings.pyi,sha256=lyBw-t_9ONYwVSaUTeeYDWpBbhKod1wf9Dvc4MgnC1g,1776
6
- kinfer/rust/Cargo.toml,sha256=uxjvuM1Sm82UgCdHW3R6PbbiJjXvfE4VCLvjGPqp2mY,606
7
- kinfer/rust/src/runtime.rs,sha256=l9wG0XbZmpHSqho3SfGuW54P7YXs8eXZ365kOj7UqUE,3321
8
- kinfer/rust/src/lib.rs,sha256=Z3dWdhKhqhWqPJee6vWde4ptqquOYW6W9wB0wyaKCyk,71
9
- kinfer/rust/src/model.rs,sha256=zY_XmkCZOq8VhVE_LpgTxKDrV-FceY8tnkkqS1tFa8c,11890
10
- kinfer/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- kinfer/common/types.py,sha256=jiuCfoTEumOJm4VZ03VoFVIL5PMJ1tCG7b2Kr66qJQA,176
12
- kinfer/export/serialize.py,sha256=7rcSrXawVnJinxeJp26wovUwoR04BnJ5CZAV9vwKUUo,3474
13
- kinfer/export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- kinfer/export/jax.py,sha256=gZWWUJKb3MPEtfNYeKfPnypeK0elJwGuDYfpCW0GqNY,1413
15
- kinfer/export/pytorch.py,sha256=rwHGOTcd08IeL5-HZ_7JVUMPMHleiHI8-TkShAoRrYM,1612
16
- kinfer/export/common.py,sha256=AYN_gwmxmSlZ7BuswGUKYcY8uVUCh9UyXnWw-k8RVys,1290
17
- kinfer/rust_bindings/Cargo.toml,sha256=i1RGB9VNd9Q4FJ6gGwjZJQYo8DBBvpVWf3GJ95EfVgM,637
18
- kinfer/rust_bindings/pyproject.toml,sha256=jLcJuHCnQRh9HWR_R7a9qLHwj6LMBgnHyeKK_DruO1Y,135
19
- kinfer/rust_bindings/rust_bindings.pyi,sha256=lyBw-t_9ONYwVSaUTeeYDWpBbhKod1wf9Dvc4MgnC1g,1776
20
- kinfer/rust_bindings/src/lib.rs,sha256=pjsRej2BxTsecG9Y7GdBpW6GGR-pgbBk3yrKqEEQ3K0,11547
21
- kinfer/rust_bindings/src/bin/stub_gen.rs,sha256=hhoVGnaSfazbSfj5a4x6mPicGPOgWQAfsDmiPej0B6Y,133
22
- kinfer-0.4.3.dist-info/RECORD,,
23
- kinfer-0.4.3.dist-info/WHEEL,sha256=3lrG374qykB8NIZqJ4ApcNfFLoAGB9tcfdD4_UMfO40,136
24
- kinfer-0.4.3.dist-info/top_level.txt,sha256=6mY_t3PYr3Dm0dpqMk80uSnArbvGfCFkxOh1QWtgDEo,7
25
- kinfer-0.4.3.dist-info/METADATA,sha256=DZKaMDT-DToGkw_Ge_nSYBoo3ZAzxatlv6sR0x9kv-A,1761
26
- kinfer-0.4.3.dist-info/licenses/LICENSE,sha256=Qw-Z0XTwS-diSW91e_jLeBPX9zZbAatOJTBLdPHPaC0,1069