screws 0.1.0__tar.gz
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.
- screws-0.1.0/.github/workflows/ci.yml +21 -0
- screws-0.1.0/.github/workflows/publish.yml +47 -0
- screws-0.1.0/.gitignore +8 -0
- screws-0.1.0/LICENSE +26 -0
- screws-0.1.0/PKG-INFO +159 -0
- screws-0.1.0/README.md +119 -0
- screws-0.1.0/docs/RELEASING.md +21 -0
- screws-0.1.0/docs/superpowers/plans/2026-09-26-screws-0.1.md +611 -0
- screws-0.1.0/docs/superpowers/specs/2026-09-26-screws-package-design.md +469 -0
- screws-0.1.0/pyproject.toml +36 -0
- screws-0.1.0/screws/__init__.py +37 -0
- screws-0.1.0/screws/_version.py +1 -0
- screws-0.1.0/screws/aliases.py +94 -0
- screws-0.1.0/screws/coppelia/__init__.py +8 -0
- screws-0.1.0/screws/coppelia/_sim.py +119 -0
- screws-0.1.0/screws/coppelia/arm.py +230 -0
- screws-0.1.0/screws/coppelia/log.py +105 -0
- screws-0.1.0/screws/coppelia/scene.py +152 -0
- screws-0.1.0/screws/kinematics.py +214 -0
- screws-0.1.0/screws/robot.py +261 -0
- screws-0.1.0/screws/robots/__init__.py +49 -0
- screws-0.1.0/screws/robots/rrp.urdf +20 -0
- screws-0.1.0/screws/robots/ur5.urdf +114 -0
- screws-0.1.0/screws/se3.py +210 -0
- screws-0.1.0/screws/so3.py +166 -0
- screws-0.1.0/screws/testing.py +68 -0
- screws-0.1.0/screws/urdf.py +240 -0
- screws-0.1.0/tests/__init__.py +0 -0
- screws-0.1.0/tests/conftest.py +12 -0
- screws-0.1.0/tests/coppelia/__init__.py +0 -0
- screws-0.1.0/tests/coppelia/fake_sim.py +253 -0
- screws-0.1.0/tests/coppelia/test_arm.py +150 -0
- screws-0.1.0/tests/coppelia/test_conversions.py +68 -0
- screws-0.1.0/tests/coppelia/test_live_ur5.py +77 -0
- screws-0.1.0/tests/coppelia/test_scene.py +121 -0
- screws-0.1.0/tests/mr_examples.py +142 -0
- screws-0.1.0/tests/test_aliases.py +62 -0
- screws-0.1.0/tests/test_import.py +11 -0
- screws-0.1.0/tests/test_kinematics.py +108 -0
- screws-0.1.0/tests/test_mr_examples.py +50 -0
- screws-0.1.0/tests/test_readme_table.py +20 -0
- screws-0.1.0/tests/test_robot.py +95 -0
- screws-0.1.0/tests/test_se3.py +96 -0
- screws-0.1.0/tests/test_so3.py +85 -0
- screws-0.1.0/tests/test_testing.py +41 -0
- screws-0.1.0/tests/test_urdf.py +113 -0
- screws-0.1.0/tools/harvest_mr_examples.py +53 -0
- screws-0.1.0/tools/print_alias_table.py +13 -0
- screws-0.1.0/uv.lock +1489 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: uv sync --all-extras
|
|
20
|
+
- run: uv run ruff check .
|
|
21
|
+
- run: uv run pytest -q
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Runs when a version tag is pushed (git tag v0.1.0 && git push --tags).
|
|
4
|
+
# Publishes with PyPI trusted publishing (OIDC) when the project has this workflow
|
|
5
|
+
# registered as a publisher; otherwise falls back to the PYPI_API_TOKEN repository secret.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- name: Check that the tag matches the package version
|
|
20
|
+
run: |
|
|
21
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
22
|
+
version="$(uv run --no-project python -c 'import tomllib,sys; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
|
23
|
+
test "$tag" = "$version" || { echo "tag v$tag != pyproject version $version"; exit 1; }
|
|
24
|
+
grep -q "__version__ = \"$version\"" screws/_version.py || { echo "_version.py disagrees"; exit 1; }
|
|
25
|
+
- run: uv sync --all-extras
|
|
26
|
+
- run: uv run pytest -q
|
|
27
|
+
- run: uv build
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: pypi
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # trusted publishing
|
|
39
|
+
contents: read
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
46
|
+
with:
|
|
47
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
screws-0.1.0/.gitignore
ADDED
screws-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rico Picone
|
|
4
|
+
|
|
5
|
+
Portions of this software are derived from the Modern Robotics code library
|
|
6
|
+
(https://github.com/NxRLab/ModernRobotics),
|
|
7
|
+
Copyright (c) 2018 Huan Weng, Bill Hunt, Jarvis Schultz, Mikhail Todes,
|
|
8
|
+
distributed under the same MIT License reproduced below.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
screws-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: screws
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Screw-theory robotics: the Modern Robotics library, reorganised, with a CoppeliaSim bridge
|
|
5
|
+
Author: Rico Picone
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Rico Picone
|
|
9
|
+
|
|
10
|
+
Portions of this software are derived from the Modern Robotics code library
|
|
11
|
+
(https://github.com/NxRLab/ModernRobotics),
|
|
12
|
+
Copyright (c) 2018 Huan Weng, Bill Hunt, Jarvis Schultz, Mikhail Todes,
|
|
13
|
+
distributed under the same MIT License reproduced below.
|
|
14
|
+
|
|
15
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
in the Software without restriction, including without limitation the rights
|
|
18
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
furnished to do so, subject to the following conditions:
|
|
21
|
+
|
|
22
|
+
The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
copies or substantial portions of the Software.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
|
32
|
+
License-File: LICENSE
|
|
33
|
+
Requires-Python: >=3.10
|
|
34
|
+
Requires-Dist: numpy>=1.24
|
|
35
|
+
Provides-Extra: coppelia
|
|
36
|
+
Requires-Dist: coppeliasim-zmqremoteapi-client==2.0.4; extra == 'coppelia'
|
|
37
|
+
Provides-Extra: plot
|
|
38
|
+
Requires-Dist: matplotlib>=3.7; extra == 'plot'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# screws
|
|
42
|
+
|
|
43
|
+
Screw-theory robotics in Python, after Lynch and Park, *Modern Robotics* (MR).
|
|
44
|
+
`screws` is the MR code library reorganised: the same mathematics under
|
|
45
|
+
snake_case names, MR's own names kept as aliases, a `Robot` class, a URDF
|
|
46
|
+
loader, robots that ship ready to use, and a bridge to CoppeliaSim.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
uv add screws # the mathematics: numpy is the only dependency
|
|
52
|
+
uv add "screws[coppelia]" # plus the CoppeliaSim ZMQ remote API client
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Four lines
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import screws as sc
|
|
59
|
+
|
|
60
|
+
ur5 = sc.robots.ur5() # M, screw axes, inertias from the textbook's URDF
|
|
61
|
+
T = ur5.fk([0.3, -1.2, 0.8, -0.4, 1.1, 0.2]) # a reachable, non-singular pose
|
|
62
|
+
result = ur5.ik(T, theta0=[0.1, -1.4, 0.1, 0.1, 1.4, 0.1]) # result.theta, result.converged, result.history
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The free functions are the reference implementation and read as the book does:
|
|
66
|
+
`sc.exp6(sc.vec_to_se3(S * theta))` is $e^{[\mathcal{S}]\theta}$, `sc.fk_space(M, S, theta)`
|
|
67
|
+
is the space form of the product of exponentials, and `sc.FKinSpace` is the very same
|
|
68
|
+
function under MR's name.
|
|
69
|
+
|
|
70
|
+
Screw-axis lists are **6xn, one axis per column**, as MR writes them. Nothing guesses the
|
|
71
|
+
orientation (a 6x6 array is ambiguous for a six-axis arm), so hand entry goes through a
|
|
72
|
+
sequence of 6-vectors: `sc.Robot.from_screw_axes(M, [S1, S2, ...])`.
|
|
73
|
+
|
|
74
|
+
## Check your own code against the library
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import numpy as np
|
|
78
|
+
import screws as sc
|
|
79
|
+
|
|
80
|
+
def my_exp6(se3mat): ... # your implementation
|
|
81
|
+
|
|
82
|
+
rng = np.random.default_rng(0)
|
|
83
|
+
cases = [sc.vec_to_se3(rng.normal(size=6)) for _ in range(20)]
|
|
84
|
+
sc.testing.check(my_exp6, sc.exp6, cases) # raises on the first disagreement
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## CoppeliaSim
|
|
88
|
+
|
|
89
|
+
Works with CoppeliaSim 4.9 or later through the ZMQ remote API (0.1 verified on 4.10.0).
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from screws.coppelia import Scene
|
|
93
|
+
|
|
94
|
+
with Scene() as scene: # connects to localhost:23000 in stepping mode
|
|
95
|
+
arm = scene.arm("/UR5") # the joints under that tree, base to tip
|
|
96
|
+
robot = arm.robot() # a screws.Robot read off the scene at zero
|
|
97
|
+
arm.mode("position")
|
|
98
|
+
log = scene.run(lambda t, theta, dtheta: theta_desired(t), duration=5.0, arm=arm)
|
|
99
|
+
log.plot()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`Scene` owns the connection and the clock (`start`, `step`, `stop`, `time`, `dt`,
|
|
103
|
+
`frame`, `show_frame`). `Arm` reads (`theta`, `dtheta`, `tau`, `tip_frame`) and commands
|
|
104
|
+
in one of three modes (`position`, `velocity`, `torque`), or `teleport`s without physics
|
|
105
|
+
to animate an IK history. `Arm.robot()` derives M and the screw axes from the scene's
|
|
106
|
+
joint frames (omega is the joint's z axis, v = -omega x q). Scene inertias arrive in 0.2.
|
|
107
|
+
|
|
108
|
+
## Two UR5s
|
|
109
|
+
|
|
110
|
+
`sc.robots.ur5()` is built from the URDF the textbook prints in section 4.2: the
|
|
111
|
+
manufacturer's lengths and the printed inertias, the closest published match to the
|
|
112
|
+
simulator's model. `sc.robots.ur5(source="textbook")` is the rounded table of MR Figure
|
|
113
|
+
4.6 with no inertias. They differ in the third decimal of the $v$ entries, and the course
|
|
114
|
+
notes' problems ask why.
|
|
115
|
+
|
|
116
|
+
## Names
|
|
117
|
+
|
|
118
|
+
| Modern Robotics | `screws` |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `NearZero` | `near_zero` |
|
|
121
|
+
| `Normalize` | `normalize` |
|
|
122
|
+
| `RotInv` | `rot_inv` |
|
|
123
|
+
| `VecToso3` | `vec_to_so3` |
|
|
124
|
+
| `so3ToVec` | `so3_to_vec` |
|
|
125
|
+
| `AxisAng3` | `axis_angle3` |
|
|
126
|
+
| `MatrixExp3` | `exp3` |
|
|
127
|
+
| `MatrixLog3` | `log3` |
|
|
128
|
+
| `RpToTrans` | `rp_to_transform` |
|
|
129
|
+
| `TransToRp` | `transform_to_rp` |
|
|
130
|
+
| `TransInv` | `transform_inv` |
|
|
131
|
+
| `VecTose3` | `vec_to_se3` |
|
|
132
|
+
| `se3ToVec` | `se3_to_vec` |
|
|
133
|
+
| `Adjoint` | `adjoint` |
|
|
134
|
+
| `ScrewToAxis` | `screw_axis` |
|
|
135
|
+
| `AxisAng6` | `axis_angle6` |
|
|
136
|
+
| `MatrixExp6` | `exp6` |
|
|
137
|
+
| `MatrixLog6` | `log6` |
|
|
138
|
+
| `ProjectToSO3` | `project_so3` |
|
|
139
|
+
| `ProjectToSE3` | `project_se3` |
|
|
140
|
+
| `DistanceToSO3` | `distance_so3` |
|
|
141
|
+
| `DistanceToSE3` | `distance_se3` |
|
|
142
|
+
| `TestIfSO3` | `is_so3` |
|
|
143
|
+
| `TestIfSE3` | `is_se3` |
|
|
144
|
+
| `FKinBody` | `fk_body` |
|
|
145
|
+
| `FKinSpace` | `fk_space` |
|
|
146
|
+
| `JacobianBody` | `jacobian_body` |
|
|
147
|
+
| `JacobianSpace` | `jacobian_space` |
|
|
148
|
+
| `IKinBody` | `ik_body` |
|
|
149
|
+
| `IKinSpace` | `ik_space` |
|
|
150
|
+
|
|
151
|
+
Where a screws function's signature matches MR's, the alias **is** that function
|
|
152
|
+
(`sc.FKinSpace is sc.fk_space`). `IKinBody` and `IKinSpace` are thin wrappers that return
|
|
153
|
+
MR's `(thetalist, success)` tuple; the primaries return an `IKResult` with the iteration
|
|
154
|
+
history. No deprecation warnings, ever.
|
|
155
|
+
|
|
156
|
+
## Licence
|
|
157
|
+
|
|
158
|
+
MIT. Portions derived from the Modern Robotics code library, copyright 2018 Huan Weng,
|
|
159
|
+
Bill Hunt, Jarvis Schultz and Mikhail Todes, MIT licence; see `LICENSE`.
|
screws-0.1.0/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# screws
|
|
2
|
+
|
|
3
|
+
Screw-theory robotics in Python, after Lynch and Park, *Modern Robotics* (MR).
|
|
4
|
+
`screws` is the MR code library reorganised: the same mathematics under
|
|
5
|
+
snake_case names, MR's own names kept as aliases, a `Robot` class, a URDF
|
|
6
|
+
loader, robots that ship ready to use, and a bridge to CoppeliaSim.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
uv add screws # the mathematics: numpy is the only dependency
|
|
12
|
+
uv add "screws[coppelia]" # plus the CoppeliaSim ZMQ remote API client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Four lines
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import screws as sc
|
|
19
|
+
|
|
20
|
+
ur5 = sc.robots.ur5() # M, screw axes, inertias from the textbook's URDF
|
|
21
|
+
T = ur5.fk([0.3, -1.2, 0.8, -0.4, 1.1, 0.2]) # a reachable, non-singular pose
|
|
22
|
+
result = ur5.ik(T, theta0=[0.1, -1.4, 0.1, 0.1, 1.4, 0.1]) # result.theta, result.converged, result.history
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The free functions are the reference implementation and read as the book does:
|
|
26
|
+
`sc.exp6(sc.vec_to_se3(S * theta))` is $e^{[\mathcal{S}]\theta}$, `sc.fk_space(M, S, theta)`
|
|
27
|
+
is the space form of the product of exponentials, and `sc.FKinSpace` is the very same
|
|
28
|
+
function under MR's name.
|
|
29
|
+
|
|
30
|
+
Screw-axis lists are **6xn, one axis per column**, as MR writes them. Nothing guesses the
|
|
31
|
+
orientation (a 6x6 array is ambiguous for a six-axis arm), so hand entry goes through a
|
|
32
|
+
sequence of 6-vectors: `sc.Robot.from_screw_axes(M, [S1, S2, ...])`.
|
|
33
|
+
|
|
34
|
+
## Check your own code against the library
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import numpy as np
|
|
38
|
+
import screws as sc
|
|
39
|
+
|
|
40
|
+
def my_exp6(se3mat): ... # your implementation
|
|
41
|
+
|
|
42
|
+
rng = np.random.default_rng(0)
|
|
43
|
+
cases = [sc.vec_to_se3(rng.normal(size=6)) for _ in range(20)]
|
|
44
|
+
sc.testing.check(my_exp6, sc.exp6, cases) # raises on the first disagreement
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## CoppeliaSim
|
|
48
|
+
|
|
49
|
+
Works with CoppeliaSim 4.9 or later through the ZMQ remote API (0.1 verified on 4.10.0).
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from screws.coppelia import Scene
|
|
53
|
+
|
|
54
|
+
with Scene() as scene: # connects to localhost:23000 in stepping mode
|
|
55
|
+
arm = scene.arm("/UR5") # the joints under that tree, base to tip
|
|
56
|
+
robot = arm.robot() # a screws.Robot read off the scene at zero
|
|
57
|
+
arm.mode("position")
|
|
58
|
+
log = scene.run(lambda t, theta, dtheta: theta_desired(t), duration=5.0, arm=arm)
|
|
59
|
+
log.plot()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`Scene` owns the connection and the clock (`start`, `step`, `stop`, `time`, `dt`,
|
|
63
|
+
`frame`, `show_frame`). `Arm` reads (`theta`, `dtheta`, `tau`, `tip_frame`) and commands
|
|
64
|
+
in one of three modes (`position`, `velocity`, `torque`), or `teleport`s without physics
|
|
65
|
+
to animate an IK history. `Arm.robot()` derives M and the screw axes from the scene's
|
|
66
|
+
joint frames (omega is the joint's z axis, v = -omega x q). Scene inertias arrive in 0.2.
|
|
67
|
+
|
|
68
|
+
## Two UR5s
|
|
69
|
+
|
|
70
|
+
`sc.robots.ur5()` is built from the URDF the textbook prints in section 4.2: the
|
|
71
|
+
manufacturer's lengths and the printed inertias, the closest published match to the
|
|
72
|
+
simulator's model. `sc.robots.ur5(source="textbook")` is the rounded table of MR Figure
|
|
73
|
+
4.6 with no inertias. They differ in the third decimal of the $v$ entries, and the course
|
|
74
|
+
notes' problems ask why.
|
|
75
|
+
|
|
76
|
+
## Names
|
|
77
|
+
|
|
78
|
+
| Modern Robotics | `screws` |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `NearZero` | `near_zero` |
|
|
81
|
+
| `Normalize` | `normalize` |
|
|
82
|
+
| `RotInv` | `rot_inv` |
|
|
83
|
+
| `VecToso3` | `vec_to_so3` |
|
|
84
|
+
| `so3ToVec` | `so3_to_vec` |
|
|
85
|
+
| `AxisAng3` | `axis_angle3` |
|
|
86
|
+
| `MatrixExp3` | `exp3` |
|
|
87
|
+
| `MatrixLog3` | `log3` |
|
|
88
|
+
| `RpToTrans` | `rp_to_transform` |
|
|
89
|
+
| `TransToRp` | `transform_to_rp` |
|
|
90
|
+
| `TransInv` | `transform_inv` |
|
|
91
|
+
| `VecTose3` | `vec_to_se3` |
|
|
92
|
+
| `se3ToVec` | `se3_to_vec` |
|
|
93
|
+
| `Adjoint` | `adjoint` |
|
|
94
|
+
| `ScrewToAxis` | `screw_axis` |
|
|
95
|
+
| `AxisAng6` | `axis_angle6` |
|
|
96
|
+
| `MatrixExp6` | `exp6` |
|
|
97
|
+
| `MatrixLog6` | `log6` |
|
|
98
|
+
| `ProjectToSO3` | `project_so3` |
|
|
99
|
+
| `ProjectToSE3` | `project_se3` |
|
|
100
|
+
| `DistanceToSO3` | `distance_so3` |
|
|
101
|
+
| `DistanceToSE3` | `distance_se3` |
|
|
102
|
+
| `TestIfSO3` | `is_so3` |
|
|
103
|
+
| `TestIfSE3` | `is_se3` |
|
|
104
|
+
| `FKinBody` | `fk_body` |
|
|
105
|
+
| `FKinSpace` | `fk_space` |
|
|
106
|
+
| `JacobianBody` | `jacobian_body` |
|
|
107
|
+
| `JacobianSpace` | `jacobian_space` |
|
|
108
|
+
| `IKinBody` | `ik_body` |
|
|
109
|
+
| `IKinSpace` | `ik_space` |
|
|
110
|
+
|
|
111
|
+
Where a screws function's signature matches MR's, the alias **is** that function
|
|
112
|
+
(`sc.FKinSpace is sc.fk_space`). `IKinBody` and `IKinSpace` are thin wrappers that return
|
|
113
|
+
MR's `(thetalist, success)` tuple; the primaries return an `IKResult` with the iteration
|
|
114
|
+
history. No deprecation warnings, ever.
|
|
115
|
+
|
|
116
|
+
## Licence
|
|
117
|
+
|
|
118
|
+
MIT. Portions derived from the Modern Robotics code library, copyright 2018 Huan Weng,
|
|
119
|
+
Bill Hunt, Jarvis Schultz and Mikhail Todes, MIT licence; see `LICENSE`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Releasing screws
|
|
2
|
+
|
|
3
|
+
1. `uv run ruff check .` and `uv run pytest -q` are clean.
|
|
4
|
+
2. With CoppeliaSim open (4.9 or later; 0.1 was verified on 4.10.0) and the stock UR5 model
|
|
5
|
+
loaded at `/UR5` (Model browser > robots > non-mobile > UR5.ttm; no tip dummy needed),
|
|
6
|
+
run the simulator tests: `SCREWS_COPPELIASIM=1 uv run pytest -q -m coppelia`.
|
|
7
|
+
If a run is killed mid-way the simulation can be left waiting on a dead stepping
|
|
8
|
+
client; `Scene.start()` stops such a simulation first, so a rerun recovers by itself.
|
|
9
|
+
3. Bump the version in `screws/_version.py` and `pyproject.toml` (they must agree;
|
|
10
|
+
`tests/test_readme_table.py` pins the release number).
|
|
11
|
+
4. If `screws/aliases.py` changed, regenerate the README table with
|
|
12
|
+
`uv run python tools/print_alias_table.py` and paste it under "Names".
|
|
13
|
+
5. If the `modern_robotics` dev pin changed, re-harvest the docstring examples:
|
|
14
|
+
`uv run python tools/harvest_mr_examples.py`.
|
|
15
|
+
6. Commit, then tag and push the tag: `git tag v<version> && git push origin v<version>`.
|
|
16
|
+
The `Publish to PyPI` workflow (`.github/workflows/publish.yml`) checks that the tag
|
|
17
|
+
matches `pyproject.toml` and `_version.py`, runs the suite, builds, and uploads.
|
|
18
|
+
It uses the `PYPI_API_TOKEN` repository secret when set, else PyPI trusted publishing
|
|
19
|
+
(register the workflow as a publisher for `screws` on pypi.org, environment `pypi`).
|
|
20
|
+
Fallback by hand: `uv build && uvx twine upload dist/*` (`uv publish` ignores `~/.pypirc`).
|
|
21
|
+
7. Watch it: `gh run watch`. Then pin the new version in the course repo.
|