moteus 0.3.72__tar.gz → 0.3.77__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.
- {moteus-0.3.72 → moteus-0.3.77}/PKG-INFO +1 -8
- {moteus-0.3.72 → moteus-0.3.77}/moteus/calibrate_encoder.py +13 -4
- {moteus-0.3.72 → moteus-0.3.77}/moteus/export.py +3 -1
- {moteus-0.3.72 → moteus-0.3.77}/moteus/moteus.py +103 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/moteus_tool.py +568 -153
- {moteus-0.3.72 → moteus-0.3.77}/moteus/multiplex.py +68 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/pythoncan.py +5 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/version.py +1 -1
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/PKG-INFO +1 -8
- {moteus-0.3.72 → moteus-0.3.77}/setup.py +1 -1
- {moteus-0.3.72 → moteus-0.3.77}/README.md +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/__init__.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/aioserial.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/aiostream.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/command.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/fdcanusb.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/posix_aioserial.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/reader.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/regression.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/router.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/transport.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus/win32_aioserial.py +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/SOURCES.txt +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/dependency_links.txt +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/entry_points.txt +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/requires.txt +3 -3
- {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/top_level.txt +0 -0
- {moteus-0.3.72 → moteus-0.3.77}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: moteus
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.77
|
4
4
|
Summary: moteus brushless controller library and tools
|
5
5
|
Home-page: https://github.com/mjbots/moteus
|
6
6
|
Author: mjbots Robotic Systems
|
@@ -12,13 +12,6 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
13
13
|
Requires-Python: >=3.7, <4
|
14
14
|
Description-Content-Type: text/markdown
|
15
|
-
Requires-Dist: pyserial>=3.5
|
16
|
-
Requires-Dist: python-can>=3.3
|
17
|
-
Requires-Dist: pyelftools>=0.26
|
18
|
-
Requires-Dist: scipy>=1.8.0
|
19
|
-
Requires-Dist: importlib_metadata>=3.6
|
20
|
-
Requires-Dist: pywin32; platform_system == "Windows"
|
21
|
-
Requires-Dist: numpy<2
|
22
15
|
|
23
16
|
# Python bindings for moteus brushless controller #
|
24
17
|
|
@@ -53,11 +53,15 @@ def _parse_entry(line):
|
|
53
53
|
return result
|
54
54
|
|
55
55
|
|
56
|
+
def check_line(cmd):
|
57
|
+
return ' '.join(cmd.strip().split(' ')[0:2])
|
58
|
+
|
59
|
+
|
56
60
|
def parse_file(fp):
|
57
61
|
lines = [x.decode('latin1') for x in fp.readlines()]
|
58
|
-
if not lines[0]
|
62
|
+
if not check_line(lines[0]) in ["CAL start", "CALI start"]:
|
59
63
|
raise RuntimeError("calibration does not start with magic line")
|
60
|
-
if not lines[-1]
|
64
|
+
if not check_line(lines[-1]) in ["CAL done", "CALI done"]:
|
61
65
|
raise RuntimeError("calibration does not end with magic line")
|
62
66
|
|
63
67
|
lines = lines[1:-1]
|
@@ -65,8 +69,8 @@ def parse_file(fp):
|
|
65
69
|
entries = [_parse_entry(line) for line in lines]
|
66
70
|
|
67
71
|
result = File()
|
68
|
-
result.phase_up = [x for x in entries if x.direction == 1]
|
69
|
-
result.phase_down = [x for x in entries if x.direction == 2]
|
72
|
+
result.phase_up = [x for x in entries if x.direction == 1 or x.direction == 3]
|
73
|
+
result.phase_down = [x for x in entries if x.direction == 2 or x.direction == 4]
|
70
74
|
|
71
75
|
return result
|
72
76
|
|
@@ -181,6 +185,8 @@ class CalibrationResult:
|
|
181
185
|
|
182
186
|
self.fit_metric = None
|
183
187
|
|
188
|
+
self.current_quality_factor = None
|
189
|
+
|
184
190
|
self.errors = []
|
185
191
|
|
186
192
|
def __repr__(self):
|
@@ -190,6 +196,7 @@ class CalibrationResult:
|
|
190
196
|
"poles": self.poles,
|
191
197
|
"offset": self.offset,
|
192
198
|
"fit_metric": self.fit_metric,
|
199
|
+
"current_quality_factor": self.current_quality_factor,
|
193
200
|
"errors": self.errors,
|
194
201
|
})
|
195
202
|
|
@@ -200,6 +207,7 @@ class CalibrationResult:
|
|
200
207
|
'poles': self.poles,
|
201
208
|
'offset': self.offset,
|
202
209
|
'fit_metric': self.fit_metric,
|
210
|
+
'current_quality_factor': self.current_quality_factor,
|
203
211
|
}
|
204
212
|
|
205
213
|
|
@@ -382,6 +390,7 @@ def calibrate(parsed,
|
|
382
390
|
(starting_metric > 30 or
|
383
391
|
starting_errors > 0))):
|
384
392
|
print()
|
393
|
+
print(f"Initial metric: {starting_metric}")
|
385
394
|
# Optimize these initial offsets.
|
386
395
|
optimres = scipy.optimize.minimize(metric, offset, tol=1e1)
|
387
396
|
|
@@ -25,6 +25,7 @@ ALL = [
|
|
25
25
|
'TRANSPORT_FACTORIES',
|
26
26
|
'INT8', 'INT16', 'INT32', 'F32', 'IGNORE',
|
27
27
|
'reader',
|
28
|
+
'RegisterParser', 'QueryParser',
|
28
29
|
]
|
29
30
|
from moteus.command import Command
|
30
31
|
from moteus.fdcanusb import Fdcanusb
|
@@ -36,7 +37,8 @@ from moteus.moteus import (
|
|
36
37
|
Controller, Register, Mode, QueryResolution, PositionResolution, Stream,
|
37
38
|
make_transport_args, get_singleton_transport,
|
38
39
|
TRANSPORT_FACTORIES)
|
39
|
-
from moteus.multiplex import (INT8, INT16, INT32, F32, IGNORE
|
40
|
+
from moteus.multiplex import (INT8, INT16, INT32, F32, IGNORE,
|
41
|
+
RegisterParser, QueryParser)
|
40
42
|
import moteus.reader as reader
|
41
43
|
import moteus.aiostream as aiostream
|
42
44
|
|
@@ -252,6 +252,17 @@ class Register(enum.IntEnum):
|
|
252
252
|
MILLISECOND_COUNTER = 0x070
|
253
253
|
CLOCK_TRIM = 0x071
|
254
254
|
|
255
|
+
AUX1_PWM1 = 0x076,
|
256
|
+
AUX1_PWM2 = 0x077,
|
257
|
+
AUX1_PWM3 = 0x078,
|
258
|
+
AUX1_PWM4 = 0x079,
|
259
|
+
AUX1_PWM5 = 0x07a,
|
260
|
+
AUX2_PWM1 = 0x07b,
|
261
|
+
AUX2_PWM2 = 0x07c,
|
262
|
+
AUX2_PWM3 = 0x07d,
|
263
|
+
AUX2_PWM4 = 0x07e,
|
264
|
+
AUX2_PWM5 = 0x07f,
|
265
|
+
|
255
266
|
REGISTER_MAP_VERSION = 0x102
|
256
267
|
SERIAL_NUMBER = 0x120
|
257
268
|
SERIAL_NUMBER1 = 0x120
|
@@ -267,6 +278,16 @@ class Register(enum.IntEnum):
|
|
267
278
|
DRIVER_FAULT1 = 0x140
|
268
279
|
DRIVER_FAULT2 = 0x141
|
269
280
|
|
281
|
+
UUID1 = 0x150
|
282
|
+
UUID2 = 0x151
|
283
|
+
UUID3 = 0x152
|
284
|
+
UUID4 = 0x153
|
285
|
+
|
286
|
+
UUID_MASK1 = 0x0154
|
287
|
+
UUID_MASK2 = 0x0155
|
288
|
+
UUID_MASK3 = 0x0156
|
289
|
+
UUID_MASK4 = 0x0157
|
290
|
+
|
270
291
|
|
271
292
|
class Mode(enum.IntEnum):
|
272
293
|
"""Valid values for the Register.MODE register"""
|
@@ -347,6 +368,19 @@ class CurrentResolution:
|
|
347
368
|
q_A = mp.F32
|
348
369
|
|
349
370
|
|
371
|
+
class PwmResolution:
|
372
|
+
aux1_pwm1 = mp.INT16
|
373
|
+
aux1_pwm2 = mp.INT16
|
374
|
+
aux1_pwm3 = mp.INT16
|
375
|
+
aux1_pwm4 = mp.INT16
|
376
|
+
aux1_pwm5 = mp.INT16
|
377
|
+
aux2_pwm1 = mp.INT16
|
378
|
+
aux2_pwm2 = mp.INT16
|
379
|
+
aux2_pwm3 = mp.INT16
|
380
|
+
aux2_pwm4 = mp.INT16
|
381
|
+
aux2_pwm5 = mp.INT16
|
382
|
+
|
383
|
+
|
350
384
|
class Parser(mp.RegisterParser):
|
351
385
|
def read_position(self, resolution):
|
352
386
|
return self.read_mapped(resolution, 0.01, 0.0001, 0.00001)
|
@@ -507,6 +541,9 @@ def parse_register(parser, register, resolution):
|
|
507
541
|
return parser.read_int(resolution)
|
508
542
|
elif register == Register.CLOCK_TRIM:
|
509
543
|
return parser.read_int(resolution)
|
544
|
+
elif (register >= Register.AUX1_PWM1 and
|
545
|
+
register <= Register.AUX2_PWM5):
|
546
|
+
return parser.read_pwm(resolution)
|
510
547
|
else:
|
511
548
|
# We don't know what kind of value this is, so we don't know
|
512
549
|
# the units.
|
@@ -609,6 +646,7 @@ class Controller:
|
|
609
646
|
position_resolution=PositionResolution(),
|
610
647
|
vfoc_resolution=VFOCResolution(),
|
611
648
|
current_resolution=CurrentResolution(),
|
649
|
+
pwm_resolution=PwmResolution(),
|
612
650
|
transport=None,
|
613
651
|
can_prefix=0x0000):
|
614
652
|
self.id = id
|
@@ -616,6 +654,7 @@ class Controller:
|
|
616
654
|
self.position_resolution = position_resolution
|
617
655
|
self.vfoc_resolution = vfoc_resolution
|
618
656
|
self.current_resolution = current_resolution
|
657
|
+
self.pwm_resolution = pwm_resolution
|
619
658
|
self.transport = transport
|
620
659
|
self._parser = make_parser(id)
|
621
660
|
self._can_prefix = can_prefix
|
@@ -1315,6 +1354,70 @@ class Controller:
|
|
1315
1354
|
async def set_trim(self, *args, **kwargs):
|
1316
1355
|
return await self.execute(self.make_set_trim(*args, **kwargs))
|
1317
1356
|
|
1357
|
+
def make_aux_pwm(self, *,
|
1358
|
+
aux1_pwm1=None,
|
1359
|
+
aux1_pwm2=None,
|
1360
|
+
aux1_pwm3=None,
|
1361
|
+
aux1_pwm4=None,
|
1362
|
+
aux1_pwm5=None,
|
1363
|
+
aux2_pwm1=None,
|
1364
|
+
aux2_pwm2=None,
|
1365
|
+
aux2_pwm3=None,
|
1366
|
+
aux2_pwm4=None,
|
1367
|
+
aux2_pwm5=None,
|
1368
|
+
query=False,
|
1369
|
+
query_override=None):
|
1370
|
+
result = self._make_command(query=query, query_override=query_override)
|
1371
|
+
|
1372
|
+
pr = self.pwm_resolution
|
1373
|
+
resolutions = [
|
1374
|
+
pr.aux1_pwm1 if aux1_pwm1 is not None else mp.IGNORE,
|
1375
|
+
pr.aux1_pwm2 if aux1_pwm2 is not None else mp.IGNORE,
|
1376
|
+
pr.aux1_pwm3 if aux1_pwm3 is not None else mp.IGNORE,
|
1377
|
+
pr.aux1_pwm4 if aux1_pwm4 is not None else mp.IGNORE,
|
1378
|
+
pr.aux1_pwm5 if aux1_pwm5 is not None else mp.IGNORE,
|
1379
|
+
pr.aux2_pwm1 if aux2_pwm1 is not None else mp.IGNORE,
|
1380
|
+
pr.aux2_pwm2 if aux2_pwm2 is not None else mp.IGNORE,
|
1381
|
+
pr.aux2_pwm3 if aux2_pwm3 is not None else mp.IGNORE,
|
1382
|
+
pr.aux2_pwm4 if aux2_pwm4 is not None else mp.IGNORE,
|
1383
|
+
pr.aux2_pwm5 if aux2_pwm5 is not None else mp.IGNORE,
|
1384
|
+
]
|
1385
|
+
|
1386
|
+
data_buf = io.BytesIO()
|
1387
|
+
writer = Writer(data_buf)
|
1388
|
+
combiner = mp.WriteCombiner(
|
1389
|
+
writer, 0x00, int(Register.AUX1_PWM1), resolutions)
|
1390
|
+
|
1391
|
+
if combiner.maybe_write():
|
1392
|
+
writer.write_pwm(aux1_pwm1, pr.aux1_pwm1)
|
1393
|
+
if combiner.maybe_write():
|
1394
|
+
writer.write_pwm(aux1_pwm2, pr.aux1_pwm2)
|
1395
|
+
if combiner.maybe_write():
|
1396
|
+
writer.write_pwm(aux1_pwm3, pr.aux1_pwm3)
|
1397
|
+
if combiner.maybe_write():
|
1398
|
+
writer.write_pwm(aux1_pwm4, pr.aux1_pwm4)
|
1399
|
+
if combiner.maybe_write():
|
1400
|
+
writer.write_pwm(aux1_pwm5, pr.aux1_pwm5)
|
1401
|
+
if combiner.maybe_write():
|
1402
|
+
writer.write_pwm(aux2_pwm1, pr.aux2_pwm1)
|
1403
|
+
if combiner.maybe_write():
|
1404
|
+
writer.write_pwm(aux2_pwm2, pr.aux2_pwm2)
|
1405
|
+
if combiner.maybe_write():
|
1406
|
+
writer.write_pwm(aux2_pwm3, pr.aux2_pwm3)
|
1407
|
+
if combiner.maybe_write():
|
1408
|
+
writer.write_pwm(aux2_pwm4, pr.aux2_pwm4)
|
1409
|
+
if combiner.maybe_write():
|
1410
|
+
writer.write_pwm(aux2_pwm5, pr.aux2_pwm5)
|
1411
|
+
|
1412
|
+
self._format_query(query, query_override, data_buf, result)
|
1413
|
+
|
1414
|
+
result.data = data_buf.getvalue()
|
1415
|
+
|
1416
|
+
return result
|
1417
|
+
|
1418
|
+
async def set_aux_pwm(self, *args, **kwargs):
|
1419
|
+
return await self.execute(self.make_aux_pwm(*args, **kwargs))
|
1420
|
+
|
1318
1421
|
def _extract(self, value):
|
1319
1422
|
if len(value):
|
1320
1423
|
return value[0]
|