moteus 0.3.57__py3-none-any.whl → 0.3.59__py3-none-any.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.
- moteus/command.py +1 -0
- moteus/fdcanusb.py +23 -1
- moteus/moteus.py +131 -50
- moteus/moteus_tool.py +16 -15
- moteus/multiplex.py +19 -0
- moteus/pythoncan.py +24 -2
- moteus/version.py +1 -1
- {moteus-0.3.57.dist-info → moteus-0.3.59.dist-info}/METADATA +1 -1
- {moteus-0.3.57.dist-info → moteus-0.3.59.dist-info}/RECORD +12 -12
- {moteus-0.3.57.dist-info → moteus-0.3.59.dist-info}/WHEEL +0 -0
- {moteus-0.3.57.dist-info → moteus-0.3.59.dist-info}/entry_points.txt +0 -0
- {moteus-0.3.57.dist-info → moteus-0.3.59.dist-info}/top_level.txt +0 -0
moteus/command.py
CHANGED
@@ -19,6 +19,7 @@ class Command():
|
|
19
19
|
reply_required = False
|
20
20
|
data = b''
|
21
21
|
can_prefix = 0x0000 # a 13 bit CAN prefix
|
22
|
+
expected_reply_size = 0
|
22
23
|
|
23
24
|
# If True, then the following parameters are used directly instead
|
24
25
|
# of being calculated from destination and source (i.e. for
|
moteus/fdcanusb.py
CHANGED
@@ -173,8 +173,11 @@ class Fdcanusb:
|
|
173
173
|
bus_id = (command.destination |
|
174
174
|
(0x8000 if command.reply_required else 0) |
|
175
175
|
(command.can_prefix << 16))
|
176
|
+
hexdata = _hexify(command.data)
|
177
|
+
on_wire_size = self._round_up_dlc(len(command.data))
|
178
|
+
hexdata += '50' * (on_wire_size - len(command.data))
|
176
179
|
cmd = "can send {:04x} {}{}\n".format(
|
177
|
-
bus_id,
|
180
|
+
bus_id, hexdata, self._send_flags).encode('latin1')
|
178
181
|
self._serial.write(cmd)
|
179
182
|
if self._debug_log:
|
180
183
|
self._debug_log.write(f'{time.time()} > '.encode('latin1') +
|
@@ -197,3 +200,22 @@ class Fdcanusb:
|
|
197
200
|
message.data = _dehexify(fields[2])
|
198
201
|
message.arbitration_id = int(fields[1], 16)
|
199
202
|
return message
|
203
|
+
|
204
|
+
def _round_up_dlc(self, size):
|
205
|
+
if size <= 8:
|
206
|
+
return size
|
207
|
+
if size <= 12:
|
208
|
+
return 12
|
209
|
+
if size <= 16:
|
210
|
+
return 16
|
211
|
+
if size <= 20:
|
212
|
+
return 20
|
213
|
+
if size <= 24:
|
214
|
+
return 24
|
215
|
+
if size <= 32:
|
216
|
+
return 32
|
217
|
+
if size <= 48:
|
218
|
+
return 48
|
219
|
+
if size <= 64:
|
220
|
+
return 64
|
221
|
+
return size
|
moteus/moteus.py
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
import asyncio
|
16
16
|
import argparse
|
17
|
+
import copy
|
17
18
|
import enum
|
18
19
|
import importlib_metadata
|
19
20
|
import io
|
@@ -606,7 +607,7 @@ class Controller:
|
|
606
607
|
self._can_prefix = can_prefix
|
607
608
|
|
608
609
|
# Pre-compute our query string.
|
609
|
-
self._query_data = self._make_query_data()
|
610
|
+
self._query_data, self._default_query_reply_size = self._make_query_data()
|
610
611
|
|
611
612
|
def _get_transport(self):
|
612
613
|
if self.transport:
|
@@ -617,10 +618,15 @@ class Controller:
|
|
617
618
|
self.transport = get_singleton_transport()
|
618
619
|
return self.transport
|
619
620
|
|
620
|
-
def _make_query_data(self):
|
621
|
+
def _make_query_data(self, query_resolution=None):
|
622
|
+
if query_resolution is None:
|
623
|
+
query_resolution = self.query_resolution
|
624
|
+
|
625
|
+
expected_reply_size = 0
|
626
|
+
|
621
627
|
buf = io.BytesIO()
|
622
628
|
writer = Writer(buf)
|
623
|
-
qr =
|
629
|
+
qr = query_resolution
|
624
630
|
c1 = mp.WriteCombiner(writer, 0x10, int(Register.MODE), [
|
625
631
|
qr.mode,
|
626
632
|
qr.position,
|
@@ -633,6 +639,8 @@ class Controller:
|
|
633
639
|
for i in range(c1.size()):
|
634
640
|
c1.maybe_write()
|
635
641
|
|
642
|
+
expected_reply_size += c1.reply_size
|
643
|
+
|
636
644
|
c2 = mp.WriteCombiner(writer, 0x10, int(Register.MOTOR_TEMPERATURE), [
|
637
645
|
qr.motor_temperature,
|
638
646
|
qr.trajectory_complete,
|
@@ -644,6 +652,8 @@ class Controller:
|
|
644
652
|
for i in range(c2.size()):
|
645
653
|
c2.maybe_write()
|
646
654
|
|
655
|
+
expected_reply_size += c2.reply_size
|
656
|
+
|
647
657
|
c3 = mp.WriteCombiner(writer, 0x10, int(Register.AUX1_GPIO_STATUS), [
|
648
658
|
qr.aux1_gpio,
|
649
659
|
qr.aux2_gpio,
|
@@ -651,6 +661,8 @@ class Controller:
|
|
651
661
|
for i in range(c3.size()):
|
652
662
|
c3.maybe_write()
|
653
663
|
|
664
|
+
expected_reply_size += c3.reply_size
|
665
|
+
|
654
666
|
if len(qr._extra):
|
655
667
|
min_val = int(min(qr._extra.keys()))
|
656
668
|
max_val = int(max(qr._extra.keys()))
|
@@ -660,23 +672,41 @@ class Controller:
|
|
660
672
|
for i in range(min_val, max_val +1)])
|
661
673
|
for _ in range(c4.size()):
|
662
674
|
c4.maybe_write()
|
675
|
+
expected_reply_size += c4.reply_size
|
676
|
+
|
677
|
+
return buf.getvalue(), expected_reply_size
|
663
678
|
|
664
|
-
|
679
|
+
def _format_query(self, query, query_override, data_buf, result):
|
680
|
+
if query_override is not None:
|
681
|
+
query_data, expected_reply_size = \
|
682
|
+
self._make_query_data(query_override)
|
683
|
+
data_buf.write(query_data)
|
684
|
+
result.expected_reply_size = expected_reply_size
|
685
|
+
elif query:
|
686
|
+
data_buf.write(self._query_data)
|
687
|
+
result.expected_reply_size = self._default_query_reply_size
|
665
688
|
|
666
|
-
def _make_command(self, *, query, source=0):
|
689
|
+
def _make_command(self, *, query, query_override=None, source=0):
|
667
690
|
result = cmd.Command()
|
668
691
|
|
669
692
|
result.destination = self.id
|
670
693
|
result.source = source
|
671
|
-
result.reply_required = query
|
694
|
+
result.reply_required = query or (query_override is not None)
|
672
695
|
result.parse = self._parser
|
673
696
|
result.can_prefix = self._can_prefix
|
697
|
+
result.expected_reply_size = self._default_query_reply_size if query else 0
|
674
698
|
|
675
699
|
return result
|
676
700
|
|
677
|
-
def make_query(self):
|
678
|
-
result = self._make_command(
|
679
|
-
|
701
|
+
def make_query(self, query_override=None):
|
702
|
+
result = self._make_command(
|
703
|
+
query=True, query_override=query_override)
|
704
|
+
if query_override:
|
705
|
+
result.data, result.expected_reply_size = \
|
706
|
+
self._make_query_data(query_override)
|
707
|
+
else:
|
708
|
+
result.data = self._query_data
|
709
|
+
result.expected_reply_size = self._default_query_reply_size
|
680
710
|
return result;
|
681
711
|
|
682
712
|
async def query(self, **kwargs):
|
@@ -702,16 +732,18 @@ class Controller:
|
|
702
732
|
c.maybe_write()
|
703
733
|
|
704
734
|
result.data = buf.getvalue()
|
735
|
+
result.expected_reply_size = c.reply_size
|
705
736
|
return result
|
706
737
|
|
707
738
|
async def custom_query(self, *args, **kwargs):
|
708
739
|
return await self.execute(self.make_custom_query(*args, **kwargs))
|
709
740
|
|
710
|
-
def make_stop(self, *, query=False):
|
741
|
+
def make_stop(self, *, query=False, query_override=None):
|
711
742
|
"""Return a moteus.Command structure with data necessary to send a
|
712
743
|
stop mode command."""
|
713
744
|
|
714
|
-
result = self._make_command(
|
745
|
+
result = self._make_command(
|
746
|
+
query=query, query_override=query_override)
|
715
747
|
|
716
748
|
data_buf = io.BytesIO()
|
717
749
|
writer = Writer(data_buf)
|
@@ -719,8 +751,7 @@ class Controller:
|
|
719
751
|
writer.write_int8(int(Register.MODE))
|
720
752
|
writer.write_int8(int(Mode.STOPPED))
|
721
753
|
|
722
|
-
|
723
|
-
data_buf.write(self._query_data)
|
754
|
+
self._format_query(query, query_override, data_buf, result)
|
724
755
|
|
725
756
|
result.data = data_buf.getvalue()
|
726
757
|
|
@@ -732,12 +763,14 @@ class Controller:
|
|
732
763
|
def make_set_output(self, *,
|
733
764
|
position=0.0,
|
734
765
|
query=False,
|
766
|
+
query_override=None,
|
735
767
|
cmd=None
|
736
768
|
):
|
737
769
|
"""Return a moteus.Command structure with data necessary to send a
|
738
770
|
set output nearest command."""
|
739
771
|
|
740
|
-
result = self._make_command(
|
772
|
+
result = self._make_command(
|
773
|
+
query=query, query_override=query_override)
|
741
774
|
|
742
775
|
data_buf = io.BytesIO()
|
743
776
|
writer = Writer(data_buf)
|
@@ -745,23 +778,26 @@ class Controller:
|
|
745
778
|
writer.write_varuint(cmd)
|
746
779
|
writer.write_f32(position)
|
747
780
|
|
748
|
-
|
749
|
-
data_buf.write(self._query_data)
|
781
|
+
self._format_query(query, query_override, data_buf, result)
|
750
782
|
|
751
783
|
result.data = data_buf.getvalue()
|
752
784
|
return result
|
753
785
|
|
754
786
|
def make_set_output_nearest(self, *,
|
755
787
|
position=0.0,
|
756
|
-
query=False
|
788
|
+
query=False,
|
789
|
+
query_override=None):
|
757
790
|
return self.make_set_output(
|
758
|
-
position=position, query=query,
|
791
|
+
position=position, query=query, query_override=query_override,
|
792
|
+
cmd=Register.SET_OUTPUT_NEAREST)
|
759
793
|
|
760
794
|
def make_set_output_exact(self, *,
|
761
795
|
position=0.0,
|
762
|
-
query=False
|
796
|
+
query=False,
|
797
|
+
query_override=None):
|
763
798
|
return self.make_set_output(
|
764
|
-
position=position, query=query,
|
799
|
+
position=position, query=query, query_override=query_override,
|
800
|
+
cmd=Register.SET_OUTPUT_EXACT)
|
765
801
|
|
766
802
|
async def set_output(self, *args, cmd=None, **kwargs):
|
767
803
|
return await self.execute(self.make_set_output(**kwargs, cmd=cmd))
|
@@ -777,15 +813,20 @@ class Controller:
|
|
777
813
|
# "make/set_rezero".
|
778
814
|
def make_rezero(self, *,
|
779
815
|
rezero=0.0,
|
780
|
-
query=False
|
816
|
+
query=False,
|
817
|
+
query_override=None):
|
781
818
|
return self.make_set_output(
|
782
|
-
position=rezero, query=query,
|
819
|
+
position=rezero, query=query, query_override=query_override,
|
820
|
+
cmd=Register.SET_OUTPUT_NEAREST)
|
783
821
|
|
784
822
|
async def set_rezero(self, *args, **kwargs):
|
785
823
|
return await self.execute(self.make_rezero(**kwargs))
|
786
824
|
|
787
|
-
def make_require_reindex(self
|
788
|
-
|
825
|
+
def make_require_reindex(self,
|
826
|
+
query=False,
|
827
|
+
query_override=None):
|
828
|
+
result = self._make_command(
|
829
|
+
query=query, query_override=query_override)
|
789
830
|
|
790
831
|
data_buf = io.BytesIO()
|
791
832
|
writer = Writer(data_buf)
|
@@ -796,8 +837,9 @@ class Controller:
|
|
796
837
|
result.data = data_buf.getvalue()
|
797
838
|
return result
|
798
839
|
|
799
|
-
async def set_require_reindex(self):
|
800
|
-
return await self.execute(self.make_require_reindex(
|
840
|
+
async def set_require_reindex(self, query=False, query_override=None):
|
841
|
+
return await self.execute(self.make_require_reindex(
|
842
|
+
query=query, query_override=query_override))
|
801
843
|
|
802
844
|
def make_position(self,
|
803
845
|
*,
|
@@ -812,11 +854,13 @@ class Controller:
|
|
812
854
|
velocity_limit=None,
|
813
855
|
accel_limit=None,
|
814
856
|
fixed_voltage_override=None,
|
815
|
-
query=False
|
857
|
+
query=False,
|
858
|
+
query_override=None):
|
816
859
|
"""Return a moteus.Command structure with data necessary to send a
|
817
860
|
position mode command with the given values."""
|
818
861
|
|
819
|
-
result = self._make_command(
|
862
|
+
result = self._make_command(
|
863
|
+
query=query, query_override=query_override)
|
820
864
|
|
821
865
|
pr = self.position_resolution
|
822
866
|
resolutions = [
|
@@ -866,8 +910,7 @@ class Controller:
|
|
866
910
|
if combiner.maybe_write():
|
867
911
|
writer.write_voltage(fixed_voltage_override, pr.fixed_voltage_override)
|
868
912
|
|
869
|
-
|
870
|
-
data_buf.write(self._query_data)
|
913
|
+
self._format_query(query, query_override, data_buf, result)
|
871
914
|
|
872
915
|
result.data = data_buf.getvalue()
|
873
916
|
|
@@ -876,16 +919,51 @@ class Controller:
|
|
876
919
|
async def set_position(self, *args, **kwargs):
|
877
920
|
return await self.execute(self.make_position(**kwargs))
|
878
921
|
|
922
|
+
async def set_position_wait_complete(
|
923
|
+
self,
|
924
|
+
period_s=0.025,
|
925
|
+
query_override=None,
|
926
|
+
*args, **kwargs):
|
927
|
+
"""Repeatedly send a position mode command to a device until it
|
928
|
+
reports that the trajectory has been completed.
|
929
|
+
|
930
|
+
If the controller is unresponsive, this method will never return.
|
931
|
+
"""
|
932
|
+
|
933
|
+
if query_override is None:
|
934
|
+
query_override = copy.deepcopy(self.query_resolution)
|
935
|
+
else:
|
936
|
+
query_override = copy.deepcopy(query_override)
|
937
|
+
|
938
|
+
query_override.trajectory_complete = mp.INT8
|
939
|
+
|
940
|
+
count = 2
|
941
|
+
while True:
|
942
|
+
result = await self.set_position(
|
943
|
+
query_override=query_override, *args, **kwargs)
|
944
|
+
|
945
|
+
if result is not None:
|
946
|
+
count = max(count - 1, 0)
|
947
|
+
|
948
|
+
if (count == 0 and
|
949
|
+
result is not None and
|
950
|
+
result.values[Register.TRAJECTORY_COMPLETE]):
|
951
|
+
return result
|
952
|
+
|
953
|
+
await asyncio.sleep(period_s)
|
954
|
+
|
879
955
|
def make_vfoc(self,
|
880
956
|
*,
|
881
957
|
theta,
|
882
958
|
voltage,
|
883
959
|
theta_rate=0.0,
|
884
|
-
query=False
|
960
|
+
query=False,
|
961
|
+
query_override=None):
|
885
962
|
"""Return a moteus.Command structure with data necessary to send a
|
886
963
|
voltage mode FOC command."""
|
887
964
|
|
888
|
-
result = self._make_command(
|
965
|
+
result = self._make_command(
|
966
|
+
query=query, query_override=query_override)
|
889
967
|
cr = self.vfoc_resolution
|
890
968
|
resolutions = [
|
891
969
|
cr.theta if theta is not None else mp.IGNORE,
|
@@ -921,8 +999,7 @@ class Controller:
|
|
921
999
|
if combiner.maybe_write():
|
922
1000
|
writer.write_velocity(theta_rate / math.pi, cr.theta_rate)
|
923
1001
|
|
924
|
-
|
925
|
-
data_buf.write(self._query_data)
|
1002
|
+
self._format_query(query, query_override, data_buf, result)
|
926
1003
|
|
927
1004
|
result.data = data_buf.getvalue()
|
928
1005
|
|
@@ -935,12 +1012,14 @@ class Controller:
|
|
935
1012
|
*,
|
936
1013
|
d_A,
|
937
1014
|
q_A,
|
938
|
-
query=False
|
1015
|
+
query=False,
|
1016
|
+
query_override=None):
|
939
1017
|
"""Return a moteus.Command structure with data necessary to send a
|
940
1018
|
current mode command.
|
941
1019
|
"""
|
942
1020
|
|
943
|
-
result = self._make_command(
|
1021
|
+
result = self._make_command(
|
1022
|
+
query=query, query_override=query_override)
|
944
1023
|
cr = self.current_resolution
|
945
1024
|
resolutions = [
|
946
1025
|
cr.d_A if d_A is not None else mp.IGNORE,
|
@@ -965,8 +1044,7 @@ class Controller:
|
|
965
1044
|
if combiner.maybe_write():
|
966
1045
|
writer.write_current(d_A, cr.d_A)
|
967
1046
|
|
968
|
-
|
969
|
-
data_buf.write(self._query_data)
|
1047
|
+
self._format_query(query, query_override, data_buf, result)
|
970
1048
|
|
971
1049
|
result.data = data_buf.getvalue()
|
972
1050
|
|
@@ -986,11 +1064,13 @@ class Controller:
|
|
986
1064
|
maximum_torque=None,
|
987
1065
|
stop_position=None,
|
988
1066
|
watchdog_timeout=None,
|
989
|
-
query=False
|
1067
|
+
query=False,
|
1068
|
+
query_override=None):
|
990
1069
|
"""Return a moteus.Command structure with data necessary to send a
|
991
1070
|
within mode command with the given values."""
|
992
1071
|
|
993
|
-
result = self._make_command(
|
1072
|
+
result = self._make_command(
|
1073
|
+
query=query, query_override=query_override)
|
994
1074
|
|
995
1075
|
pr = self.position_resolution
|
996
1076
|
resolutions = [
|
@@ -1029,8 +1109,7 @@ class Controller:
|
|
1029
1109
|
if combiner.maybe_write():
|
1030
1110
|
writer.write_time(watchdog_timeout, pr.watchdog_timeout)
|
1031
1111
|
|
1032
|
-
|
1033
|
-
data_buf.write(self._query_data)
|
1112
|
+
self._format_query(query, query_override, data_buf, result)
|
1034
1113
|
|
1035
1114
|
result.data = data_buf.getvalue()
|
1036
1115
|
|
@@ -1039,8 +1118,9 @@ class Controller:
|
|
1039
1118
|
async def set_stay_within(self, *args, **kwargs):
|
1040
1119
|
return await self.execute(self.make_stay_within(**kwargs))
|
1041
1120
|
|
1042
|
-
def make_brake(self, *, query=False):
|
1043
|
-
result = self._make_command(
|
1121
|
+
def make_brake(self, *, query=False, query_override=None):
|
1122
|
+
result = self._make_command(
|
1123
|
+
query=query, query_override=query_override)
|
1044
1124
|
|
1045
1125
|
data_buf = io.BytesIO()
|
1046
1126
|
writer = Writer(data_buf)
|
@@ -1048,8 +1128,7 @@ class Controller:
|
|
1048
1128
|
writer.write_int8(int(Register.MODE))
|
1049
1129
|
writer.write_int8(int(Mode.BRAKE))
|
1050
1130
|
|
1051
|
-
|
1052
|
-
data_buf.write(self._query_data)
|
1131
|
+
self._format_query(query, query_override, data_buf, result)
|
1053
1132
|
|
1054
1133
|
result.data = data_buf.getvalue()
|
1055
1134
|
|
@@ -1058,7 +1137,8 @@ class Controller:
|
|
1058
1137
|
async def set_brake(self, *args, **kwargs):
|
1059
1138
|
return await self.execute(self.make_brake(**kwargs))
|
1060
1139
|
|
1061
|
-
def make_write_gpio(self, aux1=None, aux2=None,
|
1140
|
+
def make_write_gpio(self, aux1=None, aux2=None,
|
1141
|
+
query=False, query_override=None):
|
1062
1142
|
"""Return a moteus.Command structure with data necessary to set one or
|
1063
1143
|
more GPIO registers.
|
1064
1144
|
|
@@ -1066,7 +1146,8 @@ class Controller:
|
|
1066
1146
|
significant bit is pin 0 on the respective port.
|
1067
1147
|
"""
|
1068
1148
|
|
1069
|
-
result = self._make_command(
|
1149
|
+
result = self._make_command(
|
1150
|
+
query=query, query_override=query_override)
|
1070
1151
|
|
1071
1152
|
data_buf = io.BytesIO()
|
1072
1153
|
writer = Writer(data_buf)
|
@@ -1082,8 +1163,7 @@ class Controller:
|
|
1082
1163
|
if combiner.maybe_write():
|
1083
1164
|
writer.write_int8(aux2)
|
1084
1165
|
|
1085
|
-
|
1086
|
-
data_buf.write(self._query_data)
|
1166
|
+
self._format_query(query, query_override, data_buf, result)
|
1087
1167
|
|
1088
1168
|
result.data = data_buf.getvalue()
|
1089
1169
|
return result
|
@@ -1157,6 +1237,7 @@ class Controller:
|
|
1157
1237
|
result.parse = make_diagnostic_parser(self.id, channel)
|
1158
1238
|
|
1159
1239
|
result.data = data_buf.getvalue()
|
1240
|
+
result.expected_reply_size = 3 + max_length
|
1160
1241
|
return result
|
1161
1242
|
|
1162
1243
|
async def diagnostic_read(self, *args, **kwargs):
|
moteus/moteus_tool.py
CHANGED
@@ -818,7 +818,8 @@ class Stream:
|
|
818
818
|
await self.check_for_fault()
|
819
819
|
|
820
820
|
enc_kp, enc_ki, enc_bw_hz = await self.set_encoder_filter(
|
821
|
-
torque_bw_hz,
|
821
|
+
torque_bw_hz, inductance,
|
822
|
+
control_rate_hz=control_rate_hz)
|
822
823
|
await self.check_for_fault()
|
823
824
|
|
824
825
|
v_per_hz = await self.calibrate_kv_rating(
|
@@ -1163,7 +1164,7 @@ class Stream:
|
|
1163
1164
|
print(f"Calculated inductance: {inductance}H")
|
1164
1165
|
return inductance
|
1165
1166
|
|
1166
|
-
async def set_encoder_filter(self, torque_bw_hz, control_rate_hz = None):
|
1167
|
+
async def set_encoder_filter(self, torque_bw_hz, inductance, control_rate_hz = None):
|
1167
1168
|
# Check to see if our firmware supports encoder filtering.
|
1168
1169
|
motor_position_style = await self.is_config_supported("motor_position.sources.0.pll_filter_hz")
|
1169
1170
|
servo_style = await self.is_config_supported("servo.encoder_filter.enabled")
|
@@ -1173,18 +1174,17 @@ class Stream:
|
|
1173
1174
|
if self.args.encoder_bw_hz:
|
1174
1175
|
desired_encoder_bw_hz = self.args.encoder_bw_hz
|
1175
1176
|
else:
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
desired_encoder_bw_hz = max(50, 2 * torque_bw_hz)
|
1177
|
+
# Don't let the encoder bandwidth be less than 10Hz by default.
|
1178
|
+
desired_encoder_bw_hz = max(10, 2 * torque_bw_hz)
|
1179
|
+
|
1180
|
+
# However, we limit the maximum encoder bandwidth for high
|
1181
|
+
# inductance motors, because they shouldn't be able to
|
1182
|
+
# accelerate that fast anyways.
|
1183
|
+
if inductance:
|
1184
|
+
# This is just a random constant that seems to work
|
1185
|
+
# well in practice.
|
1186
|
+
desired_encoder_bw_hz = min(
|
1187
|
+
desired_encoder_bw_hz, 2e-2 / inductance)
|
1188
1188
|
|
1189
1189
|
# And our bandwidth with the filter can be no larger than
|
1190
1190
|
# 1/10th the control rate.
|
@@ -1203,7 +1203,8 @@ class Stream:
|
|
1203
1203
|
await self.command(f"conf set servo.encoder_filter.kp {kp}")
|
1204
1204
|
await self.command(f"conf set servo.encoder_filter.ki {ki}")
|
1205
1205
|
elif motor_position_style:
|
1206
|
-
await self.
|
1206
|
+
commutation_source = await self.read_config_int("motor_position.commutation_source")
|
1207
|
+
await self.command(f"conf set motor_position.sources.{commutation_source}.pll_filter_hz {encoder_bw_hz}")
|
1207
1208
|
else:
|
1208
1209
|
assert False
|
1209
1210
|
return kp, ki, encoder_bw_hz
|
moteus/multiplex.py
CHANGED
@@ -244,21 +244,30 @@ class WriteFrame:
|
|
244
244
|
|
245
245
|
def __init__(self, buf):
|
246
246
|
self._buf = buf
|
247
|
+
self._size = 0
|
248
|
+
|
249
|
+
def size(self):
|
250
|
+
return self._size
|
247
251
|
|
248
252
|
def write(self, value, resolution):
|
249
253
|
self._buf.write(TYPES[resolution].pack(value))
|
254
|
+
self._size += TYPES[resolution].size
|
250
255
|
|
251
256
|
def write_int8(self, value):
|
252
257
|
self._buf.write(TYPES[INT8].pack(value))
|
258
|
+
self._size += 1
|
253
259
|
|
254
260
|
def write_int16(self, value):
|
255
261
|
self._buf.write(TYPES[INT16].pack(value))
|
262
|
+
self._size += 2
|
256
263
|
|
257
264
|
def write_int32(self, value):
|
258
265
|
self._buf.write(TYPES[INT32].pack(value))
|
266
|
+
self._size += 4
|
259
267
|
|
260
268
|
def write_f32(self, value):
|
261
269
|
self._buf.write(TYPES[F32].pack(value))
|
270
|
+
self._size += 4
|
262
271
|
|
263
272
|
def write_varuint(self, value):
|
264
273
|
while True:
|
@@ -266,6 +275,7 @@ class WriteFrame:
|
|
266
275
|
value >>= 7
|
267
276
|
this_byte |= 0x80 if value else 0x00
|
268
277
|
self._buf.write(bytes([this_byte]))
|
278
|
+
self._size += 1
|
269
279
|
|
270
280
|
if value == 0:
|
271
281
|
break
|
@@ -286,6 +296,7 @@ class WriteCombiner:
|
|
286
296
|
self.base_command = base_command
|
287
297
|
self.start_register = start_register
|
288
298
|
self.resolutions = resolutions
|
299
|
+
self.reply_size = 0
|
289
300
|
self._offset = 0
|
290
301
|
self._current_resolution = -1
|
291
302
|
|
@@ -320,6 +331,8 @@ class WriteCombiner:
|
|
320
331
|
|
321
332
|
write_command = [0x00, 0x04, 0x08, 0x0c][new_resolution] | self.base_command
|
322
333
|
|
334
|
+
start_size = self.writer.size()
|
335
|
+
|
323
336
|
if count <= 3:
|
324
337
|
# Use the shorthand formulation.
|
325
338
|
self.writer.write_int8(write_command + count)
|
@@ -329,4 +342,10 @@ class WriteCombiner:
|
|
329
342
|
self.writer.write_int8(count)
|
330
343
|
|
331
344
|
self.writer.write_varuint(self.start_register + this_offset)
|
345
|
+
|
346
|
+
end_size = self.writer.size()
|
347
|
+
|
348
|
+
self.reply_size += end_size - start_size
|
349
|
+
self.reply_size += count * resolution_size(new_resolution)
|
350
|
+
|
332
351
|
return True
|
moteus/pythoncan.py
CHANGED
@@ -87,10 +87,13 @@ class PythonCan:
|
|
87
87
|
arbitration_id = (command.destination |
|
88
88
|
(0x8000 if reply_required else 0) |
|
89
89
|
(command.can_prefix << 16))
|
90
|
+
on_wire_size = self._round_up_dlc(len(command.data))
|
91
|
+
full_message = (command.data +
|
92
|
+
bytes([0x50]) * (on_wire_size - len(command.data)))
|
90
93
|
message = can.Message(arbitration_id=arbitration_id,
|
91
94
|
is_extended_id=(arbitration_id >= 0x7ff),
|
92
|
-
dlc=
|
93
|
-
data=
|
95
|
+
dlc=on_wire_size,
|
96
|
+
data=full_message,
|
94
97
|
is_fd=True,
|
95
98
|
bitrate_switch=not self._disable_brs)
|
96
99
|
|
@@ -99,3 +102,22 @@ class PythonCan:
|
|
99
102
|
async def read(self):
|
100
103
|
self._maybe_setup()
|
101
104
|
return await self._reader.get_message()
|
105
|
+
|
106
|
+
def _round_up_dlc(self, size):
|
107
|
+
if size <= 8:
|
108
|
+
return size
|
109
|
+
if size <= 12:
|
110
|
+
return 12
|
111
|
+
if size <= 16:
|
112
|
+
return 16
|
113
|
+
if size <= 20:
|
114
|
+
return 20
|
115
|
+
if size <= 24:
|
116
|
+
return 24
|
117
|
+
if size <= 32:
|
118
|
+
return 32
|
119
|
+
if size <= 48:
|
120
|
+
return 48
|
121
|
+
if size <= 64:
|
122
|
+
return 64
|
123
|
+
return size
|
moteus/version.py
CHANGED
@@ -2,22 +2,22 @@ moteus/__init__.py,sha256=1Prmih65X7Qz7WY8AUiSwUWXz3Dev8ggQS9xohL75M0,721
|
|
2
2
|
moteus/aioserial.py,sha256=lOBFPXManRBMsjMc1zuyw-NRt6CNVMvnj_mQqIZLpyI,1061
|
3
3
|
moteus/aiostream.py,sha256=SLDXwdBMHIWVTAmeUGG6ke7sntfNVxd9CqapzfniJOo,3678
|
4
4
|
moteus/calibrate_encoder.py,sha256=kY6mV1WkgCrD9z2YpRoqTTK_BUrRfop4_YJym9AQI-I,12403
|
5
|
-
moteus/command.py,sha256=
|
5
|
+
moteus/command.py,sha256=IXb0ToAg74fm8FYKPiOdjlzxosqdafLhqADffCvw5OY,1156
|
6
6
|
moteus/export.py,sha256=dI8QjdqrcI3pi5fKfP25PwLvsIlVJ695x0ta0PVMKx8,1628
|
7
|
-
moteus/fdcanusb.py,sha256=
|
8
|
-
moteus/moteus.py,sha256=
|
9
|
-
moteus/moteus_tool.py,sha256=
|
10
|
-
moteus/multiplex.py,sha256=
|
7
|
+
moteus/fdcanusb.py,sha256=6nfnqxwJqyqX0MOn7pj3-kqWq-Vzwx5M8I0wUux3RnA,7294
|
8
|
+
moteus/moteus.py,sha256=7lObHdVYCELclm6JMg-QygbAmRgNyQrS0BxkOjERJlo,46993
|
9
|
+
moteus/moteus_tool.py,sha256=L-7IBs9YHcbd2nc8SPwCEinHvzp09iVb_JeQCUtNM2U,66800
|
10
|
+
moteus/multiplex.py,sha256=EBOhAE-DHkS_AXtqUl2YEs745evvLMPIMeXQSYxF8zk,10102
|
11
11
|
moteus/posix_aioserial.py,sha256=3JFiY5p4dtC2ztg6N5SOffnNk9lNcjie02yjD3UlJWo,2971
|
12
|
-
moteus/pythoncan.py,sha256=
|
12
|
+
moteus/pythoncan.py,sha256=629d_DUfunyXpMZcaA6ICuvgodes-KH7OVDC7smco7s,3886
|
13
13
|
moteus/reader.py,sha256=jGADQTmONSBMQ25I5AqXELLqil2TEha1KjraPdOsf78,11932
|
14
14
|
moteus/regression.py,sha256=wpPlxAZ9nC9kfv0oX1K9W2AZNnBLbY8htAJz60SxIb8,1183
|
15
15
|
moteus/router.py,sha256=k4Tf6hWtHSgzznmdnj4NySe84-y9feYRxGz0yTrJtoc,2043
|
16
16
|
moteus/transport.py,sha256=3asI2A87eQDImLP74LNLtETaShQRiD9RuCjlxNY6AlE,1003
|
17
|
-
moteus/version.py,sha256=
|
17
|
+
moteus/version.py,sha256=lYdyuBwllEdH5WL5gQPVzkIpGpTIZtY-1x9F1WUWUa8,609
|
18
18
|
moteus/win32_aioserial.py,sha256=SZsnoBWE0Uwo4ZZF8ALB1WNPRY9NiaCOBz6VfvVcnxA,1841
|
19
|
-
moteus-0.3.
|
20
|
-
moteus-0.3.
|
21
|
-
moteus-0.3.
|
22
|
-
moteus-0.3.
|
23
|
-
moteus-0.3.
|
19
|
+
moteus-0.3.59.dist-info/METADATA,sha256=Wye8gTlX1__CYw-VJcnfC7w7ynANVPsjOkcGNtxGgOU,3417
|
20
|
+
moteus-0.3.59.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
21
|
+
moteus-0.3.59.dist-info/entry_points.txt,sha256=indCsEML1fmtWJU1WiV-d7UmmTaAMhyBLEc1iiKnexQ,57
|
22
|
+
moteus-0.3.59.dist-info/top_level.txt,sha256=aZzmI_yecTaDrdSp29pTJuowaSQ9dlIZheQpshGg4YQ,7
|
23
|
+
moteus-0.3.59.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|