franky-control 1.0.2__cp313-cp313t-manylinux_2_34_x86_64.whl → 1.1.1__cp313-cp313t-manylinux_2_34_x86_64.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.
- franky/__init__.py +7 -2
- franky/_franky.cpython-313t-x86_64-linux-gnu.so +0 -0
- franky/motion.py +8 -3
- franky/reaction.py +19 -8
- franky/robot_web_session.py +140 -51
- {franky_control-1.0.2.dist-info → franky_control-1.1.1.dist-info}/METADATA +288 -116
- franky_control-1.1.1.dist-info/RECORD +26 -0
- {franky_control-1.0.2.dist-info → franky_control-1.1.1.dist-info}/WHEEL +1 -1
- {franky_control-1.0.2.dist-info → franky_control-1.1.1.dist-info}/licenses/LICENSE +1 -1
- franky_control.libs/{libPocoFoundation-44c2a91d.so.95 → libPocoFoundation-44d67b9f.so.95} +0 -0
- franky_control.libs/{libPocoNet-2f9ce4bf.so.95 → libPocoNet-3bdfeb79.so.95} +0 -0
- franky_control.libs/libboost_filesystem-0bf67256.so.1.75.0 +0 -0
- franky_control.libs/{libboost_serialization-0b785d4d.so.1.75.0 → libboost_serialization-67da18f6.so.1.75.0} +0 -0
- franky_control.libs/{libfranka-976dfd01.so.0.15.0 → libfranka-e5e9247f.so.0.15.0} +0 -0
- franky_control.libs/{libpinocchio_default-b05a4796.so.3.1.0 → libpinocchio_default-def973fb.so.3.1.0} +0 -0
- franky_control.libs/{libpinocchio_parsers-74bdc3cc.so.3.1.0 → libpinocchio_parsers-d98f9e85.so.3.1.0} +0 -0
- franky_control-1.0.2.dist-info/RECORD +0 -26
- franky_control.libs/libboost_filesystem-42b111b5.so.1.75.0 +0 -0
- {franky_control-1.0.2.dist-info → franky_control-1.1.1.dist-info}/top_level.txt +0 -0
franky/__init__.py
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
from .robot import Robot
|
2
|
-
from .robot_web_session import
|
2
|
+
from .robot_web_session import (
|
3
|
+
RobotWebSession,
|
4
|
+
RobotWebSessionError,
|
5
|
+
FrankaAPIError,
|
6
|
+
TakeControlTimeoutError,
|
7
|
+
)
|
3
8
|
from .reaction import (
|
4
9
|
Reaction,
|
5
10
|
TorqueReaction,
|
6
11
|
JointVelocityReaction,
|
7
12
|
JointPositionReaction,
|
8
13
|
CartesianVelocityReaction,
|
9
|
-
CartesianPoseReaction
|
14
|
+
CartesianPoseReaction,
|
10
15
|
)
|
11
16
|
from .motion import Motion
|
12
17
|
from ._franky import *
|
Binary file
|
franky/motion.py
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
from typing import Union
|
2
2
|
|
3
|
-
from ._franky import
|
4
|
-
|
3
|
+
from ._franky import (
|
4
|
+
BaseCartesianPoseMotion,
|
5
|
+
BaseCartesianVelocityMotion,
|
6
|
+
BaseJointPositionMotion,
|
7
|
+
BaseJointVelocityMotion,
|
8
|
+
BaseTorqueMotion,
|
9
|
+
)
|
5
10
|
|
6
11
|
Motion = Union[
|
7
12
|
BaseCartesianPoseMotion,
|
8
13
|
BaseCartesianVelocityMotion,
|
9
14
|
BaseJointPositionMotion,
|
10
15
|
BaseJointVelocityMotion,
|
11
|
-
BaseTorqueMotion
|
16
|
+
BaseTorqueMotion,
|
12
17
|
]
|
franky/reaction.py
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
-
from ._franky import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
from ._franky import (
|
2
|
+
Condition,
|
3
|
+
BaseCartesianPoseMotion,
|
4
|
+
BaseCartesianVelocityMotion,
|
5
|
+
BaseJointPositionMotion,
|
6
|
+
BaseJointVelocityMotion,
|
7
|
+
BaseTorqueMotion,
|
8
|
+
CartesianPoseReaction as _CartesianPoseReaction,
|
9
|
+
CartesianVelocityReaction as _CartesianVelocityReaction,
|
10
|
+
JointPositionReaction as _JointPositionReaction,
|
11
|
+
JointVelocityReaction as _JointVelocityReaction,
|
12
|
+
TorqueReaction as _TorqueReaction,
|
13
|
+
)
|
8
14
|
|
9
15
|
from .motion import Motion
|
10
16
|
|
@@ -40,4 +46,9 @@ class TorqueReaction(_TorqueReaction, Reaction):
|
|
40
46
|
|
41
47
|
|
42
48
|
_REACTION_TYPES = [
|
43
|
-
CartesianPoseReaction,
|
49
|
+
CartesianPoseReaction,
|
50
|
+
CartesianVelocityReaction,
|
51
|
+
JointPositionReaction,
|
52
|
+
JointVelocityReaction,
|
53
|
+
TorqueReaction,
|
54
|
+
]
|
franky/robot_web_session.py
CHANGED
@@ -7,22 +7,37 @@ import time
|
|
7
7
|
import urllib.parse
|
8
8
|
from http.client import HTTPSConnection, HTTPResponse
|
9
9
|
from typing import Dict, Optional, Any, Literal
|
10
|
-
from urllib.error import HTTPError
|
11
10
|
|
12
11
|
|
13
|
-
class
|
14
|
-
|
12
|
+
class RobotWebSessionError(Exception):
|
13
|
+
pass
|
14
|
+
|
15
|
+
|
16
|
+
class FrankaAPIError(RobotWebSessionError):
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
target: str,
|
20
|
+
http_code: int,
|
21
|
+
http_reason: str,
|
22
|
+
headers: Dict[str, str],
|
23
|
+
message: str,
|
24
|
+
):
|
15
25
|
super().__init__(
|
16
|
-
f"Franka API returned error {http_code} ({http_reason}) when accessing end-point {target}: {message}"
|
26
|
+
f"Franka API returned error {http_code} ({http_reason}) when accessing end-point {target}: {message}"
|
27
|
+
)
|
17
28
|
self.target = target
|
18
29
|
self.http_code = http_code
|
19
30
|
self.headers = headers
|
20
31
|
self.message = message
|
21
32
|
|
22
33
|
|
34
|
+
class TakeControlTimeoutError(RobotWebSessionError):
|
35
|
+
pass
|
36
|
+
|
37
|
+
|
23
38
|
class RobotWebSession:
|
24
|
-
def __init__(self,
|
25
|
-
self.
|
39
|
+
def __init__(self, hostname: str, username: str, password: str):
|
40
|
+
self.__hostname = hostname
|
26
41
|
self.__username = username
|
27
42
|
self.__password = password
|
28
43
|
|
@@ -33,24 +48,45 @@ class RobotWebSession:
|
|
33
48
|
|
34
49
|
@staticmethod
|
35
50
|
def __encode_password(user: str, password: str) -> str:
|
36
|
-
bs = ",".join(
|
51
|
+
bs = ",".join(
|
52
|
+
[
|
53
|
+
str(b)
|
54
|
+
for b in hashlib.sha256(
|
55
|
+
(password + "#" + user + "@franka").encode("utf-8")
|
56
|
+
).digest()
|
57
|
+
]
|
58
|
+
)
|
37
59
|
return base64.encodebytes(bs.encode("utf-8")).decode("utf-8")
|
38
60
|
|
39
|
-
def _send_api_request(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
61
|
+
def _send_api_request(
|
62
|
+
self,
|
63
|
+
target: str,
|
64
|
+
headers: Optional[Dict[str, str]] = None,
|
65
|
+
body: Optional[Any] = None,
|
66
|
+
method: Literal["GET", "POST", "DELETE"] = "POST",
|
67
|
+
):
|
68
|
+
_headers = {"Cookie": f"authorization={self.__token}"}
|
44
69
|
if headers is not None:
|
45
70
|
_headers.update(headers)
|
46
71
|
self.__client.request(method, target, headers=_headers, body=body)
|
47
72
|
res: HTTPResponse = self.__client.getresponse()
|
48
73
|
if res.getcode() != 200:
|
49
|
-
raise FrankaAPIError(
|
74
|
+
raise FrankaAPIError(
|
75
|
+
target,
|
76
|
+
res.getcode(),
|
77
|
+
res.reason,
|
78
|
+
dict(res.headers),
|
79
|
+
res.read().decode("utf-8"),
|
80
|
+
)
|
50
81
|
return res.read()
|
51
82
|
|
52
|
-
def send_api_request(
|
53
|
-
|
83
|
+
def send_api_request(
|
84
|
+
self,
|
85
|
+
target: str,
|
86
|
+
headers: Optional[Dict[str, str]] = None,
|
87
|
+
body: Optional[Any] = None,
|
88
|
+
method: Literal["GET", "POST", "DELETE"] = "POST",
|
89
|
+
):
|
54
90
|
last_error = None
|
55
91
|
for i in range(3):
|
56
92
|
try:
|
@@ -59,28 +95,38 @@ class RobotWebSession:
|
|
59
95
|
last_error = ex
|
60
96
|
raise last_error
|
61
97
|
|
62
|
-
def send_control_api_request(
|
63
|
-
|
64
|
-
|
98
|
+
def send_control_api_request(
|
99
|
+
self,
|
100
|
+
target: str,
|
101
|
+
headers: Optional[Dict[str, str]] = None,
|
102
|
+
body: Optional[Any] = None,
|
103
|
+
method: Literal["GET", "POST", "DELETE"] = "POST",
|
104
|
+
):
|
65
105
|
if headers is None:
|
66
106
|
headers = {}
|
67
107
|
self.__check_control_token()
|
68
|
-
_headers = {
|
69
|
-
"X-Control-Token": self.__control_token
|
70
|
-
}
|
108
|
+
_headers = {"X-Control-Token": self.__control_token}
|
71
109
|
_headers.update(headers)
|
72
110
|
return self.send_api_request(target, headers=_headers, method=method, body=body)
|
73
111
|
|
74
|
-
def open(self):
|
112
|
+
def open(self, timeout: float = 30.0):
|
75
113
|
if self.is_open:
|
76
114
|
raise RuntimeError("Session is already open.")
|
77
|
-
self.__client = HTTPSConnection(
|
115
|
+
self.__client = HTTPSConnection(
|
116
|
+
self.__hostname, timeout=timeout, context=ssl._create_unverified_context()
|
117
|
+
)
|
78
118
|
self.__client.connect()
|
79
119
|
payload = json.dumps(
|
80
|
-
{
|
120
|
+
{
|
121
|
+
"login": self.__username,
|
122
|
+
"password": self.__encode_password(self.__username, self.__password),
|
123
|
+
}
|
124
|
+
)
|
81
125
|
self.__token = self.send_api_request(
|
82
|
-
"/admin/api/login",
|
83
|
-
|
126
|
+
"/admin/api/login",
|
127
|
+
headers={"content-type": "application/json"},
|
128
|
+
body=payload,
|
129
|
+
).decode("utf-8")
|
84
130
|
return self
|
85
131
|
|
86
132
|
def close(self):
|
@@ -92,82 +138,125 @@ class RobotWebSession:
|
|
92
138
|
self.__client.close()
|
93
139
|
|
94
140
|
def __enter__(self):
|
95
|
-
self.open()
|
141
|
+
return self.open()
|
96
142
|
|
97
143
|
def __exit__(self, type, value, traceback):
|
98
144
|
self.close()
|
99
145
|
|
100
146
|
def __check_control_token(self):
|
101
147
|
if self.__control_token is None:
|
102
|
-
raise RuntimeError(
|
148
|
+
raise RuntimeError(
|
149
|
+
"Client does not have control. Call take_control() first."
|
150
|
+
)
|
103
151
|
|
104
|
-
def take_control(self, wait_timeout: float =
|
105
|
-
if self.
|
152
|
+
def take_control(self, wait_timeout: float = 30.0, force: bool = False):
|
153
|
+
if not self.has_control():
|
106
154
|
res = self.send_api_request(
|
107
|
-
"/admin/api/control-token/request
|
108
|
-
|
155
|
+
f"/admin/api/control-token/request{'?force' if force else ''}",
|
156
|
+
headers={"content-type": "application/json"},
|
157
|
+
body=json.dumps({"requestedBy": self.__username}),
|
158
|
+
)
|
159
|
+
if force:
|
160
|
+
print(
|
161
|
+
"Forcibly taking control: "
|
162
|
+
f"Please physically take control by pressing the top button on the FR3 within {wait_timeout}s!"
|
163
|
+
)
|
109
164
|
response_dict = json.loads(res)
|
110
165
|
self.__control_token = response_dict["token"]
|
111
166
|
self.__control_token_id = response_dict["id"]
|
112
167
|
# One should probably use websockets here but that would introduce another dependency
|
113
168
|
start = time.time()
|
114
|
-
|
115
|
-
|
116
|
-
|
169
|
+
has_control = self.has_control()
|
170
|
+
while time.time() - start < wait_timeout and not has_control:
|
171
|
+
time.sleep(max(0.0, min(1.0, wait_timeout - (time.time() - start))))
|
172
|
+
has_control = self.has_control()
|
173
|
+
if not has_control:
|
174
|
+
raise TakeControlTimeoutError(
|
175
|
+
f"Timed out waiting for control to be granted after {wait_timeout}s."
|
176
|
+
)
|
117
177
|
|
118
178
|
def release_control(self):
|
119
179
|
if self.__control_token is not None:
|
120
180
|
self.send_control_api_request(
|
121
|
-
"/admin/api/control-token",
|
122
|
-
|
181
|
+
"/admin/api/control-token",
|
182
|
+
headers={"content-type": "application/json"},
|
183
|
+
method="DELETE",
|
184
|
+
body=json.dumps({"token": self.__control_token}),
|
185
|
+
)
|
123
186
|
self.__control_token = None
|
124
187
|
self.__control_token_id = None
|
125
188
|
|
126
189
|
def enable_fci(self):
|
127
190
|
self.send_control_api_request(
|
128
|
-
"/desk/api/system/fci",
|
129
|
-
|
191
|
+
"/desk/api/system/fci",
|
192
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
193
|
+
body=f"token={urllib.parse.quote(base64.b64encode(self.__control_token.encode('ascii')))}",
|
194
|
+
)
|
130
195
|
|
131
196
|
def has_control(self):
|
132
197
|
if self.__control_token_id is not None:
|
133
198
|
status = self.get_system_status()
|
134
199
|
active_token = status["controlToken"]["activeToken"]
|
135
|
-
return
|
200
|
+
return (
|
201
|
+
active_token is not None
|
202
|
+
and active_token["id"] == self.__control_token_id
|
203
|
+
)
|
136
204
|
return False
|
137
205
|
|
138
206
|
def start_task(self, task: str):
|
139
207
|
self.send_api_request(
|
140
|
-
"/desk/api/execution",
|
141
|
-
|
208
|
+
"/desk/api/execution",
|
209
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
210
|
+
body=f"id={task}",
|
211
|
+
)
|
142
212
|
|
143
213
|
def unlock_brakes(self):
|
144
214
|
self.send_control_api_request(
|
145
|
-
"/desk/api/joints/unlock",
|
215
|
+
"/desk/api/joints/unlock",
|
216
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
217
|
+
)
|
146
218
|
|
147
219
|
def lock_brakes(self):
|
148
220
|
self.send_control_api_request(
|
149
|
-
"/desk/api/joints/lock",
|
221
|
+
"/desk/api/joints/lock",
|
222
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
223
|
+
)
|
150
224
|
|
151
225
|
def set_mode_programming(self):
|
152
226
|
self.send_control_api_request(
|
153
|
-
"/desk/api/operating-mode/programming",
|
227
|
+
"/desk/api/operating-mode/programming",
|
228
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
229
|
+
)
|
154
230
|
|
155
231
|
def set_mode_execution(self):
|
156
232
|
self.send_control_api_request(
|
157
|
-
"/desk/api/operating-mode/execution",
|
233
|
+
"/desk/api/operating-mode/execution",
|
234
|
+
headers={"content-type": "application/x-www-form-urlencoded"},
|
235
|
+
)
|
158
236
|
|
159
237
|
def get_system_status(self):
|
160
|
-
return json.loads(
|
238
|
+
return json.loads(
|
239
|
+
self.send_api_request("/admin/api/system-status", method="GET").decode(
|
240
|
+
"utf-8"
|
241
|
+
)
|
242
|
+
)
|
161
243
|
|
162
244
|
def execute_self_test(self):
|
163
245
|
if self.get_system_status()["safety"]["recoverableErrors"]["td2Timeout"]:
|
164
246
|
self.send_control_api_request(
|
165
|
-
"/admin/api/safety/recoverable-safety-errors/acknowledge?error_id=TD2Timeout"
|
166
|
-
|
167
|
-
|
247
|
+
"/admin/api/safety/recoverable-safety-errors/acknowledge?error_id=TD2Timeout"
|
248
|
+
)
|
249
|
+
response = json.loads(
|
250
|
+
self.send_control_api_request(
|
251
|
+
"/admin/api/safety/td2-tests/execute",
|
252
|
+
headers={"content-type": "application/json"},
|
253
|
+
).decode("utf-8")
|
254
|
+
)
|
168
255
|
assert response["code"] == "SuccessResponse"
|
169
256
|
time.sleep(0.5)
|
170
|
-
while
|
257
|
+
while (
|
258
|
+
self.get_system_status()["safety"]["safetyControllerStatus"] == "SelfTest"
|
259
|
+
):
|
171
260
|
time.sleep(0.5)
|
172
261
|
|
173
262
|
@property
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: franky-control
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1.1
|
4
4
|
Summary: High-level control library for Franka robots.
|
5
5
|
Home-page: https://github.com/TimSchneider42/franky
|
6
6
|
Author: Tim Schneider
|
@@ -76,25 +76,25 @@ at [https://timschneider42.github.io/franky/](https://timschneider42.github.io/f
|
|
76
76
|
|
77
77
|
## 🚀 Features
|
78
78
|
|
79
|
-
- **Control your Franka robot directly from Python in just a few lines!**
|
79
|
+
- **Control your Franka robot directly from Python in just a few lines!**
|
80
80
|
No more endless hours setting up ROS, juggling packages, or untangling dependencies. Just `pip install` — no ROS at all.
|
81
81
|
|
82
|
-
- **[Four control modes](#motion-types)**: [Cartesian position](#cartesian-position-control), [Cartesian velocity](#cartesian-velocity-control), [Joint position](#joint-position-control), [Joint velocity](#joint-velocity-control)
|
82
|
+
- **[Four control modes](#motion-types)**: [Cartesian position](#cartesian-position-control), [Cartesian velocity](#cartesian-velocity-control), [Joint position](#joint-position-control), [Joint velocity](#joint-velocity-control)
|
83
83
|
Franky uses [Ruckig](https://github.com/pantor/ruckig) to generate smooth, time-optimal trajectories while respecting velocity, acceleration, and jerk limits.
|
84
84
|
|
85
85
|
- **[Real-time control from Python and C++](#real-time-motions)**
|
86
86
|
Need to change the target while the robot’s moving? No problem. Franky re-plans trajectories on the fly so that you can preempt motions anytime.
|
87
87
|
|
88
|
-
- **[Reactive behavior](#-real-time-reactions)**
|
88
|
+
- **[Reactive behavior](#-real-time-reactions)**
|
89
89
|
Robots don’t always go according to plan. Franky lets you define reactions to unexpected events—like contact with the environment — so you can change course in real-time.
|
90
90
|
|
91
|
-
- **[Motion and reaction callbacks](#motion-callbacks)**
|
91
|
+
- **[Motion and reaction callbacks](#motion-callbacks)**
|
92
92
|
Want to monitor what’s happening under the hood? Add callbacks to your motions and reactions. They won’t block the control thread and are super handy for debugging or logging.
|
93
93
|
|
94
|
-
- **Things are moving too fast? [Tune the robot's dynamics to your needs](#-robot)**
|
94
|
+
- **Things are moving too fast? [Tune the robot's dynamics to your needs](#-robot)**
|
95
95
|
Adjust max velocity, acceleration, and jerk to match your setup or task. Fine control for smooth, safe operation.
|
96
96
|
|
97
|
-
- **Full Python access to the libfranka API**
|
97
|
+
- **Full Python access to the libfranka API**
|
98
98
|
Want to tweak impedance, read the robot state, set force thresholds, or mess with the Jacobian? Go for it. If libfranka supports it, chances are Franky does, too.
|
99
99
|
|
100
100
|
## 📖 Python Quickstart Guide
|
@@ -109,6 +109,7 @@ otherwise, follow the [setup instructions](#setup) first.
|
|
109
109
|
|
110
110
|
Now we are already ready to go!
|
111
111
|
Unlock the brakes in the web interface, activate FCI, and start coding:
|
112
|
+
|
112
113
|
```python
|
113
114
|
from franky import *
|
114
115
|
|
@@ -208,6 +209,60 @@ pip install numpy
|
|
208
209
|
pip install --no-index --find-links=./dist franky-control
|
209
210
|
```
|
210
211
|
|
212
|
+
### Using Docker
|
213
|
+
|
214
|
+
To use Franky within Docker we provide a [Dockerfile](docker/run/Dockerfile) and
|
215
|
+
accompanying [docker-compose](docker-compose.yml) file.
|
216
|
+
|
217
|
+
```bash
|
218
|
+
git clone --recurse-submodules https://github.com/timschneider42/franky.git
|
219
|
+
cd franky/
|
220
|
+
docker compose build franky-run
|
221
|
+
```
|
222
|
+
|
223
|
+
To use another version of libfranka than the default (0.15.0) add a build argument:
|
224
|
+
|
225
|
+
```bash
|
226
|
+
docker compose build franky-run --build-arg LIBFRANKA_VERSION=0.9.2
|
227
|
+
```
|
228
|
+
|
229
|
+
To run the container:
|
230
|
+
|
231
|
+
```bash
|
232
|
+
docker compose run franky-run bash
|
233
|
+
```
|
234
|
+
|
235
|
+
The container requires access to the host machines network *and* elevated user rights to allow the docker user to set RT
|
236
|
+
capabilities of the processes run from within it.
|
237
|
+
|
238
|
+
### Can I use CUDA jointly with Franky?
|
239
|
+
|
240
|
+
Yes. However, you need to set `IGNORE_PREEMPT_RT_PRESENCE=1` during the installation and all subsequent updates of the CUDA drivers on the real-time kernel.
|
241
|
+
|
242
|
+
First, make sure that you have rebooted your system after installing the real-time kernel.
|
243
|
+
Then, add `IGNORE_PREEMPT_RT_PRESENCE=1` to `/etc/environment`, call `export IGNORE_PREEMPT_RT_PRESENCE=1` to also set it in the current session and follow the instructions of Nvidia to install CUDA on your system.
|
244
|
+
|
245
|
+
If you are on Ubuntu, you can also use [this](tools/install_cuda_realtime.bash) script to install CUDA on your real-time system:
|
246
|
+
```bash
|
247
|
+
# Download the script
|
248
|
+
wget https://raw.githubusercontent.com/timschneider42/franky/master/tools/install_cuda_realtime.bash
|
249
|
+
|
250
|
+
# Inspect the script to ensure it does what you expect
|
251
|
+
|
252
|
+
# Make it executable
|
253
|
+
chmod +x install_cuda_realtime.bash
|
254
|
+
|
255
|
+
# Execute the script
|
256
|
+
./install_cuda_realtime.bash
|
257
|
+
```
|
258
|
+
|
259
|
+
Alternatively, if you are a cowboy and do not care about security, you can also use this one-liner to directly call the script without checking it:
|
260
|
+
```bash
|
261
|
+
bash <(wget -qO- https://raw.githubusercontent.com/timschneider42/franky/master/tools/install_cuda_realtime.bash)
|
262
|
+
```
|
263
|
+
|
264
|
+
### Building Franky
|
265
|
+
|
211
266
|
Franky is based on [libfranka](https://github.com/frankaemika/libfranka), [Eigen](https://eigen.tuxfamily.org) for
|
212
267
|
transformation calculations and [pybind11](https://github.com/pybind/pybind11) for the Python bindings.
|
213
268
|
As the Franka is sensitive to acceleration discontinuities, it requires jerk-constrained motion generation, for which
|
@@ -238,32 +293,6 @@ pip install .
|
|
238
293
|
Make sure that the built library `_franky.cpython-3**-****-linux-gnu.so` is in the Python path, e.g. by adjusting
|
239
294
|
`PYTHONPATH` accordingly.
|
240
295
|
|
241
|
-
#### Using Docker
|
242
|
-
|
243
|
-
To use Franky within Docker we provide a [Dockerfile](docker/run/Dockerfile) and
|
244
|
-
accompanying [docker-compose](docker-compose.yml) file.
|
245
|
-
|
246
|
-
```bash
|
247
|
-
git clone --recurse-submodules https://github.com/timschneider42/franky.git
|
248
|
-
cd franky/
|
249
|
-
docker compose build franky-run
|
250
|
-
```
|
251
|
-
|
252
|
-
To use another version of libfranka than the default (0.15.0) add a build argument:
|
253
|
-
|
254
|
-
```bash
|
255
|
-
docker compose build franky-run --build-arg LIBFRANKA_VERSION=0.9.2
|
256
|
-
```
|
257
|
-
|
258
|
-
To run the container:
|
259
|
-
|
260
|
-
```bash
|
261
|
-
docker compose run franky-run bash
|
262
|
-
```
|
263
|
-
|
264
|
-
The container requires access to the host machines network *and* elevated user rights to allow the docker user to set RT
|
265
|
-
capabilities of the processes run from within it.
|
266
|
-
|
267
296
|
#### Building Franky with Docker
|
268
297
|
|
269
298
|
For building Franky and its wheels, we provide another Docker container that can also be launched using docker-compose:
|
@@ -311,6 +340,8 @@ motion = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)
|
|
311
340
|
robot.move(motion)
|
312
341
|
```
|
313
342
|
|
343
|
+
Before executing any code, make sure that you have enabled the Franka Control Interface (FCI) in the Franka UI web interface.
|
344
|
+
|
314
345
|
Furthermore, we will introduce methods for geometric calculations, for moving the robot according to different motion
|
315
346
|
types, how to implement real-time reactions and changing waypoints in real time as well as controlling the gripper.
|
316
347
|
|
@@ -352,7 +383,9 @@ robot.recover_from_errors()
|
|
352
383
|
robot.relative_dynamics_factor = 0.05
|
353
384
|
|
354
385
|
# Alternatively, you can define each constraint individually
|
355
|
-
robot.relative_dynamics_factor = RelativeDynamicsFactor(
|
386
|
+
robot.relative_dynamics_factor = RelativeDynamicsFactor(
|
387
|
+
velocity=0.1, acceleration=0.05, jerk=0.1
|
388
|
+
)
|
356
389
|
|
357
390
|
# Or, for more finegrained access, set individual limits
|
358
391
|
robot.translation_velocity_limit.set(3.0)
|
@@ -384,7 +417,7 @@ The robot state can be retrieved by accessing the following properties:
|
|
384
417
|
obtained
|
385
418
|
from [franka::RobotState::O_T_EE](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a193781d47722b32925e0ea7ac415f442)
|
386
419
|
and [franka::RobotState::O_dP_EE_c](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a4be112bd1a9a7d777a67aea4a18a8dcc).
|
387
|
-
* `
|
420
|
+
* `current_joint_state`: Object of type `franky.JointState`, which contains the joint positions and velocities
|
388
421
|
obtained
|
389
422
|
from [franka::RobotState::q](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#ade3335d1ac2f6c44741a916d565f7091)
|
390
423
|
and [franka::RobotState::dq](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a706045af1b176049e9e56df755325bd2).
|
@@ -420,7 +453,8 @@ ee_pose_kin = robot.model.pose(Frame.EndEffector, q, f_t_ee, ee_t_k)
|
|
420
453
|
# Get the jacobian of the current robot state
|
421
454
|
jacobian = robot.model.body_jacobian(Frame.EndEffector, state)
|
422
455
|
|
423
|
-
# Alternatively, just get the URDF as string and do the kinematics computation yourself (only
|
456
|
+
# Alternatively, just get the URDF as string and do the kinematics computation yourself (only
|
457
|
+
# for libfranka >= 0.15.0)
|
424
458
|
urdf_model = robot.model_urdf
|
425
459
|
```
|
426
460
|
|
@@ -430,8 +464,7 @@ and [Model](https://timschneider42.github.io/franky/classfranky_1_1_model.html)
|
|
430
464
|
|
431
465
|
### <a id="motion-types" /> 🏃♂️ Motion Types
|
432
466
|
|
433
|
-
Franky currently supports four different impedance control modes: **joint position control**, **joint velocity control
|
434
|
-
**, **cartesian position control**, and **cartesian velocity control**.
|
467
|
+
Franky currently supports four different impedance control modes: **joint position control**, **joint velocity control**, **cartesian position control**, and **cartesian velocity control**.
|
435
468
|
Each of these control modes is invoked by passing the robot an appropriate _Motion_ object.
|
436
469
|
|
437
470
|
In the following, we provide a brief example for each motion type implemented by Franky in Python.
|
@@ -449,28 +482,37 @@ from franky import *
|
|
449
482
|
# A point-to-point motion in the joint space
|
450
483
|
m_jp1 = JointMotion([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7])
|
451
484
|
|
452
|
-
# A motion in joint space with multiple waypoints
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
])
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
485
|
+
# A motion in joint space with multiple waypoints. The robot will stop at each of these
|
486
|
+
# waypoints. If you want the robot to move continuously, you have to specify a target velocity
|
487
|
+
# at every waypoint as shown in the example following this one.
|
488
|
+
m_jp2 = JointWaypointMotion(
|
489
|
+
[
|
490
|
+
JointWaypoint([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7]),
|
491
|
+
JointWaypoint([0.0, 0.3, 0.3, -1.5, -0.2, 1.5, 0.8]),
|
492
|
+
JointWaypoint([0.1, 0.4, 0.3, -1.4, -0.3, 1.7, 0.9]),
|
493
|
+
]
|
494
|
+
)
|
495
|
+
|
496
|
+
# Intermediate waypoints also permit to specify target velocities. The default target velocity
|
497
|
+
# is 0, meaning that the robot will stop at every waypoint.
|
498
|
+
m_jp3 = JointWaypointMotion(
|
499
|
+
[
|
500
|
+
JointWaypoint([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7]),
|
501
|
+
JointWaypoint(
|
502
|
+
JointState(
|
503
|
+
position=[0.0, 0.3, 0.3, -1.5, -0.2, 1.5, 0.8],
|
504
|
+
velocity=[0.1, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0],
|
505
|
+
)
|
506
|
+
),
|
507
|
+
JointWaypoint([0.1, 0.4, 0.3, -1.4, -0.3, 1.7, 0.9]),
|
508
|
+
]
|
509
|
+
)
|
510
|
+
|
511
|
+
# Stop the robot in joint position control mode. The difference of JointStopMotion to other
|
512
|
+
# stop-motions such as CartesianStopMotion is that JointStopMotion stops the robot in joint
|
513
|
+
# position control mode while CartesianStopMotion stops it in cartesian pose control mode. The
|
514
|
+
# difference becomes relevant when asynchronous move commands are being sent or reactions are
|
515
|
+
# being used(see below).
|
474
516
|
m_jp4 = JointStopMotion()
|
475
517
|
```
|
476
518
|
|
@@ -480,18 +522,30 @@ m_jp4 = JointStopMotion()
|
|
480
522
|
from franky import *
|
481
523
|
|
482
524
|
# Accelerate to the given joint velocity and hold it. After 1000ms stop the robot again.
|
483
|
-
m_jv1 = JointVelocityMotion(
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
#
|
488
|
-
#
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
525
|
+
m_jv1 = JointVelocityMotion(
|
526
|
+
[0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], duration=Duration(1000)
|
527
|
+
)
|
528
|
+
|
529
|
+
# Joint velocity motions also support waypoints. Unlike in joint position control, a joint
|
530
|
+
# velocity waypoint is a target velocity to be reached. This particular example first
|
531
|
+
# accelerates the joints, holds the velocity for 1s, then reverses direction for 2s, reverses
|
532
|
+
# direction again for 1s, and finally stops. It is important not to forget to stop the robot
|
533
|
+
# at the end of such a sequence, as it will otherwise throw an error.
|
534
|
+
m_jv2 = JointVelocityWaypointMotion(
|
535
|
+
[
|
536
|
+
JointVelocityWaypoint(
|
537
|
+
[0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], hold_target_duration=Duration(1000)
|
538
|
+
),
|
539
|
+
JointVelocityWaypoint(
|
540
|
+
[-0.1, -0.3, 0.1, -0.0, -0.1, 0.2, -0.4],
|
541
|
+
hold_target_duration=Duration(2000),
|
542
|
+
),
|
543
|
+
JointVelocityWaypoint(
|
544
|
+
[0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], hold_target_duration=Duration(1000)
|
545
|
+
),
|
546
|
+
JointVelocityWaypoint([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
|
547
|
+
]
|
548
|
+
)
|
495
549
|
|
496
550
|
# Stop the robot in joint velocity control mode.
|
497
551
|
m_jv3 = JointVelocityStopMotion()
|
@@ -509,29 +563,44 @@ quat = Rotation.from_euler("xyz", [0, 0, math.pi / 2]).as_quat()
|
|
509
563
|
m_cp1 = CartesianMotion(Affine([0.4, -0.2, 0.3], quat))
|
510
564
|
|
511
565
|
# With target elbow angle (otherwise, the Franka firmware will choose by itself)
|
512
|
-
m_cp2 = CartesianMotion(
|
566
|
+
m_cp2 = CartesianMotion(
|
567
|
+
RobotPose(Affine([0.4, -0.2, 0.3], quat), elbow_state=ElbowState(0.3))
|
568
|
+
)
|
513
569
|
|
514
570
|
# A linear motion in cartesian space relative to the initial position
|
515
|
-
# (Note that this motion is relative both in position and orientation. Hence, when the robot's
|
516
|
-
# differently, it will move in a different direction)
|
571
|
+
# (Note that this motion is relative both in position and orientation. Hence, when the robot's
|
572
|
+
# end-effector is oriented differently, it will move in a different direction)
|
517
573
|
m_cp3 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)
|
518
574
|
|
519
|
-
# Generalization of CartesianMotion that allows for multiple waypoints
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
575
|
+
# Generalization of CartesianMotion that allows for multiple waypoints. The robot will stop at
|
576
|
+
# each of these waypoints. If you want the robot to move continuously, you have to specify a
|
577
|
+
# target velocity at every waypoint as shown in the example following this one.
|
578
|
+
m_cp4 = CartesianWaypointMotion(
|
579
|
+
[
|
580
|
+
CartesianWaypoint(
|
581
|
+
RobotPose(Affine([0.4, -0.2, 0.3], quat), elbow_state=ElbowState(0.3))
|
582
|
+
),
|
583
|
+
# The following waypoint is relative to the prior one and 50% slower
|
584
|
+
CartesianWaypoint(
|
585
|
+
Affine([0.2, 0.0, 0.0]),
|
586
|
+
ReferenceType.Relative,
|
587
|
+
RelativeDynamicsFactor(0.5, 1.0, 1.0),
|
588
|
+
),
|
589
|
+
]
|
590
|
+
)
|
591
|
+
|
592
|
+
# Cartesian waypoints permit to specify target velocities
|
593
|
+
m_cp5 = CartesianWaypointMotion(
|
594
|
+
[
|
595
|
+
CartesianWaypoint(Affine([0.5, -0.2, 0.3], quat)),
|
596
|
+
CartesianWaypoint(
|
597
|
+
CartesianState(
|
598
|
+
pose=Affine([0.4, -0.1, 0.3], quat), velocity=Twist([-0.01, 0.01, 0.0])
|
599
|
+
)
|
600
|
+
),
|
601
|
+
CartesianWaypoint(Affine([0.3, 0.0, 0.3], quat)),
|
602
|
+
]
|
603
|
+
)
|
535
604
|
|
536
605
|
# Stop the robot in cartesian position control mode.
|
537
606
|
m_cp6 = CartesianStopMotion()
|
@@ -542,22 +611,37 @@ m_cp6 = CartesianStopMotion()
|
|
542
611
|
```python
|
543
612
|
from franky import *
|
544
613
|
|
545
|
-
# A cartesian velocity motion with linear (first argument) and angular (second argument)
|
614
|
+
# A cartesian velocity motion with linear (first argument) and angular (second argument)
|
615
|
+
# components
|
546
616
|
m_cv1 = CartesianVelocityMotion(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]))
|
547
617
|
|
548
618
|
# With target elbow velocity
|
549
|
-
m_cv2 = CartesianVelocityMotion(
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
# velocity
|
554
|
-
#
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
619
|
+
m_cv2 = CartesianVelocityMotion(
|
620
|
+
RobotVelocity(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]), elbow_velocity=-0.2)
|
621
|
+
)
|
622
|
+
|
623
|
+
# Cartesian velocity motions also support multiple waypoints. Unlike in cartesian position
|
624
|
+
# control, a cartesian velocity waypoint is a target velocity to be reached. This particular
|
625
|
+
# example first accelerates the end-effector, holds the velocity for 1s, then reverses
|
626
|
+
# direction for 2s, reverses direction again for 1s, and finally stops. It is important not to
|
627
|
+
# forget to stop the robot at the end of such a sequence, as it will otherwise throw an error.
|
628
|
+
m_cv4 = CartesianVelocityWaypointMotion(
|
629
|
+
[
|
630
|
+
CartesianVelocityWaypoint(
|
631
|
+
Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]),
|
632
|
+
hold_target_duration=Duration(1000),
|
633
|
+
),
|
634
|
+
CartesianVelocityWaypoint(
|
635
|
+
Twist([-0.2, 0.1, -0.1], [-0.1, 0.1, -0.2]),
|
636
|
+
hold_target_duration=Duration(2000),
|
637
|
+
),
|
638
|
+
CartesianVelocityWaypoint(
|
639
|
+
Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]),
|
640
|
+
hold_target_duration=Duration(1000),
|
641
|
+
),
|
642
|
+
CartesianVelocityWaypoint(Twist()),
|
643
|
+
]
|
644
|
+
)
|
561
645
|
|
562
646
|
# Stop the robot in cartesian velocity control mode.
|
563
647
|
m_cv6 = CartesianVelocityStopMotion()
|
@@ -586,14 +670,15 @@ The real robot can be moved by applying a motion to the robot using `move`:
|
|
586
670
|
```python
|
587
671
|
# Before moving the robot, set an appropriate dynamics factor. We start small:
|
588
672
|
robot.relative_dynamics_factor = 0.05
|
589
|
-
# or alternatively, to control the scaling of velocity, acceleration, and jerk limits
|
673
|
+
# or alternatively, to control the scaling of velocity, acceleration, and jerk limits
|
674
|
+
# separately:
|
590
675
|
robot.relative_dynamics_factor = RelativeDynamicsFactor(0.05, 0.1, 0.15)
|
591
676
|
# If these values are set too high, you will see discontinuity errors
|
592
677
|
|
593
678
|
robot.move(m_jp1)
|
594
679
|
|
595
|
-
# We can also set a relative dynamics factor in the move command. It will be multiplied with
|
596
|
-
# dynamics factors (robot and motion if present).
|
680
|
+
# We can also set a relative dynamics factor in the move command. It will be multiplied with
|
681
|
+
# the other relative dynamics factors (robot and motion if present).
|
597
682
|
robot.move(m_jp2, relative_dynamics_factor=0.8)
|
598
683
|
```
|
599
684
|
|
@@ -608,7 +693,8 @@ def cb(
|
|
608
693
|
time_step: Duration,
|
609
694
|
rel_time: Duration,
|
610
695
|
abs_time: Duration,
|
611
|
-
control_signal: JointPositions
|
696
|
+
control_signal: JointPositions,
|
697
|
+
):
|
612
698
|
print(f"At time {abs_time}, the target joint positions were {control_signal.q}")
|
613
699
|
|
614
700
|
|
@@ -633,9 +719,10 @@ from franky import CartesianMotion, Affine, ReferenceType, Measure, Reaction
|
|
633
719
|
|
634
720
|
motion = CartesianMotion(Affine([0.0, 0.0, 0.1]), ReferenceType.Relative) # Move down 10cm
|
635
721
|
|
636
|
-
# It is important that the reaction motion uses the same control mode as the original motion.
|
637
|
-
# a JointMotion as a reaction motion to a CartesianMotion.
|
638
|
-
|
722
|
+
# It is important that the reaction motion uses the same control mode as the original motion.
|
723
|
+
# Hence, we cannot register a JointMotion as a reaction motion to a CartesianMotion.
|
724
|
+
# Move up for 1cm
|
725
|
+
reaction_motion = CartesianMotion(Affine([0.0, 0.0, 0.01]), ReferenceType.Relative)
|
639
726
|
|
640
727
|
# Trigger reaction if the Z force is greater than 30N
|
641
728
|
reaction = Reaction(Measure.FORCE_Z > 30.0, reaction_motion)
|
@@ -698,7 +785,8 @@ them.
|
|
698
785
|
In C++ you can additionally use lambdas to define more complex behaviours:
|
699
786
|
|
700
787
|
```c++
|
701
|
-
auto motion = CartesianMotion(
|
788
|
+
auto motion = CartesianMotion(
|
789
|
+
RobotPose(Affine({0.0, 0.0, 0.2}), 0.0), ReferenceType::Relative);
|
702
790
|
|
703
791
|
// Stop motion if force is over 10N
|
704
792
|
auto stop_motion = StopMotion<franka::CartesianPose>()
|
@@ -717,7 +805,7 @@ motion
|
|
717
805
|
}),
|
718
806
|
[](const franka::RobotState& state, double rel_time, double abs_time) {
|
719
807
|
// Lambda reaction motion generator
|
720
|
-
// (we are just returning a stop motion, but there could be arbitrary
|
808
|
+
// (we are just returning a stop motion, but there could be arbitrary
|
721
809
|
// logic here for generating reaction motions)
|
722
810
|
return StopMotion<franka::CartesianPose>();
|
723
811
|
})
|
@@ -743,8 +831,8 @@ motion1 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)
|
|
743
831
|
robot.move(motion1, asynchronous=True)
|
744
832
|
|
745
833
|
time.sleep(0.5)
|
746
|
-
# Note that similar to reactions, when preempting active motions with new motions, the
|
747
|
-
# Hence, we cannot use, e.g., a JointMotion here.
|
834
|
+
# Note that similar to reactions, when preempting active motions with new motions, the
|
835
|
+
# control mode cannot change. Hence, we cannot use, e.g., a JointMotion here.
|
748
836
|
motion2 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)
|
749
837
|
robot.move(motion2, asynchronous=True)
|
750
838
|
```
|
@@ -762,7 +850,7 @@ The next time `Robot.join_motion` or `Robot.move` are called, they will throw th
|
|
762
850
|
Hence, after an asynchronous motion has finished, make sure to call `Robot.join_motion` to ensure being notified of any
|
763
851
|
exceptions that occurred during the motion.
|
764
852
|
|
765
|
-
### Gripper
|
853
|
+
### <a id="gripper" /> 👌 Gripper
|
766
854
|
|
767
855
|
In the `franky::Gripper` class, the default gripper force and gripper speed can be set.
|
768
856
|
Then, additionally to the libfranka commands, the following helper methods can be used:
|
@@ -837,6 +925,81 @@ else:
|
|
837
925
|
print("Gripper motion timed out.")
|
838
926
|
```
|
839
927
|
|
928
|
+
### Accessing the Web Interface API
|
929
|
+
|
930
|
+
For Franka robots, control happens via the Franka Control Interface (FCI), which has to be enabled through the Franka UI in the robot's web interface.
|
931
|
+
The Franka UI also provides methods for locking and unlocking the brakes, setting the execution mode, and executing the safety self-test.
|
932
|
+
However, sometimes you may want to access these methods programmatically, e.g. for automatically unlocking the brakes before starting a motion, or automatically executing the self-test after 24h of continuous execution.
|
933
|
+
|
934
|
+
For that reason, Franky provides a `RobotWebSession` class that allows you to access the web interface API of the robot.
|
935
|
+
Note that directly accessing the web interface API is not officially supported and documented by Franka.
|
936
|
+
Hence, use this feature at your own risk.
|
937
|
+
|
938
|
+
A typical automated workflow could look like this:
|
939
|
+
|
940
|
+
```python
|
941
|
+
import franky
|
942
|
+
|
943
|
+
with franky.RobotWebSession("172.16.0.2", "username", "password") as robot_web_session:
|
944
|
+
# First take control
|
945
|
+
try:
|
946
|
+
# Try taking control. The session currently holding control has to release it in order
|
947
|
+
# for this session to gain control. In the web interface, a notification will show
|
948
|
+
# prompting the user to release control. If the other session is another
|
949
|
+
# franky.RobotWebSession, then the `release_control` method can be called on the other
|
950
|
+
# session to release control.
|
951
|
+
robot_web_session.take_control(wait_timeout=10.0)
|
952
|
+
except franky.TakeControlTimeoutError:
|
953
|
+
# If nothing happens for 10s, we try to take control forcefully. This is particularly
|
954
|
+
# useful if the session holding control is dead. Taking control by force requires the
|
955
|
+
# user to manually push the blue button close to the robot's wrist.
|
956
|
+
robot_web_session.take_control(wait_timeout=30.0, force=True)
|
957
|
+
|
958
|
+
# Unlock the brakes
|
959
|
+
robot_web_session.unlock_brakes()
|
960
|
+
|
961
|
+
# Enable the FCI
|
962
|
+
robot_web_session.enable_fci()
|
963
|
+
|
964
|
+
# Create a franky.Robot instance and do whatever you want
|
965
|
+
...
|
966
|
+
|
967
|
+
# Disable the FCI
|
968
|
+
robot_web_session.disable_fci()
|
969
|
+
|
970
|
+
# Lock brakes
|
971
|
+
robot_web_session.lock_brakes()
|
972
|
+
```
|
973
|
+
|
974
|
+
In case you are running the robot for longer than 24h you will have noticed that you have to do a safety self-test every 24h.
|
975
|
+
`RobotWebSession` allows to automate this task as well:
|
976
|
+
|
977
|
+
```python
|
978
|
+
import time
|
979
|
+
import franky
|
980
|
+
|
981
|
+
with franky.RobotWebSession("172.16.0.2", "username", "password") as robot_web_session:
|
982
|
+
# Execute self-test if the time until self-test is less than 5 minutes.
|
983
|
+
if robot_web_session.get_system_status()["safety"]["timeToTd2"] < 300:
|
984
|
+
robot_web_session.disable_fci()
|
985
|
+
robot_web_session.lock_brakes()
|
986
|
+
time.sleep(1.0)
|
987
|
+
|
988
|
+
robot_web_session.execute_self_test()
|
989
|
+
|
990
|
+
robot_web_session.unlock_brakes()
|
991
|
+
robot_web_session.enable_fci()
|
992
|
+
time.sleep(1.0)
|
993
|
+
|
994
|
+
# Recreate your franky.Robot instance as the FCI has been disabled and re-enabled
|
995
|
+
...
|
996
|
+
```
|
997
|
+
|
998
|
+
`robot_web_session.get_system_status()` contains more information than just the time until self-test, such as the current execution mode, whether the brakes are locked, whether the FCI is enabled, and more.
|
999
|
+
|
1000
|
+
If you want to call other API functions, you can use the `RobotWebSession.send_api_request` and `RobotWebSession.send_control_api_request` methods.
|
1001
|
+
See [robot_web_session.py](franky/robot_web_session.py) for an example of how to use these methods.
|
1002
|
+
|
840
1003
|
## 🛠️ Development
|
841
1004
|
|
842
1005
|
Franky is currently tested against following versions
|
@@ -879,3 +1042,12 @@ Aside of bug fixes and general performance improvements, Franky provides the fol
|
|
879
1042
|
* Franky supports [joint velocity control](#joint-velocity-control)
|
880
1043
|
and [cartesian velocity control](#cartesian-velocity-control)
|
881
1044
|
* The dynamics limits are not hard-coded anymore but can be [set for each robot instance](#-robot).
|
1045
|
+
|
1046
|
+
## Contributing
|
1047
|
+
|
1048
|
+
If you wish to contribute to this project, you are welcome to create a pull request.
|
1049
|
+
Please run the [pre-commit](https://pre-commit.com/) hooks before submitting your pull request.
|
1050
|
+
To install the pre-commit hooks, run:
|
1051
|
+
|
1052
|
+
1. [Install pre-commit](https://pre-commit.com/#install)
|
1053
|
+
2. Install the Git hooks by running `pre-commit install` or, alternatively, run `pre-commit run --all-files manually.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
franky/__init__.py,sha256=57BsmUytdeqMrhHglwvxMS6CgpAEVPw0KJge8_UTzEA,378
|
2
|
+
franky/_franky.cpython-313t-x86_64-linux-gnu.so,sha256=nd_-MebEr05KJPBSgGPr6C3w9hFCYifs44yHCr-YVyk,3078057
|
3
|
+
franky/_franky.pyi,sha256=odBnBaM2qGN4mUa8WvFN5SL9aszPgDTsZj5Jb9UkFHk,60047
|
4
|
+
franky/motion.py,sha256=TWsx2Ba9iYG9e-OxpuYZlVn1wdzKS-Zvz6ayHT_HYXQ,354
|
5
|
+
franky/reaction.py,sha256=cBuHAMek9yH-9Zmwfa0dzz2t5G_1SQEWXNdW7mhhCI8,1519
|
6
|
+
franky/robot.py,sha256=NaTNh4PaJshbz44eibvLCMt6O_Ak7TRVjhUR6AGay9A,236
|
7
|
+
franky/robot_web_session.py,sha256=rYpnUe4b8xj5JjCYxNRZ0uYz8lL2qfxKglcImFDw24Y,9070
|
8
|
+
franky_control.libs/libPocoFoundation-44d67b9f.so.95,sha256=6sSpoT5WNjvmbsEdjYvp5uu8nhEx-elAH67Gw8I2twA,3269633
|
9
|
+
franky_control.libs/libPocoNet-3bdfeb79.so.95,sha256=_ZbdxzOFhIIiSHA79e8yKNyCjfGVFbzYFvHqaKnqoFc,2482737
|
10
|
+
franky_control.libs/libboost_filesystem-0bf67256.so.1.75.0,sha256=MV-QRMY_pZ0zReU4H4AbcpyZ_sc6yZ6QR6QdDxF84Qc,139961
|
11
|
+
franky_control.libs/libboost_serialization-67da18f6.so.1.75.0,sha256=Wjn9rKqoXLrVK29NShhm_evaRsbhTFvvGydi8gwr5hs,319177
|
12
|
+
franky_control.libs/libconsole_bridge-acce180d.so.1.0,sha256=-iSKX9xNyLCSSrducf7rDx6hXzdU5v7xQqZDulppIP8,22769
|
13
|
+
franky_control.libs/libfmt-677b7a5c.so.8.1.1,sha256=rN3uzEn_limS_u6DV-BxdTEbs2GrhlicD_OjyAlKYpA,140649
|
14
|
+
franky_control.libs/libfranka-e5e9247f.so.0.15.0,sha256=rJDob_PoDQHpC78J-rhR67oqlhmlTnS2Red0bClsi2I,973497
|
15
|
+
franky_control.libs/libpinocchio_default-def973fb.so.3.1.0,sha256=cMnEW8NgbxqYEpdZH0Ymp-B5SEKzGiKq4GA0BcZ-JV4,9668545
|
16
|
+
franky_control.libs/libpinocchio_parsers-d98f9e85.so.3.1.0,sha256=HbwrdisSDbC2mXCz1NVhACUIaXconvI3i-BN1W9qJuE,707441
|
17
|
+
franky_control.libs/libtinyxml-69e5f0dd.so.0.2.6.2,sha256=V45jOzaljaUrtOAYBS20QBFHn0qPFHTEcWxdcOBoJNA,118121
|
18
|
+
franky_control.libs/liburdfdom_model-9ec0392f.so.1.0,sha256=Kwlq8DuENLdEX5URKcwUC06sl9rxUYoRjGx3EI-uRiY,146081
|
19
|
+
franky_control.libs/liburdfdom_model_state-01125232.so.1.0,sha256=LNMEG27nv_6BYvelpjXjr0YsBl-6IsGQfg3uG0DvTQQ,20857
|
20
|
+
franky_control.libs/liburdfdom_sensor-011819fa.so.1.0,sha256=LzFH_sfeyWcV042TmgSEXTQL1c0teyYg3veA8sTn_EM,20857
|
21
|
+
franky_control.libs/liburdfdom_world-4d53bc81.so.1.0,sha256=IziPhPYdFfZTgKDOnUMGLYSCsZRxYfC1rEKWjvzrpb4,146081
|
22
|
+
franky_control-1.1.1.dist-info/METADATA,sha256=qWzLTDDTUNcAXq0Fwd1Q_WKklCocvIg1RhaxlP7VLPI,41222
|
23
|
+
franky_control-1.1.1.dist-info/WHEEL,sha256=0dV9LHK2yEXG4HLEl3ZFnhHVbMh51BQdSri40CzSKg4,114
|
24
|
+
franky_control-1.1.1.dist-info/top_level.txt,sha256=LdDKRvHTODs6ll6Ow5b-gmWqt4NJffHxL83MRTCv_Ag,22
|
25
|
+
franky_control-1.1.1.dist-info/RECORD,,
|
26
|
+
franky_control-1.1.1.dist-info/licenses/LICENSE,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
|
@@ -162,4 +162,4 @@ General Public License ever published by the Free Software Foundation.
|
|
162
162
|
whether future versions of the GNU Lesser General Public License shall
|
163
163
|
apply, that proxy's public statement of acceptance of any version is
|
164
164
|
permanent authorization for you to choose that version for the
|
165
|
-
Library.
|
165
|
+
Library.
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,26 +0,0 @@
|
|
1
|
-
franky/__init__.py,sha256=lmRG8cc4eX-0gZgCkxxVCOrmuUEGJsppWl7DDURib-Y,293
|
2
|
-
franky/_franky.cpython-313t-x86_64-linux-gnu.so,sha256=xfxK0pTJSk9bT6oYt1_bLjzUVeQKMzyTbfQJ1ea8Zlk,3069865
|
3
|
-
franky/_franky.pyi,sha256=odBnBaM2qGN4mUa8WvFN5SL9aszPgDTsZj5Jb9UkFHk,60047
|
4
|
-
franky/motion.py,sha256=EZmZPvu2r4YBhcokV1PFh8s8NmiwOUERvct3Zz629C8,334
|
5
|
-
franky/reaction.py,sha256=gDD7kVc_7Et2NwMmCB4Hcd12aK0eygJVqXzZ9Dzw0_M,1488
|
6
|
-
franky/robot.py,sha256=NaTNh4PaJshbz44eibvLCMt6O_Ak7TRVjhUR6AGay9A,236
|
7
|
-
franky/robot_web_session.py,sha256=zOTAqDF4PbZLqJFr3RcuBwfWRSEMiw0Eag1stk3GWKE,7622
|
8
|
-
franky_control.libs/libPocoFoundation-44c2a91d.so.95,sha256=kMB_IJ_gYGE1jprdF_RIVEbhg9gQH3IimwBUksadJNc,3265609
|
9
|
-
franky_control.libs/libPocoNet-2f9ce4bf.so.95,sha256=wwgasubJUNFpW5brGQoUiyCEx6NgPQSG2Vjgk66OQ0s,2482737
|
10
|
-
franky_control.libs/libboost_filesystem-42b111b5.so.1.75.0,sha256=8tanNbQ7I455iTdjcNXuKf8snHpXq6N7SVvnX5H-_bY,139961
|
11
|
-
franky_control.libs/libboost_serialization-0b785d4d.so.1.75.0,sha256=sOHF1ncSl_Hg4oBqbyEuQCYAE9IaREqfm-dspP-O5mw,319177
|
12
|
-
franky_control.libs/libconsole_bridge-acce180d.so.1.0,sha256=-iSKX9xNyLCSSrducf7rDx6hXzdU5v7xQqZDulppIP8,22769
|
13
|
-
franky_control.libs/libfmt-677b7a5c.so.8.1.1,sha256=rN3uzEn_limS_u6DV-BxdTEbs2GrhlicD_OjyAlKYpA,140649
|
14
|
-
franky_control.libs/libfranka-976dfd01.so.0.15.0,sha256=q6EJaLfNrYHNcwJvov9s2sM18ahCZ2LgKZwZTbvDbok,973497
|
15
|
-
franky_control.libs/libpinocchio_default-b05a4796.so.3.1.0,sha256=NFyO0kOaOcpnFYVAehcgJj5spee4weJ_PqPZ5uUqpEg,9668545
|
16
|
-
franky_control.libs/libpinocchio_parsers-74bdc3cc.so.3.1.0,sha256=4yyz3Gw4SoyFEtvfx546_UMpH3GN9SjVWsWH28eCVSs,707441
|
17
|
-
franky_control.libs/libtinyxml-69e5f0dd.so.0.2.6.2,sha256=V45jOzaljaUrtOAYBS20QBFHn0qPFHTEcWxdcOBoJNA,118121
|
18
|
-
franky_control.libs/liburdfdom_model-9ec0392f.so.1.0,sha256=Kwlq8DuENLdEX5URKcwUC06sl9rxUYoRjGx3EI-uRiY,146081
|
19
|
-
franky_control.libs/liburdfdom_model_state-01125232.so.1.0,sha256=LNMEG27nv_6BYvelpjXjr0YsBl-6IsGQfg3uG0DvTQQ,20857
|
20
|
-
franky_control.libs/liburdfdom_sensor-011819fa.so.1.0,sha256=LzFH_sfeyWcV042TmgSEXTQL1c0teyYg3veA8sTn_EM,20857
|
21
|
-
franky_control.libs/liburdfdom_world-4d53bc81.so.1.0,sha256=IziPhPYdFfZTgKDOnUMGLYSCsZRxYfC1rEKWjvzrpb4,146081
|
22
|
-
franky_control-1.0.2.dist-info/METADATA,sha256=1gnubmBB-s5sz4OwqTP2hsLiArGIB1cl6b0C4ekISFQ,35135
|
23
|
-
franky_control-1.0.2.dist-info/WHEEL,sha256=sCv38u2NUOUpvqWDZFT1fIJCvaiGqDTI62C5hFQzWco,114
|
24
|
-
franky_control-1.0.2.dist-info/top_level.txt,sha256=LdDKRvHTODs6ll6Ow5b-gmWqt4NJffHxL83MRTCv_Ag,22
|
25
|
-
franky_control-1.0.2.dist-info/RECORD,,
|
26
|
-
franky_control-1.0.2.dist-info/licenses/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
Binary file
|
File without changes
|