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.
Files changed (28) hide show
  1. {moteus-0.3.72 → moteus-0.3.77}/PKG-INFO +1 -8
  2. {moteus-0.3.72 → moteus-0.3.77}/moteus/calibrate_encoder.py +13 -4
  3. {moteus-0.3.72 → moteus-0.3.77}/moteus/export.py +3 -1
  4. {moteus-0.3.72 → moteus-0.3.77}/moteus/moteus.py +103 -0
  5. {moteus-0.3.72 → moteus-0.3.77}/moteus/moteus_tool.py +568 -153
  6. {moteus-0.3.72 → moteus-0.3.77}/moteus/multiplex.py +68 -0
  7. {moteus-0.3.72 → moteus-0.3.77}/moteus/pythoncan.py +5 -0
  8. {moteus-0.3.72 → moteus-0.3.77}/moteus/version.py +1 -1
  9. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/PKG-INFO +1 -8
  10. {moteus-0.3.72 → moteus-0.3.77}/setup.py +1 -1
  11. {moteus-0.3.72 → moteus-0.3.77}/README.md +0 -0
  12. {moteus-0.3.72 → moteus-0.3.77}/moteus/__init__.py +0 -0
  13. {moteus-0.3.72 → moteus-0.3.77}/moteus/aioserial.py +0 -0
  14. {moteus-0.3.72 → moteus-0.3.77}/moteus/aiostream.py +0 -0
  15. {moteus-0.3.72 → moteus-0.3.77}/moteus/command.py +0 -0
  16. {moteus-0.3.72 → moteus-0.3.77}/moteus/fdcanusb.py +0 -0
  17. {moteus-0.3.72 → moteus-0.3.77}/moteus/posix_aioserial.py +0 -0
  18. {moteus-0.3.72 → moteus-0.3.77}/moteus/reader.py +0 -0
  19. {moteus-0.3.72 → moteus-0.3.77}/moteus/regression.py +0 -0
  20. {moteus-0.3.72 → moteus-0.3.77}/moteus/router.py +0 -0
  21. {moteus-0.3.72 → moteus-0.3.77}/moteus/transport.py +0 -0
  22. {moteus-0.3.72 → moteus-0.3.77}/moteus/win32_aioserial.py +0 -0
  23. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/SOURCES.txt +0 -0
  24. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/dependency_links.txt +0 -0
  25. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/entry_points.txt +0 -0
  26. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/requires.txt +3 -3
  27. {moteus-0.3.72 → moteus-0.3.77}/moteus.egg-info/top_level.txt +0 -0
  28. {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.72
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].startswith("CAL start"):
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].startswith("CAL done"):
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]