From 77ae7b62bf406a39918d4f908a9fa91c413374af Mon Sep 17 00:00:00 2001 From: sblaes Date: Tue, 10 Jul 2018 14:38:42 +0200 Subject: [PATCH 01/18] ported to python 3 --- dxl/dxlchain.py | 58 ++++++++++++++++++++++--------------------- dxl/dxlcore.py | 17 +++++++------ dxl/dxlmotors.py | 25 +++++++------------ dxl/post_threading.py | 8 +++--- 4 files changed, 52 insertions(+), 56 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index e5248b0..a0bed6d 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -89,8 +89,10 @@ def send(self,id,packet): def _send(self, id, packet): """ Takes a payload, packages it as [header,id,length,payload,checksum], sends it on serial and flush""" checksumed_data = [id, len(packet)+1] + packet - - data="".join(map(chr, [0xFF, 0xFF] + checksumed_data + [self.checksum(checksumed_data)])) + + # data="".join(map(chr, [0xFF, 0xFF] + checksumed_data + [self.checksum(checksumed_data)])) + data = b"".join( + map(lambda x: bytearray((x,)), [0xFF, 0xFF] + checksumed_data + [self.checksum(checksumed_data)])) self.port.write(data) self.port.flushOutput() @@ -204,13 +206,13 @@ def _get_model(self,id): def get_reg(self,id,name): """Read a named register from a motor""" if id not in self.motors.keys(): - raise DxlConfigurationException,'Motor ID %d does not exist on the chain'%(id) + raise DxlConfigurationException('Motor ID %d does not exist on the chain'%(id)) m=self.motors[id] reg=m.registers[name] (esize,cmd)=m.getRegisterCmd(name) (nid,data)=self.comm(id,cmd) if len(data)!=esize: - raise DxlCommunicationException,'Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data)) + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) v=reg.fromdxl(data) logging.info('Motor ID %d get register %s: %d'%(id,name,v) ) return v @@ -218,13 +220,13 @@ def get_reg(self,id,name): def get_reg_si(self,id,name): """Read a named register from a motor and returns value converted to SI units""" if id not in self.motors.keys(): - raise DxlConfigurationException,'Motor ID %d does not exist on the chain'%(id) + raise DxlConfigurationException('Motor ID %d does not exist on the chain'%(id)) m=self.motors[id] reg=m.registers[name] (esize,cmd)=m.getRegisterCmd(name) (nid,data)=self.comm(id,cmd) if len(data)!=esize: - raise DxlCommunicationException,'Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data)) + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) v=reg.fromdxl(data) logging.info('Motor ID %d get register %s: %d'%(id,name,v) ) return reg.tosi(v) @@ -232,26 +234,26 @@ def get_reg_si(self,id,name): def set_reg(self,id,name,v): """Sets a named register on a motor""" if id not in self.motors.keys(): - raise DxlConfigurationException,'Motor ID %d does not exist on the chain'%(id) + raise DxlConfigurationException('Motor ID %d does not exist on the chain'%(id)) m=self.motors[id] reg=m.registers[name] (esize,cmd)=m.setRegisterCmd(name,reg.todxl(v)) (nid,data)=self.comm(id,cmd) logging.info('Motor ID %d set register %s to %d'%(id,name,v) ) if len(data)!=esize: - raise DxlCommunicationException,'Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data)) + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) def set_reg_si(self,id,name,v): """Sets a named register on a motor using SI units""" if id not in self.motors.keys(): - raise DxlConfigurationException,'Motor ID %d does not exist on the chain'%(id) + raise DxlConfigurationException('Motor ID %d does not exist on the chain'%(id)) m=self.motors[id] reg=m.registers[name] (esize,cmd)=m.setRegisterCmd(name,reg.todxl(reg.fromsi(v))) (nid,data)=self.comm(id,cmd) logging.info('Motor ID %d set register %s to %d'%(id,name,v) ) if len(data)!=esize: - raise DxlCommunicationException,'Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data)) + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) def sync_write_pos_speed(self,ids,positions,speeds): @@ -261,34 +263,34 @@ def sync_write_pos_speed(self,ids,positions,speeds): # Check motor IDs, goal_pos and moving_speed register address and sizes for id in ids: if id not in self.motors.keys(): - raise DxlConfigurationException,"Motor ID %d cannot be found in chain"%id + raise DxlConfigurationException("Motor ID %d cannot be found in chain"%id) m=self.motors[id] reg_name="goal_pos" if reg_name not in m.registers.keys(): - raise DxlConfigurationException,"Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id)) r=m.registers[reg_name] if regpos==None: regpos=r else: if regpos.address!=r.address: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id)) if regpos.size!=r.size: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id)) reg_name="moving_speed" if reg_name not in m.registers.keys(): - raise DxlConfigurationException,"Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id)) r=m.registers[reg_name] if regspeed==None: regspeed=r else: if regspeed.address!=r.address: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id)) if regspeed.size!=r.size: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id)) if (regpos.address+regpos.size)!=regspeed.address: - raise DxlConfigurationException,"Synchronized write goal_pos/moving_speed impossible on chain, registers are not consecutive" + raise DxlConfigurationException("Synchronized write goal_pos/moving_speed impossible on chain, registers are not consecutive") # Everything is ok, build command and send payload= [Dxl.CMD_SYNC_WRITE,regpos.address,regpos.size+regspeed.size] @@ -312,19 +314,19 @@ def sync_write_pos(self,ids,positions): # Check motor IDs, goal_pos and moving_speed register address and sizes for id in ids: if id not in self.motors.keys(): - raise DxlConfigurationException,"Motor ID %d cannot be found in chain"%id + raise DxlConfigurationException("Motor ID %d cannot be found in chain"%id) m=self.motors[id] reg_name="goal_pos" if reg_name not in m.registers.keys(): - raise DxlConfigurationException,"Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id)) r=m.registers[reg_name] if reg==None: reg=r else: if reg.address!=r.address: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id)) if reg.size!=r.size: - raise DxlConfigurationException,"Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id) + raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id)) # Everything is ok, build command and send payload= [Dxl.CMD_SYNC_WRITE,reg.address,reg.size] @@ -397,7 +399,7 @@ def set_configuration(self,conf): for id in conf.keys(): sid=id iid=int(sid) - if iid not in self.motors.keys(): raise DxlConfigurationException,"Cannot find motor ID %d to be configured"%iid + if iid not in self.motors.keys(): raise DxlConfigurationException("Cannot find motor ID %d to be configured"%iid) motor=self.motors[iid] # Validate EEPROM read-only settings @@ -408,18 +410,18 @@ def set_configuration(self,conf): if current==val: continue # Value has to be changed if not 'w' in reg.mode: # read only: generate error if setting is EEPROM - if reg.eeprom: raise DxlConfigurationException,"Invalid EEPROM value in motor ID %d register %s: current=%d expected=%d"%(iid,name,current,val) + if reg.eeprom: raise DxlConfigurationException("Invalid EEPROM value in motor ID %d register %s: current=%d expected=%d"%(iid,name,current,val)) else: pass # Check/Set all registers for (name,val) in conf[sid].items(): - if name not in motor.registers.keys(): raise DxlConfigurationException,"Cannot configure missing register %s on motor ID %d"%(name,iid) + if name not in motor.registers.keys(): raise DxlConfigurationException("Cannot configure missing register %s on motor ID %d"%(name,iid)) reg=motor.registers[name] current=self.get_reg(iid,name) if current==val: continue # Value has to be changed if not 'w' in reg.mode: # read only: generate error if setting is EEPROM - if reg.eeprom: raise DxlConfigurationException,"Invalid EEPROM value in motor ID %d register %s: current=%d expected=%d"%(iid,name,current,val) + if reg.eeprom: raise DxlConfigurationException("Invalid EEPROM value in motor ID %d register %s: current=%d expected=%d"%(iid,name,current,val)) else: pass else: # Set value if reg.eeprom: @@ -429,7 +431,7 @@ def set_configuration(self,conf): def dump(self): """Obtain the motors chain configuration and dumps it on stdout""" conf=self.get_configuration() - print json.dumps(conf,indent=4,sort_keys=False) + print(json.dumps(conf,indent=4,sort_keys=False)) def get_motors(self,ids=None): """Return the list of all motors ids, or a specific set, or a single id""" @@ -440,7 +442,7 @@ def get_motors(self,ids=None): return ids elif type(ids)==type(int()): return [ids] - raise Exception,"Invalid type for motor id: %s"%str(ids) + raise Exception("Invalid type for motor id: %s"%str(ids)) diff --git a/dxl/dxlcore.py b/dxl/dxlcore.py index c09f6f3..b70c808 100644 --- a/dxl/dxlcore.py +++ b/dxl/dxlcore.py @@ -57,7 +57,7 @@ def registerModel(cls,model_number,model_cls): @classmethod def instantiateMotor(cls,id,model_number): if not model_number in cls.DxlModels.keys(): - raise DxlConfigurationException,"Cannot instantiate non registered element model %d on ID %d"%(model_number,id) + raise DxlConfigurationException("Cannot instantiate non registered element model %d on ID %d"%(model_number,id)) mcls=cls.DxlModels[model_number] return mcls() @@ -65,30 +65,31 @@ def instantiateMotor(cls,id,model_number): def getRegisterCmd(self,name): if not name in self.registers.keys(): - raise DxlConfigurationException,"Model %s has no register called %s"%(name,self.model_name) + raise DxlConfigurationException("Model %s has no register called %s"%(name,self.model_name)) r=self.registers[name] if not 'r' in r.mode: - raise DxlConfigurationException,"Register %s is not readable"%(name) + raise DxlConfigurationException("Register %s is not readable"%(name)) return (r.size,[Dxl.CMD_READ_DATA,r.address,r.size]) def setRegisterCmd(self,name,value): if not name in self.registers.keys(): - raise DxlConfigurationException,"Model %s has no register called %s"%(self.model_name,name) + raise DxlConfigurationException("Model %s has no register called %s"%(self.model_name,name)) r=self.registers[name] if not 'w' in r.mode: - raise DxlConfigurationException,"Register %s is not writable"%(name) + raise DxlConfigurationException("Register %s is not writable"%(name)) if r.size!=len(value): - raise DxlConfigurationException,"Model %s register %s has size %d: passed size %d"%(self.model_name,name,r.size,len(value)) + raise DxlConfigurationException("Model %s register %s has size %d: passed size %d"%(self.model_name,name,r.size,len(value))) return (0,[Dxl.CMD_WRITE_DATA,r.address]+value ) def sort(self): - self.registers = OrderedDict( sorted(self.registers.iteritems(), key=lambda x: x[1].address) ) + self.registers = OrderedDict( sorted(self.registers.items(), key=lambda x: x[1].address) ) def baud_to_si(self,val): return int(2000000/(val+1)) - def si_to_baud(self,val): + def si_to_baud(self,val): + irint(ids) return int(2000000/(val)-1) diff --git a/dxl/dxlmotors.py b/dxl/dxlmotors.py index 09e63b4..c363496 100644 --- a/dxl/dxlmotors.py +++ b/dxl/dxlmotors.py @@ -94,8 +94,7 @@ def __init__(self): self.sort() -class DxlMotorAX12A(DxlMotorAX12): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorAX12A(DxlMotorAX12, metaclass=ModelRegisteringMetaclass): model_name="AX12A" model_number=12 documentation_url="http://support.robotis.com/en/product/dynamixel/ax_series/dxl_ax_actuator.htm" @@ -104,8 +103,8 @@ class DxlMotorAX12A(DxlMotorAX12): def __init__(self): DxlMotorAX12.__init__(self) -class DxlMotorAX12W(DxlMotorAX12): - __metaclass__=ModelRegisteringMetaclass + +class DxlMotorAX12W(DxlMotorAX12, metaclass=ModelRegisteringMetaclass): model_name="AX12W" model_number=300 documentation_url="http://support.robotis.com/en/product/dynamixel/ax_series/ax-12w.htm" @@ -114,8 +113,7 @@ class DxlMotorAX12W(DxlMotorAX12): def __init__(self): DxlMotorAX12.__init__(self) -class DxlMotorAX18(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorAX18(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="AX18" model_number=18 documentation_url="http://support.robotis.com/en/product/dynamixel/ax_series/ax-18f.htm" @@ -135,8 +133,7 @@ def __init__(self): self.sort() -class DxlMotorMX12W(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorMX12W(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="MX12W" model_number=360 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-12w.htm" @@ -155,8 +152,7 @@ def __init__(self): self.sort() -class DxlMotorMX28(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorMX28(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="MX28" model_number=29 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-28.htm" @@ -175,8 +171,7 @@ def __init__(self): self.sort() -class DxlMotorMX64(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorMX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="MX64" model_number=310 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-64.htm" @@ -195,8 +190,7 @@ def __init__(self): self.sort() -class DxlMotorRX64(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorRX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="RX64" model_number=64 documentation_url="http://support.robotis.com/en/product/dynamixel/rx_series/rx-64.htm" @@ -216,8 +210,7 @@ def __init__(self): self.sort() -class DxlMotorMX106(DxlMotorAXMX): - __metaclass__=ModelRegisteringMetaclass +class DxlMotorMX106(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="MX106" model_number=320 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-106.htm" diff --git a/dxl/post_threading.py b/dxl/post_threading.py index 57e19c2..86bb14f 100644 --- a/dxl/post_threading.py +++ b/dxl/post_threading.py @@ -58,9 +58,9 @@ def __init__(self): def do(self,param): import time - print "Doing... param="+str(param) + print("Doing... param="+str(param)) time.sleep(2) - print "Done" + print("Done") return param @@ -70,6 +70,6 @@ def do(self,param): dummy.post.do("post2") t3=dummy.post.do("post3") t3.join() - print t3.result - print "Finished" + print(t3.result) + print("Finished") \ No newline at end of file From 812f05c99133704c1a7a71b0c7796cb20ff70f88 Mon Sep 17 00:00:00 2001 From: sblaes Date: Tue, 10 Jul 2018 14:38:57 +0200 Subject: [PATCH 02/18] added bulk read --- dxl/dxlchain.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ dxl/dxlcore.py | 2 ++ 2 files changed, 49 insertions(+) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index a0bed6d..e5c1dda 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -255,6 +255,53 @@ def set_reg_si(self,id,name,v): if len(data)!=esize: raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) + def bulk_read(self, ids, reg_names): + + payload = [Dxl.CMD_BULK_READ, 0x00] + + for id, reg_name in zip(ids, reg_names): + + if id not in self.motors.keys(): + raise DxlConfigurationException("Motor ID %d cannot be found in chain" % id) + + m = self.motors[id] + if reg_name not in m.registers.keys(): + raise DxlConfigurationException( + "Synchronized read %s impossible on chain, register absent from motor ID %d" % (reg_name, id)) + + r = m.registers[reg_name] + + payload.append(r.size) + payload.append(id) + payload.append(r.address) + + self.send(Dxl.BROADCAST, payload) + + # Retrieve response. packages from motors come unordered one after another + res = [] + + for _ in range(len(ids)): + (nid, data) = self.recv() + m = self.motors[nid] + r = m.registers[reg_names[ids.index(nid)]] + + if len(data) != r.size: + raise DxlCommunicationException( + 'Motor ID %d did not retrieve expected register %s size %d: got %d bytes' % ( + id, name, r.size, len(data))) + + res.append((nid, r.fromdxl(data))) + + return res + + def sync_read_pos(self, ids): + + return bulk_read(self, ids, ['present_position'] * len(ids)) + + def sync_read_temp(self, ids): + + return bulk_read(self, ids, ['present_temp'] * len(ids)) + def sync_write_pos_speed(self,ids,positions,speeds): """Performs a synchronized write of 'goal_pos' and 'moving_speed' registers for a set of motors (if possible)""" diff --git a/dxl/dxlcore.py b/dxl/dxlcore.py index b70c808..995e9f4 100644 --- a/dxl/dxlcore.py +++ b/dxl/dxlcore.py @@ -25,6 +25,8 @@ class Dxl: CMD_ACTION = 0x05 CMD_RESET = 0x06 CMD_SYNC_WRITE = 0x83 + CMD_SYNC_READ = 0x82 + CMD_BULK_READ = 0x92 def get_model_name(model_number): From 5c7e074655b95f432718279013b43ea38fde759c Mon Sep 17 00:00:00 2001 From: s-bl Date: Tue, 10 Jul 2018 14:44:11 +0200 Subject: [PATCH 03/18] Update README.rst --- README.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.rst b/README.rst index 72b4bed..5e6ce01 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,14 @@ HumaRobotics Dynamixel Library ######################################################## +Changes in this fork +========================== + +2018-07-10: Ported library to Python 3. Added bulk read. + +Original +========================== + HumaRobotics Dynamixel Library is a Python 2.7 library for programming Robotis Dynamixel motors directly from python or through the ROS bindings provided separately in https://github.com/HumaRobotics/dynamixel_hr_ros . It also comes with a GUI that allows to quickly identify/configure/manipulate your motors. From 6928df19bf9bf8afa54124b089571d1a5408cf2e Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Mon, 13 Aug 2018 20:25:58 +0200 Subject: [PATCH 04/18] Sync read (using bulk read internally) helpers for position, speed and torque Support for switching between control modes (position, speed, torque) --- dxl/dxlchain.py | 121 ++++++++++++++++++++++++++++++++++++++++------- dxl/dxlcore.py | 4 +- dxl/dxlmotors.py | 50 +++++++++++++------- 3 files changed, 140 insertions(+), 35 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index e5c1dda..df791ab 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -19,10 +19,6 @@ from collections import OrderedDict from post_threading import Post - - - - class DxlChain: """ Manages a list of Dynamixel motors on the same serial link. @@ -88,6 +84,7 @@ def send(self,id,packet): def _send(self, id, packet): """ Takes a payload, packages it as [header,id,length,payload,checksum], sends it on serial and flush""" +# checksumed_data = [id, len(packet)+1] + packet checksumed_data = [id, len(packet)+1] + packet # data="".join(map(chr, [0xFF, 0xFF] + checksumed_data + [self.checksum(checksumed_data)])) @@ -185,9 +182,9 @@ def _read(self,id,address,size): raise DxlCommunicationException('Read command did not obtain the %d bytes expected: got %d bytes'%(size,len(data))) return data - def _write(self,id,address,values): + def _write(self, id, register, values): """Write data to a motor registers""" - self._comm(id,[Dxl.CMD_WRITE_DATA,register,values]) + self._comm(id,[Dxl.CMD_WRITE_DATA, register, values]) def get_model_number(self,id): @@ -255,6 +252,84 @@ def set_reg_si(self,id,name,v): if len(data)!=esize: raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) + + def set_control_mode(self, id, mode): + m = self.motors[id] + if m.control_mode is None: + # determine control mode + if "torque_control_mode_enable" in m.registers and self.get_reg(id, "torque_control_mode_enable") == 1: + m.control_mode = m.TorqueControl + elif self.get_reg(id, "ccw_angle_limit") == 0: + m.control_mode = m.SpeedControl + else: + m.control_mode = m.PositionControl + + if m.control_mode != mode: + if m.control_mode == m.TorqueControl and "torque_control_mode_enable" in m.registers: + self.set_reg(id, "torque_control_mode_enable", 0) + if m.control_mode == m.SpeedControl: + self.set_reg(id, "moving_speed", 100) # we set this to a small value that the motor can move in pos control + if mode == m.SpeedControl: + m.cw_angle_limit = self.get_reg(id, "cw_angle_limit") + m.ccw_angle_limit = self.get_reg(id, "ccw_angle_limit") + self.set_reg(id, "cw_angle_limit", 0) + self.set_reg(id, "ccw_angle_limit", 0) + m.control_mode = mode + elif mode == m.PositionControl: + if m.ccw_angle_limit is 0 or m.ccw_angle_limit is None: + m.cw_angle_limit, m.ccw_angle_limit = m.registers["goal_pos"].range + self.set_reg(id, "cw_angle_limit", m.cw_angle_limit) + self.set_reg(id, "ccw_angle_limit", m.ccw_angle_limit) + # self.set_reg(id, "goal_pos", self.get_reg(id, "present_position")) + m.control_mode = mode + elif mode == m.TorqueControl: + if "torque_control_mode_enable" in m.registers: + self.set_reg(id, "torque_control_mode_enable", 1) + m.control_mode = mode + else: + logging.warning("Set Torque mode failed: Motor id {}" % (id)) + + + + # # Todo: check for sync_read and if it is faster + # # does not work + # def sync_read(self, ids, reg_name): + # + # payload = [Dxl.CMD_SYNC_READ, 0x00] + # + # m = self.motors[ids[0]] + # if reg_name not in m.registers.keys(): + # raise DxlConfigurationException( + # "Synchronized read %s impossible on chain, register absent from motor ID %d" % (reg_name, ids[0])) + # r = m.registers[reg_name] + # payload.append(r.address) + # payload.append(r.size) + # + # for id in ids: + # if id not in self.motors.keys(): + # raise DxlConfigurationException("Motor ID %d cannot be found in chain" % id) + # payload.append(id) + # + # self.send(Dxl.BROADCAST, payload) + # + # # Retrieve response. packages from motors come unordered one after another + # res = [] + # + # for _ in range(len(ids)): + # (nid, data) = self.recv() + # m = self.motors[nid] + # r = m.registers[reg_name] + # + # if len(data) != r.size: + # raise DxlCommunicationException( + # 'Motor ID %d did not retrieve expected register %s size %d: got %d bytes' % ( + # id, reg_name, r.size, len(data))) + # + # res.append((nid, r.fromdxl(data))) + # + # return res + + def bulk_read(self, ids, reg_names): payload = [Dxl.CMD_BULK_READ, 0x00] @@ -283,24 +358,36 @@ def bulk_read(self, ids, reg_names): for _ in range(len(ids)): (nid, data) = self.recv() m = self.motors[nid] - r = m.registers[reg_names[ids.index(nid)]] + reg_name =reg_names[ids.index(nid)] + r = m.registers[reg_name] if len(data) != r.size: raise DxlCommunicationException( 'Motor ID %d did not retrieve expected register %s size %d: got %d bytes' % ( - id, name, r.size, len(data))) + id, reg_name, r.size, len(data))) res.append((nid, r.fromdxl(data))) return res - def sync_read_pos(self, ids): - return bulk_read(self, ids, ['present_position'] * len(ids)) + def sync_read_pos(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_position') + + def sync_read_speed(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_speed') - def sync_read_temp(self, ids): + def sync_read_load(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_load') - return bulk_read(self, ids, ['present_temp'] * len(ids)) + def sync_read_temp(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_temp') + + # Todo: use sync read if it works + def _sync_read_X_wrapper(self, ids, register): + if ids is None: + ids = self.motors + return dict(self.bulk_read(ids, [register] * len(ids))) def sync_write_pos_speed(self,ids,positions,speeds): @@ -350,11 +437,8 @@ def sync_write_pos_speed(self,ids,positions,speeds): payload.extend(regspeed.todxl(speed)) self.send(Dxl.BROADCAST,payload) - - - def sync_write_pos(self,ids,positions): """Performs a synchronized write of 'goal_pos' register for a set of motors (if possible)""" reg=None @@ -383,8 +467,11 @@ def sync_write_pos(self,ids,positions): payload.append(id) payload.extend(reg.todxl(pos)) - self.send(Dxl.BROADCAST,payload) - + self.send(Dxl.BROADCAST,payload) + + + + def to_si(self,id,name,v): """Converts a motor register value from dynamixel format to SI units""" reg=self.motors[id].registers[name] diff --git a/dxl/dxlcore.py b/dxl/dxlcore.py index 995e9f4..b765177 100644 --- a/dxl/dxlcore.py +++ b/dxl/dxlcore.py @@ -24,9 +24,9 @@ class Dxl: CMD_REG_WRITE = 0x04 CMD_ACTION = 0x05 CMD_RESET = 0x06 - CMD_SYNC_WRITE = 0x83 + CMD_SYNC_WRITE = 0x83 # write to multiple devices with same register CMD_SYNC_READ = 0x82 - CMD_BULK_READ = 0x92 + CMD_BULK_READ = 0x92 # read from multiple devices with different register def get_model_name(model_number): diff --git a/dxl/dxlmotors.py b/dxl/dxlmotors.py index c363496..c394e82 100644 --- a/dxl/dxlmotors.py +++ b/dxl/dxlmotors.py @@ -23,7 +23,10 @@ def is_motor(self): class DxlMotorAXMX(DxlMotor): - + PositionControl = 1 + SpeedControl = 2 + TorqueControl = 3 + def __init__(self): DxlMotor.__init__(self) @@ -31,9 +34,9 @@ def __init__(self): self.registers["firmware"]= DxlRegisterByte(0x02,'r',eeprom=True) self.registers["id"]= DxlRegisterByte(0x03,'rw',eeprom=True) self.registers["baud_rate"]= DxlRegisterByte(0x04,'rw',eeprom=True,tosi=self.baud_to_si,fromsi=self.si_to_baud) - self.registers["return_delay"]= DxlRegisterByte(0x05,'rw',eeprom=True,tosi=self.pos_to_si,fromsi=self.si_to_pos) + self.registers["return_delay"]= DxlRegisterByte(0x05,'rw',eeprom=True) self.registers["cw_angle_limit"]= DxlRegisterWord(0x06,'rw',eeprom=True,tosi=self.pos_to_si,fromsi=self.si_to_pos) - self.registers["ccw_angle_limit"]= DxlRegisterWord(0x08,'rw',eeprom=True) + self.registers["ccw_angle_limit"]= DxlRegisterWord(0x08,'rw',eeprom=True,tosi=self.pos_to_si,fromsi=self.si_to_pos) self.registers["high_temp_limit"]= DxlRegisterByte(0x0b,'rw',eeprom=True) self.registers["low_voltage_limit"]= DxlRegisterByte(0x0c,'rw',eeprom=True) self.registers["high_voltage_limit"]= DxlRegisterByte(0x0d,'rw',eeprom=True) @@ -60,6 +63,10 @@ def __init__(self): self.registers["moving"]= DxlRegisterByte(0x2E,'r') self.registers["lock"]= DxlRegisterByte(0x2F,'rw',range=[0,1]) self.registers["punch"]= DxlRegisterWord(0x30,'rw',range=[0x20,0x3ff]) + + self.control_mode = None + self.cw_angle_limit = 0 + self.ccw_angle_limit = None self.sort() @@ -113,6 +120,7 @@ class DxlMotorAX12W(DxlMotorAX12, metaclass=ModelRegisteringMetaclass): def __init__(self): DxlMotorAX12.__init__(self) + class DxlMotorAX18(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): model_name="AX18" model_number=18 @@ -132,8 +140,19 @@ def __init__(self): self.registers["moving_speed"]= DxlRegisterWord(0x20,'rw',range=[0,1023],tosi=self.speed_to_si,fromsi=self.si_to_speed) self.sort() - -class DxlMotorMX12W(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): + + +class DxlMotorMXBase(DxlMotorAXMX): + + def __init__(self): + DxlMotorAXMX.__init__(self) + self.registers["torque_control_mode_enable"] = DxlRegisterByte(0x46, 'rw', range=[0, 1]) + self.registers["goal_torque"] = DxlRegisterWord(0x47, 'rw', range=[0, 1023]) + + self.sort() + + +class DxlMotorMX12W(DxlMotorMXBase, metaclass=ModelRegisteringMetaclass): model_name="MX12W" model_number=360 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-12w.htm" @@ -141,18 +160,17 @@ class DxlMotorMX12W(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): tick_to_rpm=0.114 def __init__(self): - DxlMotorAXMX.__init__(self) + super().__init__() self.registers["p_gain"]= DxlRegisterByte(0x1C,'rw') self.registers["i_gain"]= DxlRegisterByte(0x1B,'rw') self.registers["d_gain"]= DxlRegisterByte(0x1A,'rw') self.registers["goal_pos"]= DxlRegisterWord(0x1E,'rw',range=[0,4095],tosi=self.pos_to_si,fromsi=self.si_to_pos) - self.registers["moving_speed"]= DxlRegisterWord(0x20,'rw',range=[0,1023],tosi=self.speed_to_si,fromsi=self.si_to_speed) self.sort() -class DxlMotorMX28(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): +class DxlMotorMX28(DxlMotorMXBase, metaclass=ModelRegisteringMetaclass): model_name="MX28" model_number=29 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-28.htm" @@ -160,7 +178,7 @@ class DxlMotorMX28(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): tick_to_rpm=0.114 def __init__(self): - DxlMotorAXMX.__init__(self) + super().__init__() self.registers["d_gain"]= DxlRegisterByte(0x1A,'rw') self.registers["i_gain"]= DxlRegisterByte(0x1B,'rw') @@ -171,7 +189,7 @@ def __init__(self): self.sort() -class DxlMotorMX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): +class DxlMotorMX64(DxlMotorMXBase, metaclass=ModelRegisteringMetaclass): model_name="MX64" model_number=310 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-64.htm" @@ -179,7 +197,7 @@ class DxlMotorMX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): tick_to_rpm=0.114 def __init__(self): - DxlMotorAXMX.__init__(self) + super().__init__() self.registers["d_gain"]= DxlRegisterByte(0x1A,'rw') self.registers["i_gain"]= DxlRegisterByte(0x1B,'rw') @@ -187,10 +205,10 @@ def __init__(self): self.registers["goal_pos"]= DxlRegisterWord(0x1E,'rw',range=[0,4095],tosi=self.pos_to_si,fromsi=self.si_to_pos) self.registers["moving_speed"]= DxlRegisterWord(0x20,'rw',range=[0,1023],tosi=self.speed_to_si,fromsi=self.si_to_speed) - + self.sort() -class DxlMotorRX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): +class DxlMotorRX64(DxlMotorMXBase, metaclass=ModelRegisteringMetaclass): model_name="RX64" model_number=64 documentation_url="http://support.robotis.com/en/product/dynamixel/rx_series/rx-64.htm" @@ -198,7 +216,7 @@ class DxlMotorRX64(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): tick_to_rpm=0.111 def __init__(self): - DxlMotorAXMX.__init__(self) + super().__init__() self.registers["cw_compliance_margin"]= DxlRegisterByte(0x1A,'rw') self.registers["ccw_compliance_margin"]=DxlRegisterByte(0x1B,'rw') @@ -210,14 +228,14 @@ def __init__(self): self.sort() -class DxlMotorMX106(DxlMotorAXMX, metaclass=ModelRegisteringMetaclass): +class DxlMotorMX106(DxlMotorMXBase, metaclass=ModelRegisteringMetaclass): model_name="MX106" model_number=320 documentation_url="http://support.robotis.com/en/product/dynamixel/mx_series/mx-106.htm" tick_to_rad=0.00153588974175501002769284787627 def __init__(self): - DxlMotorAXMX.__init__(self) + super().__init__() self.registers["d_gain"]= DxlRegisterByte(0x1A,'rw') self.registers["i_gain"]= DxlRegisterByte(0x1B,'rw') From 78ab055e44e25f0d8886bc63ee801c42452aea81 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Tue, 14 Aug 2018 11:17:40 +0200 Subject: [PATCH 05/18] added notebook to test functions --- dynamixel-hr-python3.ipynb | 784 +++++++++++++++++++++++++++++++++++++ 1 file changed, 784 insertions(+) create mode 100644 dynamixel-hr-python3.ipynb diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb new file mode 100644 index 0000000..587ee6e --- /dev/null +++ b/dynamixel-hr-python3.ipynb @@ -0,0 +1,784 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:14:25.669177Z", + "start_time": "2018-08-13T18:14:25.662316Z" + } + }, + "outputs": [], + "source": [ + "import sys\n", + "#sys.path = ['../../src/dynamixel_hr/dxl_python3'] + sys.path\n", + "sys.path = ['dxl'] + sys.path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:14:25.987781Z", + "start_time": "2018-08-13T18:14:25.972500Z" + } + }, + "outputs": [], + "source": [ + "import dxlchain as Dxl\n", + "import logging" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:14:26.392145Z", + "start_time": "2018-08-13T18:14:26.388427Z" + } + }, + "outputs": [], + "source": [ + "logger = logging.getLogger()\n", + "#logger.setLevel('DEBUG')\n", + "#logger.setLevel('WARN')" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:00.964291Z", + "start_time": "2018-08-13T18:23:00.947831Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from importlib import reload\n", + "reload(Dxl)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:01.095614Z", + "start_time": "2018-08-13T18:23:01.091078Z" + } + }, + "outputs": [], + "source": [ + "# Open the serial device\n", + "chain=Dxl.DxlChain(\"/dev/ttyACM0\",rate=1000000)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:01.926918Z", + "start_time": "2018-08-13T18:23:01.867615Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# Load all the motors and obtain the list of IDs\n", + "motors=chain.get_motor_list() # Discover all motors on the chain and return their IDs\n", + "print(motors)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:02.738231Z", + "start_time": "2018-08-13T18:23:02.731851Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: ,\n", + " 5: }" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:15:45.209111Z", + "start_time": "2018-08-13T18:15:45.099257Z" + } + }, + "outputs": [], + "source": [ + "chain.goto(5, 100, speed=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:15:45.943282Z", + "start_time": "2018-08-13T18:15:45.939209Z" + } + }, + "outputs": [], + "source": [ + "pos = 2000" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T16:15:11.138216Z", + "start_time": "2018-08-13T16:15:11.133352Z" + } + }, + "outputs": [], + "source": [ + "chain.sync_writepos_speed([1], [pos, pos,pos], [100, 500,1000])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T16:15:14.368008Z", + "start_time": "2018-08-13T16:15:14.358909Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 2001, 2: 2002, 3: 2002, 4: 2948, 5: 999}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_position()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T14:06:26.910662Z", + "start_time": "2018-08-13T14:06:26.901621Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(1, 1998), (2, 1998), (3, 1997)]" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.sync_read_pos([1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "chain.sync_read_([1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T16:18:36.561215Z", + "start_time": "2018-08-13T16:18:36.338251Z" + }, + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([(1,\n", + " OrderedDict([('model_number', 310),\n", + " ('firmware', 39),\n", + " ('id', 1),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 250),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 4095),\n", + " ('high_temp_limit', 80),\n", + " ('low_voltage_limit', 60),\n", + " ('high_voltage_limit', 160),\n", + " ('max_torque', 1023),\n", + " ('status_return_level', 2),\n", + " ('alarm_led', 36),\n", + " ('alarm_shutdown', 36),\n", + " ('torque_enable', 1),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 2000),\n", + " ('moving_speed', 100),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 2001),\n", + " ('present_speed', 0),\n", + " ('present_load', 16),\n", + " ('present_voltage', 119),\n", + " ('present_temp', 37),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 0)])),\n", + " (2,\n", + " OrderedDict([('model_number', 29),\n", + " ('firmware', 39),\n", + " ('id', 2),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 0),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 4095),\n", + " ('high_temp_limit', 80),\n", + " ('low_voltage_limit', 60),\n", + " ('high_voltage_limit', 160),\n", + " ('max_torque', 1023),\n", + " ('status_return_level', 2),\n", + " ('alarm_led', 36),\n", + " ('alarm_shutdown', 36),\n", + " ('torque_enable', 1),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 2000),\n", + " ('moving_speed', 500),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 2002),\n", + " ('present_speed', 0),\n", + " ('present_load', 16),\n", + " ('present_voltage', 119),\n", + " ('present_temp', 38),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 0)])),\n", + " (3,\n", + " OrderedDict([('model_number', 29),\n", + " ('firmware', 39),\n", + " ('id', 3),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 0),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 4095),\n", + " ('high_temp_limit', 80),\n", + " ('low_voltage_limit', 60),\n", + " ('high_voltage_limit', 160),\n", + " ('max_torque', 1023),\n", + " ('status_return_level', 2),\n", + " ('alarm_led', 36),\n", + " ('alarm_shutdown', 36),\n", + " ('torque_enable', 1),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 2000),\n", + " ('moving_speed', 1000),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 2002),\n", + " ('present_speed', 0),\n", + " ('present_load', 24),\n", + " ('present_voltage', 118),\n", + " ('present_temp', 38),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 0)])),\n", + " (4,\n", + " OrderedDict([('model_number', 29),\n", + " ('firmware', 39),\n", + " ('id', 4),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 0),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 4095),\n", + " ('high_temp_limit', 80),\n", + " ('low_voltage_limit', 60),\n", + " ('high_voltage_limit', 160),\n", + " ('max_torque', 1023),\n", + " ('status_return_level', 1),\n", + " ('alarm_led', 36),\n", + " ('alarm_shutdown', 36),\n", + " ('torque_enable', 1),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 2952),\n", + " ('moving_speed', 1300),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 2947),\n", + " ('present_speed', 0),\n", + " ('present_load', 1048),\n", + " ('present_voltage', 119),\n", + " ('present_temp', 39),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 0)])),\n", + " (5,\n", + " OrderedDict([('model_number', 12),\n", + " ('firmware', 24),\n", + " ('id', 5),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 250),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 1023),\n", + " ('high_temp_limit', 70),\n", + " ('low_voltage_limit', 60),\n", + " ('high_voltage_limit', 140),\n", + " ('max_torque', 1023),\n", + " ('status_return_level', 2),\n", + " ('alarm_led', 36),\n", + " ('alarm_shutdown', 36),\n", + " ('torque_enable', 1),\n", + " ('led', 0),\n", + " ('cw_compliance_margin', 1),\n", + " ('ccw_compliance_margin', 1),\n", + " ('cw_compliance_slope', 32),\n", + " ('ccw_compliance_slope', 32),\n", + " ('goal_pos', 1000),\n", + " ('moving_speed', 100),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 999),\n", + " ('present_speed', 0),\n", + " ('present_load', 0),\n", + " ('present_voltage', 121),\n", + " ('present_temp', 38),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 32)]))])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_configuration()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:55:16.297583Z", + "start_time": "2018-08-13T17:55:16.081601Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import time\n", + "# %matplotlib " + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:36:07.203386Z", + "start_time": "2018-08-13T17:36:03.807102Z" + } + }, + "outputs": [], + "source": [ + "data=[]\n", + "ctrl=[]\n", + "motors=[1,2,3,4]\n", + "for t in range(1000):\n", + " pos = np.sin(t/50.0)*1000+2048\n", + " chain.sync_write_pos_speed(motors, [pos] * len(motors), [i*200 + 100 for i in motors])\n", + " #time.sleep(0.01) \n", + " new_pos = [0] * len(motors) \n", + " for i,v in chain.sync_read_pos(motors).items():\n", + " new_pos[i-1]=v\n", + " #for i in motors:\n", + " # v = chain.get_position(i)\n", + " # new_pos[i-1]=v[i]\n", + " \n", + " data.append(new_pos)\n", + " ctrl.append(pos)\n", + "data=np.asarray(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:36:08.917766Z", + "start_time": "2018-08-13T17:36:08.776044Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(ctrl)\n", + "plt.plot(data[:,0])\n", + "plt.plot(data[:,1])\n", + "plt.plot(data[:,2])\n", + "plt.plot(data[:,3])" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T16:45:59.131999Z", + "start_time": "2018-08-13T16:45:59.125062Z" + } + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'DxlChain' object has no attribute 'sync_read'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msync_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'present_position'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'DxlChain' object has no attribute 'sync_read'" + ] + } + ], + "source": [ + "chain.sync_read([1,2,3],'present_position')" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:01:17.307125Z", + "start_time": "2018-08-13T17:01:17.302883Z" + } + }, + "outputs": [], + "source": [ + "(esize,cmd)=chain.motors[1].getRegisterCmd(\"present_speed\")" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:30.882404Z", + "start_time": "2018-08-13T18:23:30.874569Z" + } + }, + "outputs": [], + "source": [ + "chain.set_control_mode(1, chain.motors[1].PositionControl)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:27.958020Z", + "start_time": "2018-08-13T18:23:27.947702Z" + } + }, + "outputs": [], + "source": [ + "chain.set_control_mode(1, chain.motors[1].SpeedControl)" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:35:46.566562Z", + "start_time": "2018-08-13T17:35:46.559728Z" + } + }, + "outputs": [], + "source": [ + "chain.set_reg(1, \"cw_angle_limit\", 0)\n", + "chain.set_reg(1, \"ccw_angle_limit\", 4095)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:17:26.647714Z", + "start_time": "2018-08-13T18:17:26.642211Z" + } + }, + "outputs": [], + "source": [ + "chain.set_reg(1, \"goal_torque\", 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:29.343477Z", + "start_time": "2018-08-13T18:23:29.338456Z" + } + }, + "outputs": [], + "source": [ + "chain.set_reg(1, \"moving_speed\", 400)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:23:32.655420Z", + "start_time": "2018-08-13T18:23:32.650356Z" + } + }, + "outputs": [], + "source": [ + "chain.set_reg(1, \"goal_pos\", 400)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T18:20:20.918402Z", + "start_time": "2018-08-13T18:20:20.911772Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 4095]" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[4].registers[\"goal_pos\"].range" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:32:41.442374Z", + "start_time": "2018-08-13T17:32:41.433876Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-620" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[5].si_to_pos(-3.1415)" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:32:59.144033Z", + "start_time": "2018-08-13T17:32:59.137878Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[5].pos_to_si(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-13T17:33:02.721466Z", + "start_time": "2018-08-13T17:33:02.714648Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.182929746722361" + ] + }, + "execution_count": 118, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[5].pos_to_si(1024)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 981a9d89f4adacfcaa072e1895961794b6d6a70e Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Tue, 14 Aug 2018 13:23:49 +0200 Subject: [PATCH 06/18] dxl local imports --- dxl/dxlchain.py | 11 ++++++----- dxl/dxlcontrollers.py | 4 ++-- dxl/dxlmotors.py | 4 ++-- dxl/dxlsensors.py | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index df791ab..80e5c1c 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -4,10 +4,11 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from dxlcore import * -from dxlregisters import * -from dxlmotors import * -from dxlsensors import * +from .dxlcore import * +from .dxlregisters import * +from .dxlmotors import * +from .dxlsensors import * +from .post_threading import Post import sys import serial @@ -17,7 +18,7 @@ import json import array from collections import OrderedDict -from post_threading import Post + class DxlChain: """ diff --git a/dxl/dxlcontrollers.py b/dxl/dxlcontrollers.py index 7f8e7ff..32c16ae 100644 --- a/dxl/dxlcontrollers.py +++ b/dxl/dxlcontrollers.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from dxlcore import * -from dxlregisters import * +from .dxlcore import * +from .dxlregisters import * diff --git a/dxl/dxlmotors.py b/dxl/dxlmotors.py index c394e82..a97ce9e 100644 --- a/dxl/dxlmotors.py +++ b/dxl/dxlmotors.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from dxlcore import * -from dxlregisters import * +from .dxlcore import * +from .dxlregisters import * import math diff --git a/dxl/dxlsensors.py b/dxl/dxlsensors.py index 505967d..6f972b5 100644 --- a/dxl/dxlsensors.py +++ b/dxl/dxlsensors.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from dxlcore import * -from dxlregisters import * +from .dxlcore import * +from .dxlregisters import * From 22ded76cc47eb28d5144bf44f7523dfa0bfe0da6 Mon Sep 17 00:00:00 2001 From: Sebastian Blaes Date: Tue, 14 Aug 2018 17:08:47 +0200 Subject: [PATCH 07/18] added bulk_multi_read which allows to read multiple consecutive registers from multiple devices --- dxl/dxlchain.py | 83 ++++++++++++-- dxl/dxlcontrollers.py | 5 +- dxl/dxlmotors.py | 4 +- dxl/dxlsensors.py | 5 +- dynamixel-hr-python3.ipynb | 218 +++++++++++++++++++++++++++++++++---- 5 files changed, 279 insertions(+), 36 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 80e5c1c..623bf6e 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -4,11 +4,11 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from .dxlcore import * -from .dxlregisters import * -from .dxlmotors import * -from .dxlsensors import * -from .post_threading import Post +from dxlcore import * +from dxlregisters import * +from dxlmotors import * +from dxlsensors import * +from post_threading import Post import sys import serial @@ -372,6 +372,75 @@ def bulk_read(self, ids, reg_names): return res + def bulk_multi_read(self, ids=None, user_regs=None): + + ids = self.get_motors(ids) # returns all motors if ids is None + + "Needs to be consecutive list of registers" + regs = ['goal_pos', + 'moving_speed', + 'torque_limit', + 'present_position', + 'present_speed', + 'present_load', + 'present_voltage', + 'present_temp' + ] + + if user_regs is not None: + regs = user_regs + + payload = [Dxl.CMD_BULK_READ, 0x00] + + tot_sizes = dict() + + for id in ids: + + if id not in self.motors.keys(): + raise DxlConfigurationException("Motor ID %d cannot be found in chain" % id) + + m = self.motors[id] + + tot_size = 0 + for reg in regs: + + if reg not in m.registers.keys(): + raise DxlConfigurationException( + "Synchronized read %s impossible on chain, register absent from motor ID %d" % (reg_name, id)) + + r = m.registers[reg] + tot_size += r.size + tot_sizes[id] = tot_size + + payload.append(tot_size) + payload.append(id) + fst_addr = m.registers[regs[0]].address # address of first register + payload.append(fst_addr) + + self.send(Dxl.BROADCAST, payload) + + # Retrieve response. packages from motors come unordered one after another + res = [] + + for _ in ids: + (nid, data) = self.recv() + + if len(data) != tot_sizes[nid]: + raise DxlCommunicationException( + 'Motor ID %d did not retrieve expected register size %d: got %d bytes' % ( + nid, tot_sizes[nid], len(data))) + + m = self.motors[nid] + blob = (nid, {}) + counter = 0 + for reg in regs: + r = m.registers[reg] + blob[1][reg] = r.fromdxl(data[counter:counter+r.size]) + counter += r.size + res.append(blob) + + return dict(res) + def sync_read_pos(self, ids=None): return self._sync_read_X_wrapper(ids, 'present_position') @@ -437,9 +506,8 @@ def sync_write_pos_speed(self,ids,positions,speeds): payload.extend(regpos.todxl(pos)) payload.extend(regspeed.todxl(speed)) - self.send(Dxl.BROADCAST,payload) + self.send(Dxl.BROADCAST,payload) - def sync_write_pos(self,ids,positions): """Performs a synchronized write of 'goal_pos' register for a set of motors (if possible)""" reg=None @@ -669,4 +737,3 @@ def load_position(self,filename,blocking=True): for k,v in d.items(): pos[int(k)]=v self.set_position(pos,blocking) - diff --git a/dxl/dxlcontrollers.py b/dxl/dxlcontrollers.py index 32c16ae..3985575 100644 --- a/dxl/dxlcontrollers.py +++ b/dxl/dxlcontrollers.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from .dxlcore import * -from .dxlregisters import * +from dxlcore import * +from dxlregisters import * @@ -43,4 +43,3 @@ def __init__(self): self.sort() - \ No newline at end of file diff --git a/dxl/dxlmotors.py b/dxl/dxlmotors.py index a97ce9e..c394e82 100644 --- a/dxl/dxlmotors.py +++ b/dxl/dxlmotors.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from .dxlcore import * -from .dxlregisters import * +from dxlcore import * +from dxlregisters import * import math diff --git a/dxl/dxlsensors.py b/dxl/dxlsensors.py index 6f972b5..3ac6d4f 100644 --- a/dxl/dxlsensors.py +++ b/dxl/dxlsensors.py @@ -3,8 +3,8 @@ # WINDOWS WARNING: For best performance, parameters of the COM Port should be set to maximum baud rate, and 1ms delay (Device Manager, COM Ports, properties, advanced) -from .dxlcore import * -from .dxlregisters import * +from dxlcore import * +from dxlregisters import * @@ -65,4 +65,3 @@ def __init__(self): self.sort() - \ No newline at end of file diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb index 587ee6e..2d4aaa4 100644 --- a/dynamixel-hr-python3.ipynb +++ b/dynamixel-hr-python3.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:25.669177Z", @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 30, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:25.987781Z", @@ -28,12 +28,13 @@ "outputs": [], "source": [ "import dxlchain as Dxl\n", - "import logging" + "import logging\n", + "import numpy as np" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:26.392145Z", @@ -49,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 366, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:00.964291Z", @@ -63,7 +64,7 @@ "" ] }, - "execution_count": 63, + "execution_count": 366, "metadata": {}, "output_type": "execute_result" } @@ -75,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 367, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:01.095614Z", @@ -90,7 +91,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 368, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:01.926918Z", @@ -114,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 369, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:02.738231Z", @@ -125,14 +126,14 @@ { "data": { "text/plain": [ - "{1: ,\n", - " 2: ,\n", - " 3: ,\n", - " 4: ,\n", - " 5: }" + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: ,\n", + " 5: }" ] }, - "execution_count": 66, + "execution_count": 369, "metadata": {}, "output_type": "execute_result" } @@ -141,6 +142,183 @@ "chain.motors" ] }, + { + "cell_type": "code", + "execution_count": 370, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-3.07177948351002" + ] + }, + "execution_count": 370, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[1].pos_to_si(-2000)" + ] + }, + { + "cell_type": "code", + "execution_count": 371, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: {'goal_pos': 100,\n", + " 'moving_speed': 100,\n", + " 'present_load': 0,\n", + " 'present_position': 100,\n", + " 'present_speed': 0,\n", + " 'present_temp': 38,\n", + " 'present_voltage': 118,\n", + " 'torque_limit': 1023},\n", + " 2: {'goal_pos': 0,\n", + " 'moving_speed': 0,\n", + " 'present_load': 0,\n", + " 'present_position': 2950,\n", + " 'present_speed': 0,\n", + " 'present_temp': 39,\n", + " 'present_voltage': 118,\n", + " 'torque_limit': 1023},\n", + " 3: {'goal_pos': 0,\n", + " 'moving_speed': 0,\n", + " 'present_load': 0,\n", + " 'present_position': 2950,\n", + " 'present_speed': 0,\n", + " 'present_temp': 38,\n", + " 'present_voltage': 117,\n", + " 'torque_limit': 1023},\n", + " 4: {'goal_pos': 0,\n", + " 'moving_speed': 0,\n", + " 'present_load': 0,\n", + " 'present_position': 2947,\n", + " 'present_speed': 0,\n", + " 'present_temp': 40,\n", + " 'present_voltage': 118,\n", + " 'torque_limit': 1023}}" + ] + }, + "execution_count": 371, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.bulk_multi_read([1,2,3,4])" + ] + }, + { + "cell_type": "code", + "execution_count": 318, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1023" + ] + }, + "execution_count": 318, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[5].registers['torque_limit'].fromdxl([255,3])" + ] + }, + { + "cell_type": "code", + "execution_count": 319, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 100, 1032)" + ] + }, + "execution_count": 319, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_reg(5, 'goal_pos'), chain.get_reg(1, 'moving_speed'), chain.get_reg(1, 'present_load')" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'address': 43,\n", + " 'eeprom': False,\n", + " 'fromdxl': .>,\n", + " 'fromsi': >,\n", + " 'mode': 'r',\n", + " 'range': None,\n", + " 'size': 1,\n", + " 'todxl': .>,\n", + " 'tosi': >}" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[1].registers['present_temp'].__dict__" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(100, 100)" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_reg(1, 'goal_pos'), chain.get_reg(1, 'moving_speed')" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "chain.set_reg(1, 'goal_pos', 100), chain." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "chain.set_reg(1, 'torque_enable', 0)" + ] + }, { "cell_type": "code", "execution_count": 33, @@ -556,7 +734,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 58, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:30.882404Z", @@ -570,7 +748,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 48, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:27.958020Z", @@ -613,7 +791,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 57, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:29.343477Z", @@ -622,7 +800,7 @@ }, "outputs": [], "source": [ - "chain.set_reg(1, \"moving_speed\", 400)" + "chain.set_reg(1, \"moving_speed\", 0)" ] }, { @@ -763,7 +941,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.3" + "version": "3.5.2" }, "toc": { "base_numbering": 1, From 0eb34af5857b0e285909452d3682086ddad08623 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Tue, 14 Aug 2018 17:11:00 +0200 Subject: [PATCH 08/18] motor class added and Wrapper adapted, WIP --- dxl/dxlchain.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 80e5c1c..70fe916 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -253,17 +253,20 @@ def set_reg_si(self,id,name,v): if len(data)!=esize: raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) + def determine_control_mode(self, id): + m = self.motors[id] + if "torque_control_mode_enable" in m.registers and self.get_reg(id, "torque_control_mode_enable") == 1: + m.control_mode = m.TorqueControl + elif self.get_reg(id, "ccw_angle_limit") == 0: + m.control_mode = m.SpeedControl + else: + m.control_mode = m.PositionControl + def set_control_mode(self, id, mode): m = self.motors[id] if m.control_mode is None: - # determine control mode - if "torque_control_mode_enable" in m.registers and self.get_reg(id, "torque_control_mode_enable") == 1: - m.control_mode = m.TorqueControl - elif self.get_reg(id, "ccw_angle_limit") == 0: - m.control_mode = m.SpeedControl - else: - m.control_mode = m.PositionControl + self.determine_control_mode(self,id) if m.control_mode != mode: if m.control_mode == m.TorqueControl and "torque_control_mode_enable" in m.registers: From 1b48e07b6d4359312cd5aeb3af035503fd8324da Mon Sep 17 00:00:00 2001 From: Sebastian Blaes Date: Tue, 14 Aug 2018 18:45:20 +0200 Subject: [PATCH 09/18] added sync_write_x which allows to do sync write with any register --- dxl/dxlchain.py | 29 +++- dynamixel-hr-python3.ipynb | 276 +++++++++++++++++++++++++++---------- 2 files changed, 227 insertions(+), 78 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 623bf6e..5427f98 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -172,7 +172,7 @@ def _ping_broadcast(self): (id,data)=self._recv() l.append(id) except DxlCommunicationException: - break + break return l @@ -365,7 +365,7 @@ def bulk_read(self, ids, reg_names): if len(data) != r.size: raise DxlCommunicationException( 'Motor ID %d did not retrieve expected register %s size %d: got %d bytes' % ( - id, reg_name, r.size, len(data))) + nid, reg_name, r.size, len(data))) res.append((nid, r.fromdxl(data))) @@ -406,7 +406,7 @@ def bulk_multi_read(self, ids=None, user_regs=None): if reg not in m.registers.keys(): raise DxlConfigurationException( - "Synchronized read %s impossible on chain, register absent from motor ID %d" % (reg_name, id)) + "Read %s impossible on chain, register absent from motor ID %d" % (reg, id)) r = m.registers[reg] tot_size += r.size @@ -538,8 +538,31 @@ def sync_write_pos(self,ids,positions): self.send(Dxl.BROADCAST,payload) + def sync_write_x(self, ids, reg, vals): + """Performs a synchronized write of given register for a set of motors (if possible)""" + + # Check motor IDs, goal_pos and moving_speed register address and sizes + for id in ids: + + if id not in self.motors.keys(): + raise DxlConfigurationException("Motor ID %d cannot be found in chain"%id) + + m=self.motors[id] + + if reg not in m.registers.keys(): + raise DxlConfigurationException("Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg,id)) + r = m.registers[reg] + + # Everything is ok, build command and send + payload= [Dxl.CMD_SYNC_WRITE,r.address,r.size] + for i in range(0,len(ids)): + id=ids[i] + val=vals[i] + payload.append(id) + payload.extend(r.todxl(val)) + self.send(Dxl.BROADCAST,payload) def to_si(self,id,name,v): """Converts a motor register value from dynamixel format to SI units""" diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb index 2d4aaa4..12ebb54 100644 --- a/dynamixel-hr-python3.ipynb +++ b/dynamixel-hr-python3.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:25.669177Z", @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:25.987781Z", @@ -29,12 +29,13 @@ "source": [ "import dxlchain as Dxl\n", "import logging\n", - "import numpy as np" + "import numpy as np\n", + "import time" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:14:26.392145Z", @@ -44,13 +45,13 @@ "outputs": [], "source": [ "logger = logging.getLogger()\n", - "#logger.setLevel('DEBUG')\n", + "# logger.setLevel('DEBUG')\n", "#logger.setLevel('WARN')" ] }, { "cell_type": "code", - "execution_count": 366, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:00.964291Z", @@ -64,7 +65,7 @@ "" ] }, - "execution_count": 366, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -76,7 +77,7 @@ }, { "cell_type": "code", - "execution_count": 367, + "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:01.095614Z", @@ -86,17 +87,18 @@ "outputs": [], "source": [ "# Open the serial device\n", - "chain=Dxl.DxlChain(\"/dev/ttyACM0\",rate=1000000)" + "chain=Dxl.DxlChain(\"/dev/ttyACM0\",rate=1000000, timeout=0.4)" ] }, { "cell_type": "code", - "execution_count": 368, + "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:01.926918Z", "start_time": "2018-08-13T18:23:01.867615Z" - } + }, + "scrolled": true }, "outputs": [ { @@ -115,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 369, + "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T18:23:02.738231Z", @@ -126,14 +128,14 @@ { "data": { "text/plain": [ - "{1: ,\n", - " 2: ,\n", - " 3: ,\n", - " 4: ,\n", - " 5: }" + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: ,\n", + " 5: }" ] }, - "execution_count": 369, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -144,67 +146,163 @@ }, { "cell_type": "code", - "execution_count": 370, + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "chain.set_reg(5, 'return_delay', 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "DxlCommunicationException", + "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'goal_pos'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" + ] + } + ], + "source": [ + "chain.set_reg(4, 'goal_pos', 100)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "-3.07177948351002" + "4095" ] }, - "execution_count": 370, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "chain.motors[1].pos_to_si(-2000)" + "chain.get_reg(4, 'ccw_angle_limit')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "DxlCommunicationException", + "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_control_mode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSpeedControl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_control_mode\u001b[0;34m(self, id, mode)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mccw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrol_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" + ] + } + ], + "source": [ + "chain.set_control_mode(4, chain.motors[4].SpeedControl)" ] }, { "cell_type": "code", - "execution_count": 371, + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "ename": "DxlCommunicationException", + "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_control_mode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSpeedControl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_control_mode\u001b[0;34m(self, id, mode)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mccw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrol_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" + ] + } + ], + "source": [ + "for i in range(1,5):\n", + " chain.set_control_mode(i, chain.motors[i].SpeedControl)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "chain.sync_write_x(motors, 'moving_speed', [0] * len(motors))" + ] + }, + { + "cell_type": "code", + "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{1: {'goal_pos': 100,\n", + "{1: {'goal_pos': 102,\n", " 'moving_speed': 100,\n", " 'present_load': 0,\n", - " 'present_position': 100,\n", + " 'present_position': 101,\n", " 'present_speed': 0,\n", - " 'present_temp': 38,\n", - " 'present_voltage': 118,\n", + " 'present_temp': 37,\n", + " 'present_voltage': 119,\n", " 'torque_limit': 1023},\n", - " 2: {'goal_pos': 0,\n", + " 2: {'goal_pos': 101,\n", " 'moving_speed': 0,\n", " 'present_load': 0,\n", - " 'present_position': 2950,\n", + " 'present_position': 102,\n", " 'present_speed': 0,\n", " 'present_temp': 39,\n", - " 'present_voltage': 118,\n", + " 'present_voltage': 119,\n", " 'torque_limit': 1023},\n", - " 3: {'goal_pos': 0,\n", + " 3: {'goal_pos': 103,\n", " 'moving_speed': 0,\n", " 'present_load': 0,\n", - " 'present_position': 2950,\n", + " 'present_position': 103,\n", " 'present_speed': 0,\n", - " 'present_temp': 38,\n", - " 'present_voltage': 117,\n", + " 'present_temp': 39,\n", + " 'present_voltage': 118,\n", " 'torque_limit': 1023},\n", - " 4: {'goal_pos': 0,\n", + " 4: {'goal_pos': 100,\n", " 'moving_speed': 0,\n", - " 'present_load': 0,\n", - " 'present_position': 2947,\n", + " 'present_load': 24,\n", + " 'present_position': 102,\n", " 'present_speed': 0,\n", " 'present_temp': 40,\n", - " 'present_voltage': 118,\n", + " 'present_voltage': 119,\n", " 'torque_limit': 1023}}" ] }, - "execution_count": 371, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -213,6 +311,26 @@ "chain.bulk_multi_read([1,2,3,4])" ] }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[] == True" + ] + }, { "cell_type": "code", "execution_count": 318, @@ -422,7 +540,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 56, "metadata": { "ExecuteTime": { "end_time": "2018-08-13T16:18:36.561215Z", @@ -439,9 +557,9 @@ " ('firmware', 39),\n", " ('id', 1),\n", " ('baud_rate', 1),\n", - " ('return_delay', 250),\n", + " ('return_delay', 0),\n", " ('cw_angle_limit', 0),\n", - " ('ccw_angle_limit', 4095),\n", + " ('ccw_angle_limit', 0),\n", " ('high_temp_limit', 80),\n", " ('low_voltage_limit', 60),\n", " ('high_voltage_limit', 160),\n", @@ -449,23 +567,25 @@ " ('status_return_level', 2),\n", " ('alarm_led', 36),\n", " ('alarm_shutdown', 36),\n", - " ('torque_enable', 1),\n", + " ('torque_enable', 0),\n", " ('led', 0),\n", " ('d_gain', 0),\n", " ('i_gain', 0),\n", " ('p_gain', 32),\n", - " ('goal_pos', 2000),\n", + " ('goal_pos', 102),\n", " ('moving_speed', 100),\n", " ('torque_limit', 1023),\n", - " ('present_position', 2001),\n", + " ('present_position', 102),\n", " ('present_speed', 0),\n", - " ('present_load', 16),\n", + " ('present_load', 0),\n", " ('present_voltage', 119),\n", " ('present_temp', 37),\n", " ('registered', 0),\n", " ('moving', 0),\n", " ('lock', 0),\n", - " ('punch', 0)])),\n", + " ('punch', 0),\n", + " ('torque_control_mode_enable', 0),\n", + " ('goal_torque', 0)])),\n", " (2,\n", " OrderedDict([('model_number', 29),\n", " ('firmware', 39),\n", @@ -473,7 +593,7 @@ " ('baud_rate', 1),\n", " ('return_delay', 0),\n", " ('cw_angle_limit', 0),\n", - " ('ccw_angle_limit', 4095),\n", + " ('ccw_angle_limit', 0),\n", " ('high_temp_limit', 80),\n", " ('low_voltage_limit', 60),\n", " ('high_voltage_limit', 160),\n", @@ -481,23 +601,25 @@ " ('status_return_level', 2),\n", " ('alarm_led', 36),\n", " ('alarm_shutdown', 36),\n", - " ('torque_enable', 1),\n", + " ('torque_enable', 0),\n", " ('led', 0),\n", " ('d_gain', 0),\n", " ('i_gain', 0),\n", " ('p_gain', 32),\n", - " ('goal_pos', 2000),\n", - " ('moving_speed', 500),\n", + " ('goal_pos', 101),\n", + " ('moving_speed', 0),\n", " ('torque_limit', 1023),\n", - " ('present_position', 2002),\n", + " ('present_position', 101),\n", " ('present_speed', 0),\n", - " ('present_load', 16),\n", + " ('present_load', 0),\n", " ('present_voltage', 119),\n", - " ('present_temp', 38),\n", + " ('present_temp', 39),\n", " ('registered', 0),\n", " ('moving', 0),\n", " ('lock', 0),\n", - " ('punch', 0)])),\n", + " ('punch', 0),\n", + " ('torque_control_mode_enable', 0),\n", + " ('goal_torque', 0)])),\n", " (3,\n", " OrderedDict([('model_number', 29),\n", " ('firmware', 39),\n", @@ -505,7 +627,7 @@ " ('baud_rate', 1),\n", " ('return_delay', 0),\n", " ('cw_angle_limit', 0),\n", - " ('ccw_angle_limit', 4095),\n", + " ('ccw_angle_limit', 0),\n", " ('high_temp_limit', 80),\n", " ('low_voltage_limit', 60),\n", " ('high_voltage_limit', 160),\n", @@ -513,23 +635,25 @@ " ('status_return_level', 2),\n", " ('alarm_led', 36),\n", " ('alarm_shutdown', 36),\n", - " ('torque_enable', 1),\n", + " ('torque_enable', 0),\n", " ('led', 0),\n", " ('d_gain', 0),\n", " ('i_gain', 0),\n", " ('p_gain', 32),\n", - " ('goal_pos', 2000),\n", - " ('moving_speed', 1000),\n", + " ('goal_pos', 103),\n", + " ('moving_speed', 0),\n", " ('torque_limit', 1023),\n", - " ('present_position', 2002),\n", + " ('present_position', 103),\n", " ('present_speed', 0),\n", - " ('present_load', 24),\n", + " ('present_load', 0),\n", " ('present_voltage', 118),\n", - " ('present_temp', 38),\n", + " ('present_temp', 39),\n", " ('registered', 0),\n", " ('moving', 0),\n", " ('lock', 0),\n", - " ('punch', 0)])),\n", + " ('punch', 0),\n", + " ('torque_control_mode_enable', 0),\n", + " ('goal_torque', 0)])),\n", " (4,\n", " OrderedDict([('model_number', 29),\n", " ('firmware', 39),\n", @@ -550,24 +674,26 @@ " ('d_gain', 0),\n", " ('i_gain', 0),\n", " ('p_gain', 32),\n", - " ('goal_pos', 2952),\n", - " ('moving_speed', 1300),\n", + " ('goal_pos', 100),\n", + " ('moving_speed', 0),\n", " ('torque_limit', 1023),\n", - " ('present_position', 2947),\n", + " ('present_position', 102),\n", " ('present_speed', 0),\n", - " ('present_load', 1048),\n", + " ('present_load', 24),\n", " ('present_voltage', 119),\n", - " ('present_temp', 39),\n", + " ('present_temp', 40),\n", " ('registered', 0),\n", " ('moving', 0),\n", " ('lock', 0),\n", - " ('punch', 0)])),\n", + " ('punch', 0),\n", + " ('torque_control_mode_enable', 0),\n", + " ('goal_torque', 0)])),\n", " (5,\n", " OrderedDict([('model_number', 12),\n", " ('firmware', 24),\n", " ('id', 5),\n", " ('baud_rate', 1),\n", - " ('return_delay', 250),\n", + " ('return_delay', 0),\n", " ('cw_angle_limit', 0),\n", " ('ccw_angle_limit', 1023),\n", " ('high_temp_limit', 70),\n", @@ -583,21 +709,21 @@ " ('ccw_compliance_margin', 1),\n", " ('cw_compliance_slope', 32),\n", " ('ccw_compliance_slope', 32),\n", - " ('goal_pos', 1000),\n", - " ('moving_speed', 100),\n", + " ('goal_pos', 100),\n", + " ('moving_speed', 0),\n", " ('torque_limit', 1023),\n", - " ('present_position', 999),\n", + " ('present_position', 101),\n", " ('present_speed', 0),\n", " ('present_load', 0),\n", " ('present_voltage', 121),\n", - " ('present_temp', 38),\n", + " ('present_temp', 40),\n", " ('registered', 0),\n", " ('moving', 0),\n", " ('lock', 0),\n", " ('punch', 32)]))])" ] }, - "execution_count": 19, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } From 5d6cbcbf16f6ea4c4b8ada20f582d2b37c0e2797 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Tue, 14 Aug 2018 23:01:13 +0200 Subject: [PATCH 10/18] New TickingThread implemented, substituting the primitives. works like a charm. New interface works. Tested with a small chain of motors. AlLogger also works --- dxl/dxlchain.py | 31 +----- dynamixel-hr-python3.ipynb | 201 +++++++++++++++++++++---------------- 2 files changed, 117 insertions(+), 115 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 76fdf52..e8d51b1 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -266,7 +266,7 @@ def determine_control_mode(self, id): def set_control_mode(self, id, mode): m = self.motors[id] if m.control_mode is None: - self.determine_control_mode(self,id) + self.determine_control_mode(id) if m.control_mode != mode: if m.control_mode == m.TorqueControl and "torque_control_mode_enable" in m.registers: @@ -512,34 +512,7 @@ def sync_write_pos_speed(self,ids,positions,speeds): self.send(Dxl.BROADCAST,payload) def sync_write_pos(self,ids,positions): - """Performs a synchronized write of 'goal_pos' register for a set of motors (if possible)""" - reg=None - # Check motor IDs, goal_pos and moving_speed register address and sizes - for id in ids: - if id not in self.motors.keys(): - raise DxlConfigurationException("Motor ID %d cannot be found in chain"%id) - m=self.motors[id] - reg_name="goal_pos" - if reg_name not in m.registers.keys(): - raise DxlConfigurationException("Synchronized write %s impossible on chain, register absent from motor ID %d"%(reg_name,id)) - r=m.registers[reg_name] - if reg==None: - reg=r - else: - if reg.address!=r.address: - raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register address for motor ID %d"%(reg_name,id)) - if reg.size!=r.size: - raise DxlConfigurationException("Synchronized write %s impossible on chain, mismatch in register size for motor ID %d"(reg_name,id)) - - # Everything is ok, build command and send - payload= [Dxl.CMD_SYNC_WRITE,reg.address,reg.size] - for i in range(0,len(ids)): - id=ids[i] - pos=positions[i] - payload.append(id) - payload.extend(reg.todxl(pos)) - - self.send(Dxl.BROADCAST,payload) + self.sync_write_x(ids,"goal_pos", positions) def sync_write_x(self, ids, reg, vals): """Performs a synchronized write of given register for a set of motors (if possible)""" diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb index 12ebb54..d021667 100644 --- a/dynamixel-hr-python3.ipynb +++ b/dynamixel-hr-python3.ipynb @@ -5,8 +5,8 @@ "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:14:25.669177Z", - "start_time": "2018-08-13T18:14:25.662316Z" + "end_time": "2018-08-14T17:11:58.308042Z", + "start_time": "2018-08-14T17:11:58.299785Z" } }, "outputs": [], @@ -21,8 +21,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:14:25.987781Z", - "start_time": "2018-08-13T18:14:25.972500Z" + "end_time": "2018-08-14T17:11:58.527805Z", + "start_time": "2018-08-14T17:11:58.460258Z" } }, "outputs": [], @@ -38,8 +38,8 @@ "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:14:26.392145Z", - "start_time": "2018-08-13T18:14:26.388427Z" + "end_time": "2018-08-14T17:11:58.614295Z", + "start_time": "2018-08-14T17:11:58.610558Z" } }, "outputs": [], @@ -54,8 +54,8 @@ "execution_count": 4, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:23:00.964291Z", - "start_time": "2018-08-13T18:23:00.947831Z" + "end_time": "2018-08-14T17:11:58.787335Z", + "start_time": "2018-08-14T17:11:58.772478Z" } }, "outputs": [ @@ -80,8 +80,8 @@ "execution_count": 5, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:23:01.095614Z", - "start_time": "2018-08-13T18:23:01.091078Z" + "end_time": "2018-08-14T17:11:59.243184Z", + "start_time": "2018-08-14T17:11:59.238158Z" } }, "outputs": [], @@ -95,8 +95,8 @@ "execution_count": 6, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:23:01.926918Z", - "start_time": "2018-08-13T18:23:01.867615Z" + "end_time": "2018-08-14T17:12:00.676539Z", + "start_time": "2018-08-14T17:12:00.262081Z" }, "scrolled": true }, @@ -105,7 +105,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[1, 2, 3, 4, 5]\n" + "[1, 2, 3, 4]\n" ] } ], @@ -117,25 +117,24 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 14, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T18:23:02.738231Z", - "start_time": "2018-08-13T18:23:02.731851Z" + "end_time": "2018-08-14T16:58:00.200561Z", + "start_time": "2018-08-14T16:58:00.194824Z" } }, "outputs": [ { "data": { "text/plain": [ - "{1: ,\n", - " 2: ,\n", - " 3: ,\n", - " 4: ,\n", - " 5: }" + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: }" ] }, - "execution_count": 7, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -155,33 +154,27 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "ename": "DxlCommunicationException", - "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'goal_pos'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" - ] + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:58:23.879049Z", + "start_time": "2018-08-14T16:58:23.874796Z" } - ], + }, + "outputs": [], "source": [ - "chain.set_reg(4, 'goal_pos', 100)" + "chain.set_reg(4, 'moving_speed', 500)" ] }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:56:54.123080Z", + "start_time": "2018-08-14T16:56:54.116451Z" + } + }, "outputs": [ { "data": { @@ -189,7 +182,7 @@ "4095" ] }, - "execution_count": 14, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -200,52 +193,28 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "ename": "DxlCommunicationException", - "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_control_mode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSpeedControl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_control_mode\u001b[0;34m(self, id, mode)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mccw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrol_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" - ] + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:58:10.623553Z", + "start_time": "2018-08-14T16:58:10.616236Z" } - ], + }, + "outputs": [], "source": [ "chain.set_control_mode(4, chain.motors[4].SpeedControl)" ] }, { "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "ename": "DxlCommunicationException", - "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_control_mode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSpeedControl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_control_mode\u001b[0;34m(self, id, mode)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mccw_angle_limit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"cw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_reg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"ccw_angle_limit\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontrol_mode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mset_reg\u001b[0;34m(self, id, name, v)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0mreg\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mregisters\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetRegisterCmd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mreg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtodxl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mcmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mlogging\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Motor ID %d set register %s to %d'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0mesize\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mcomm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0;34m\"\"\"Communicate with the Dynamixel by sending a packet and retrieving the response\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_comm\u001b[0;34m(self, id, packet)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_comm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mpacket\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 136\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 138\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mchecksum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/projects/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" - ] + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:58:31.804370Z", + "start_time": "2018-08-14T16:58:31.796794Z" } - ], + }, + "outputs": [], "source": [ "for i in range(1,5):\n", " chain.set_control_mode(i, chain.motors[i].SpeedControl)" @@ -253,11 +222,45 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:59:44.686316Z", + "start_time": "2018-08-14T16:59:44.682050Z" + } + }, + "outputs": [], + "source": [ + "chain.sync_write_x(motors, 'moving_speed', [1023] * len(motors))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:59:03.158194Z", + "start_time": "2018-08-14T16:59:03.147147Z" + } + }, + "outputs": [], + "source": [ + "for i in range(1,5):\n", + " chain.set_control_mode(i, chain.motors[i].PositionControl)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:59:49.384658Z", + "start_time": "2018-08-14T16:59:49.379340Z" + } + }, "outputs": [], "source": [ - "chain.sync_write_x(motors, 'moving_speed', [0] * len(motors))" + "chain.sync_write_x(motors, 'goal_pos', [2000] * len(motors))" ] }, { @@ -451,6 +454,32 @@ "chain.goto(5, 100, speed=100)" ] }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T17:12:57.322106Z", + "start_time": "2018-08-14T17:12:57.315334Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "24.162617417289816" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors[1].speed_to_si(2024)\n", + " " + ] + }, { "cell_type": "code", "execution_count": 34, @@ -1067,7 +1096,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.6.3" }, "toc": { "base_numbering": 1, From fb5fe514df68829bd75fd727fb2fefb2c2599819 Mon Sep 17 00:00:00 2001 From: Sebastian Blaes Date: Thu, 16 Aug 2018 17:12:19 +0200 Subject: [PATCH 11/18] added _do_bulk_sensing and _do_serial_sensing to PoppyWrapper --- dxl/dxlchain.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index e8d51b1..d8a268b 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -215,6 +215,54 @@ def get_reg(self,id,name): logging.info('Motor ID %d get register %s: %d'%(id,name,v) ) return v + def get_multi_reg(self,id,user_regs=None): + return self.get_multi_reg_si(id,user_regs=user_regs,to_si=False) + + def get_multi_reg_si(self,id,user_regs=None,to_si=True): + """Read consecutive list of registers from a motor""" + if id not in self.motors.keys(): + raise DxlConfigurationException('Motor ID %d does not exist on the chain'%(id)) + m=self.motors[id] + + "Needs to be consecutive list of registers" + regs = ['goal_pos', + 'moving_speed', + 'torque_limit', + 'present_position', + 'present_speed', + 'present_load', + 'present_voltage', + 'present_temp' + ] + + if user_regs is not None: + regs = user_regs + + r = m.registers[regs[0]] + fst_addr = r.address + + tot_size = 0 + for reg in regs: + r = m.registers[reg] + tot_size += r.size + + cmd = [Dxl.CMD_READ_DATA,fst_addr,tot_size] + + (nid,data)=self.comm(id,cmd) + if len(data)!=tot_size: + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) + + response = dict() + counter = 0 + for reg in regs: + r = m.registers[reg] + response[reg] = r.fromdxl(data[counter:counter+r.size]) + if to_si: + response[reg] = r.tosi(response[reg]) + counter += r.size + + return response + def get_reg_si(self,id,name): """Read a named register from a motor and returns value converted to SI units""" if id not in self.motors.keys(): From 0fb481e6a7a96493083614c61e2d8f7988c578d9 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Thu, 16 Aug 2018 19:01:11 +0200 Subject: [PATCH 12/18] dxl error handling. Still problems with communicaituon Servos get enabled every time --- dxl/dxlchain.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index d8a268b..2087d37 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -114,11 +114,27 @@ def _recv(self): if len(data)>0: error=data[0] + if error & 0x01!=0: # Bit 0: Input Voltage Error + logging.warning("Dynamixel: Voltage Error (id %i)" % (id)) + if error & 0x02 != 0: # Bit 1: Angle Limit Error + logging.info("Dynamixel: angle limit error (id %i)" % (id)) + if error & 0x04 != 0: # Bit 2: Overheating Error + logging.warning("Dynamixel: overheating (id %i)" % (id)) + self.error_occurred_flag = True + if error & 0x08 != 0: # Bit 3: Range Error + logging.info("Dynamixel: range error (id %i)" % (id)) + if error & 0x10 != 0: # Bit 4: CheckSum Error + logging.warning("Dynamixel: angle limit error (id %i)" % (id)) + self.error_occurred_flag = True + if error & 0x20 != 0: # Bit 5: Overload Error + logging.info("Dynamixel: overload (id %i)" % (id)) + if error & 0x40 != 0: # Bit 6: Instruction Error + logging.warning("Dynamixel: instruction error (id %i)" % (id)) + self.error_occurred_flag = True + if error & 0x80 != 0: # Bit 7: Unknown Error + logging.warning("Dynamixel: unknown error (id %i)" % (id)) + self.error_occurred_flag = True - if error!=0 and error!=2: # skip angle errors - # TODO Distinguish communication/Hardware errors - raise DxlCommunicationException('Received error code from motor %d: %d'%(id,error)) - checksum=self.checksum(header[2:]+data[:-1]) if checksum!=data[-1]: raise DxlCommunicationException('Invalid checksum') From 8809de8081165f698059d350a198c02f0ae125f3 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Tue, 9 Oct 2018 20:19:22 +0200 Subject: [PATCH 13/18] Communication works now at high freq. There is some weirdness with setting the torque_limit from the main thread --- dxl/dxlchain.py | 8 +- dynamixel-hr-python3.ipynb | 278 ++++++++++++++++++++++++++++++++----- 2 files changed, 246 insertions(+), 40 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 2087d37..d1dbd5d 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -266,7 +266,7 @@ def get_multi_reg_si(self,id,user_regs=None,to_si=True): (nid,data)=self.comm(id,cmd) if len(data)!=tot_size: - raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,name,esize,len(data))) + raise DxlCommunicationException('Motor ID %d did not retrieve expected register %s size %d: got %d bytes'%(id,regs[0],tot_size,len(data))) response = dict() counter = 0 @@ -346,6 +346,7 @@ def set_control_mode(self, id, mode): elif mode == m.PositionControl: if m.ccw_angle_limit is 0 or m.ccw_angle_limit is None: m.cw_angle_limit, m.ccw_angle_limit = m.registers["goal_pos"].range + # print(id, m.cw_angle_limit, m.ccw_angle_limit) self.set_reg(id, "cw_angle_limit", m.cw_angle_limit) self.set_reg(id, "ccw_angle_limit", m.ccw_angle_limit) # self.set_reg(id, "goal_pos", self.get_reg(id, "present_position")) @@ -521,6 +522,7 @@ def sync_read_temp(self, ids=None): return self._sync_read_X_wrapper(ids, 'present_temp') # Todo: use sync read if it works + # it uses bulk_read and this only works with MX servos def _sync_read_X_wrapper(self, ids, register): if ids is None: ids = self.motors @@ -528,7 +530,9 @@ def _sync_read_X_wrapper(self, ids, register): def sync_write_pos_speed(self,ids,positions,speeds): - """Performs a synchronized write of 'goal_pos' and 'moving_speed' registers for a set of motors (if possible)""" + """Performs a synchronized write of 'goal_pos' and 'moving_speed' registers for a set of motors (if possible) + The motors get automatically enabled if they get a goal position or the like + """ regpos=None regspeed=None # Check motor IDs, goal_pos and moving_speed register address and sizes diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb index d021667..7611af4 100644 --- a/dynamixel-hr-python3.ipynb +++ b/dynamixel-hr-python3.ipynb @@ -5,8 +5,8 @@ "execution_count": 1, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:11:58.308042Z", - "start_time": "2018-08-14T17:11:58.299785Z" + "end_time": "2018-10-09T07:27:34.219935Z", + "start_time": "2018-10-09T07:27:34.213972Z" } }, "outputs": [], @@ -21,8 +21,8 @@ "execution_count": 2, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:11:58.527805Z", - "start_time": "2018-08-14T17:11:58.460258Z" + "end_time": "2018-10-09T07:27:34.742762Z", + "start_time": "2018-10-09T07:27:34.673882Z" } }, "outputs": [], @@ -38,8 +38,8 @@ "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:11:58.614295Z", - "start_time": "2018-08-14T17:11:58.610558Z" + "end_time": "2018-10-09T07:27:35.126303Z", + "start_time": "2018-10-09T07:27:35.122011Z" } }, "outputs": [], @@ -54,8 +54,8 @@ "execution_count": 4, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:11:58.787335Z", - "start_time": "2018-08-14T17:11:58.772478Z" + "end_time": "2018-10-09T07:27:35.588112Z", + "start_time": "2018-10-09T07:27:35.570340Z" } }, "outputs": [ @@ -80,8 +80,8 @@ "execution_count": 5, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:11:59.243184Z", - "start_time": "2018-08-14T17:11:59.238158Z" + "end_time": "2018-10-09T07:27:36.515504Z", + "start_time": "2018-10-09T07:27:36.511240Z" } }, "outputs": [], @@ -95,8 +95,8 @@ "execution_count": 6, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T17:12:00.676539Z", - "start_time": "2018-08-14T17:12:00.262081Z" + "end_time": "2018-10-09T07:27:38.143716Z", + "start_time": "2018-10-09T07:27:37.658709Z" }, "scrolled": true }, @@ -105,7 +105,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[1, 2, 3, 4]\n" + "[11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 36, 37, 51, 52, 53, 54]\n" ] } ], @@ -117,24 +117,45 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 7, "metadata": { "ExecuteTime": { - "end_time": "2018-08-14T16:58:00.200561Z", - "start_time": "2018-08-14T16:58:00.194824Z" + "end_time": "2018-10-09T07:27:39.193363Z", + "start_time": "2018-10-09T07:27:39.185757Z" } }, "outputs": [ { "data": { "text/plain": [ - "{1: ,\n", - " 2: ,\n", - " 3: ,\n", - " 4: }" + "{11: ,\n", + " 12: ,\n", + " 13: ,\n", + " 14: ,\n", + " 15: ,\n", + " 21: ,\n", + " 22: ,\n", + " 23: ,\n", + " 24: ,\n", + " 25: ,\n", + " 31: ,\n", + " 32: ,\n", + " 33: ,\n", + " 34: ,\n", + " 35: ,\n", + " 41: ,\n", + " 42: ,\n", + " 43: ,\n", + " 44: ,\n", + " 36: ,\n", + " 37: ,\n", + " 51: ,\n", + " 52: ,\n", + " 53: ,\n", + " 54: }" ] }, - "execution_count": 14, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -143,6 +164,137 @@ "chain.motors" ] }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:27:55.945288Z", + "start_time": "2018-10-09T07:27:55.923584Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[chain.get_reg(i, 'return_delay') for i in motors]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:28:06.740758Z", + "start_time": "2018-10-09T07:28:06.721096Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80,\n", + " 80]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[chain.get_reg(i, 'torque_limit') for i in motors]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:28:18.266691Z", + "start_time": "2018-10-09T07:28:18.249595Z" + } + }, + "outputs": [], + "source": [ + "_ = [chain.set_reg(i, 'torque_limit',80) for i in motors]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:28:33.744135Z", + "start_time": "2018-10-09T07:28:33.712784Z" + } + }, + "outputs": [], + "source": [ + "for i in motors:\n", + " chain.set_control_mode(i, chain.motors[i].PositionControl)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:29:49.092444Z", + "start_time": "2018-10-09T07:29:49.075100Z" + } + }, + "outputs": [], + "source": [ + "chain.enable()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:31:00.313516Z", + "start_time": "2018-10-09T07:31:00.294893Z" + } + }, + "outputs": [], + "source": [ + "chain.disable()" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -763,11 +915,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T17:55:16.297583Z", - "start_time": "2018-08-13T17:55:16.081601Z" + "end_time": "2018-10-09T07:30:28.952666Z", + "start_time": "2018-10-09T07:30:28.728390Z" } }, "outputs": [], @@ -780,21 +932,38 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 14, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T17:36:07.203386Z", - "start_time": "2018-08-13T17:36:03.807102Z" + "end_time": "2018-10-09T07:30:29.709550Z", + "start_time": "2018-10-09T07:30:29.222843Z" } }, - "outputs": [], + "outputs": [ + { + "ename": "DxlCommunicationException", + "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;31m#time.sleep(0.01)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mnew_pos\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0mnew_pos\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;31m#for i in motors:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36msync_read_pos\u001b[0;34m(self, ids)\u001b[0m\n\u001b[1;32m 510\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 512\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sync_read_X_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'present_position'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 513\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 514\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_sync_read_X_wrapper\u001b[0;34m(self, ids, register)\u001b[0m\n\u001b[1;32m 525\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mids\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 526\u001b[0m \u001b[0mids\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 527\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbulk_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 528\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mbulk_read\u001b[0;34m(self, ids, reg_names)\u001b[0m\n\u001b[1;32m 425\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 426\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 427\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 428\u001b[0m \u001b[0mm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 429\u001b[0m \u001b[0mreg_name\u001b[0m \u001b[0;34m=\u001b[0m\u001b[0mreg_names\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mrecv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\"\"\"Wait for a response on the serial, validate it, raise errors if any, return id and data if any\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" + ] + } + ], "source": [ "data=[]\n", "ctrl=[]\n", "motors=[1,2,3,4]\n", "for t in range(1000):\n", " pos = np.sin(t/50.0)*1000+2048\n", - " chain.sync_write_pos_speed(motors, [pos] * len(motors), [i*200 + 100 for i in motors])\n", + " #chain.sync_write_pos_speed(motors, [pos] * len(motors), [i*200 + 100 for i in motors])\n", " #time.sleep(0.01) \n", " new_pos = [0] * len(motors) \n", " for i,v in chain.sync_read_pos(motors).items():\n", @@ -808,6 +977,34 @@ "data=np.asarray(data)" ] }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:29:49.092444Z", + "start_time": "2018-10-09T07:29:49.075100Z" + } + }, + "outputs": [], + "source": [ + "chain.enable()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:31:00.313516Z", + "start_time": "2018-10-09T07:31:00.294893Z" + } + }, + "outputs": [], + "source": [ + "chain.disable()" + ] + }, { "cell_type": "code", "execution_count": 133, @@ -849,28 +1046,33 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 20, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T16:45:59.131999Z", - "start_time": "2018-08-13T16:45:59.125062Z" + "end_time": "2018-10-09T07:33:07.633997Z", + "start_time": "2018-10-09T07:33:07.211953Z" } }, "outputs": [ { - "ename": "AttributeError", - "evalue": "'DxlChain' object has no attribute 'sync_read'", + "ename": "DxlCommunicationException", + "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msync_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'present_position'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: 'DxlChain' object has no attribute 'sync_read'" + "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m43\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m44\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m36\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36msync_read_pos\u001b[0;34m(self, ids)\u001b[0m\n\u001b[1;32m 510\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 512\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sync_read_X_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'present_position'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 513\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 514\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_sync_read_X_wrapper\u001b[0;34m(self, ids, register)\u001b[0m\n\u001b[1;32m 525\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mids\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 526\u001b[0m \u001b[0mids\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 527\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbulk_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 528\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mbulk_read\u001b[0;34m(self, ids, reg_names)\u001b[0m\n\u001b[1;32m 425\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 426\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 427\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 428\u001b[0m \u001b[0mm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 429\u001b[0m \u001b[0mreg_name\u001b[0m \u001b[0;34m=\u001b[0m\u001b[0mreg_names\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mrecv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\"\"\"Wait for a response on the serial, validate it, raise errors if any, return id and data if any\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" ] } ], "source": [ - "chain.sync_read([1,2,3],'present_position')" + "chain.sync_read_pos([43,44,36])" ] }, { From 2f552e515548c18be824c6c811899188258d1ed0 Mon Sep 17 00:00:00 2001 From: Sebastian Blaes Date: Mon, 15 Oct 2018 15:17:14 +0200 Subject: [PATCH 14/18] added compliant motors --- dxl/dxlchain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index d1dbd5d..929351e 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -726,6 +726,7 @@ def disable(self,ids=None): """Disable all the motors on the chain""" ids=self.get_motors(ids) for id in ids: + time.sleep(0.05) self.set_reg(id,"torque_enable",0) def wait_stopped(self,ids=None): From 562b2eb058f5ea332319a1f034d5b4fa06e4b561 Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Thu, 18 Oct 2018 10:18:26 +0200 Subject: [PATCH 15/18] check load sensors --- dynamixel-hr-python3.ipynb | 267 +++++++++++++++++++++++-------------- 1 file changed, 166 insertions(+), 101 deletions(-) diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb index 7611af4..f8ee2c0 100644 --- a/dynamixel-hr-python3.ipynb +++ b/dynamixel-hr-python3.ipynb @@ -2,11 +2,11 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 24, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:34.219935Z", - "start_time": "2018-10-09T07:27:34.213972Z" + "end_time": "2018-10-18T08:07:27.301109Z", + "start_time": "2018-10-18T08:07:27.296851Z" } }, "outputs": [], @@ -18,11 +18,11 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 25, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:34.742762Z", - "start_time": "2018-10-09T07:27:34.673882Z" + "end_time": "2018-10-18T08:07:27.443580Z", + "start_time": "2018-10-18T08:07:27.439735Z" } }, "outputs": [], @@ -35,11 +35,11 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 26, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:35.126303Z", - "start_time": "2018-10-09T07:27:35.122011Z" + "end_time": "2018-10-18T08:07:27.588040Z", + "start_time": "2018-10-18T08:07:27.584370Z" } }, "outputs": [], @@ -51,11 +51,11 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 27, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:35.588112Z", - "start_time": "2018-10-09T07:27:35.570340Z" + "end_time": "2018-10-18T08:07:27.879947Z", + "start_time": "2018-10-18T08:07:27.872520Z" } }, "outputs": [ @@ -65,7 +65,7 @@ "" ] }, - "execution_count": 4, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -77,11 +77,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 30, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:36.515504Z", - "start_time": "2018-10-09T07:27:36.511240Z" + "end_time": "2018-10-18T08:07:44.879462Z", + "start_time": "2018-10-18T08:07:44.875023Z" } }, "outputs": [], @@ -92,11 +92,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 31, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:38.143716Z", - "start_time": "2018-10-09T07:27:37.658709Z" + "end_time": "2018-10-18T08:07:45.815680Z", + "start_time": "2018-10-18T08:07:45.402646Z" }, "scrolled": true }, @@ -105,7 +105,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, 41, 42, 43, 44, 36, 37, 51, 52, 53, 54]\n" + "[1, 2, 3, 4]\n" ] } ], @@ -117,45 +117,24 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 32, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:39.193363Z", - "start_time": "2018-10-09T07:27:39.185757Z" + "end_time": "2018-10-18T08:07:46.828541Z", + "start_time": "2018-10-18T08:07:46.820019Z" } }, "outputs": [ { "data": { "text/plain": [ - "{11: ,\n", - " 12: ,\n", - " 13: ,\n", - " 14: ,\n", - " 15: ,\n", - " 21: ,\n", - " 22: ,\n", - " 23: ,\n", - " 24: ,\n", - " 25: ,\n", - " 31: ,\n", - " 32: ,\n", - " 33: ,\n", - " 34: ,\n", - " 35: ,\n", - " 41: ,\n", - " 42: ,\n", - " 43: ,\n", - " 44: ,\n", - " 36: ,\n", - " 37: ,\n", - " 51: ,\n", - " 52: ,\n", - " 53: ,\n", - " 54: }" + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: }" ] }, - "execution_count": 7, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -166,21 +145,21 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 33, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:27:55.945288Z", - "start_time": "2018-10-09T07:27:55.923584Z" + "end_time": "2018-10-18T08:07:48.062391Z", + "start_time": "2018-10-18T08:07:48.055137Z" } }, "outputs": [ { "data": { "text/plain": [ - "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" + "[0, 0, 0, 0]" ] }, - "execution_count": 8, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -662,21 +641,21 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 19, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T16:15:14.368008Z", - "start_time": "2018-08-13T16:15:14.358909Z" + "end_time": "2018-10-18T07:44:14.641236Z", + "start_time": "2018-10-18T07:44:14.633337Z" } }, "outputs": [ { "data": { "text/plain": [ - "{1: 2001, 2: 2002, 3: 2002, 4: 2948, 5: 999}" + "{1: 1397, 2: 1574, 3: 1551, 4: 2288}" ] }, - "execution_count": 15, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -915,11 +894,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:30:28.952666Z", - "start_time": "2018-10-09T07:30:28.728390Z" + "end_time": "2018-10-18T07:44:01.774121Z", + "start_time": "2018-10-18T07:44:01.586476Z" } }, "outputs": [], @@ -932,58 +911,47 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 35, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:30:29.709550Z", - "start_time": "2018-10-09T07:30:29.222843Z" + "end_time": "2018-10-18T08:08:13.515204Z", + "start_time": "2018-10-18T08:08:00.549651Z" } }, - "outputs": [ - { - "ename": "DxlCommunicationException", - "evalue": "Could not read first 4 bytes of expected response, got 0 bytes", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mDxlCommunicationException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;31m#time.sleep(0.01)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mnew_pos\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mchain\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0mnew_pos\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;31m#for i in motors:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36msync_read_pos\u001b[0;34m(self, ids)\u001b[0m\n\u001b[1;32m 510\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 511\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_pos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 512\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sync_read_X_wrapper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'present_position'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 513\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 514\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msync_read_speed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mids\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_sync_read_X_wrapper\u001b[0;34m(self, ids, register)\u001b[0m\n\u001b[1;32m 525\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mids\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 526\u001b[0m \u001b[0mids\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 527\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbulk_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mregister\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 528\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 529\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mbulk_read\u001b[0;34m(self, ids, reg_names)\u001b[0m\n\u001b[1;32m 425\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 426\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 427\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 428\u001b[0m \u001b[0mm\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmotors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 429\u001b[0m \u001b[0mreg_name\u001b[0m \u001b[0;34m=\u001b[0m\u001b[0mreg_names\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mids\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36mrecv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\"\"\"Wait for a response on the serial, validate it, raise errors if any, return id and data if any\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlock\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_recv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~_local/projects/Poppy/poppy-al/lib/dynamixel_hr/dxl/dxlchain.py\u001b[0m in \u001b[0;36m_recv\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 105\u001b[0m \u001b[0mheader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marray\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'B'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mport\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 106\u001b[0m \u001b[0;32mif\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 107\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mDxlCommunicationException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Could not read first 4 bytes of expected response, got %d bytes'\u001b[0m\u001b[0;34m%\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 108\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 109\u001b[0m \u001b[0mid\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mexpectedsize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheader\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDxlCommunicationException\u001b[0m: Could not read first 4 bytes of expected response, got 0 bytes" - ] - } - ], + "outputs": [], "source": [ "data=[]\n", + "loaddata=[]\n", "ctrl=[]\n", "motors=[1,2,3,4]\n", "for t in range(1000):\n", " pos = np.sin(t/50.0)*1000+2048\n", - " #chain.sync_write_pos_speed(motors, [pos] * len(motors), [i*200 + 100 for i in motors])\n", - " #time.sleep(0.01) \n", + " chain.sync_write_pos_speed(motors, [pos] * len(motors), [i*200 + 100 for i in motors])\n", + " time.sleep(0.01) \n", " new_pos = [0] * len(motors) \n", + " new_load = [0] * len(motors) \n", " for i,v in chain.sync_read_pos(motors).items():\n", " new_pos[i-1]=v\n", + " for i,v in chain.sync_read_load(motors).items():\n", + " new_load[i-1]=v\n", " #for i in motors:\n", " # v = chain.get_position(i)\n", " # new_pos[i-1]=v[i]\n", " \n", " data.append(new_pos)\n", + " loaddata.append(new_load)\n", " ctrl.append(pos)\n", - "data=np.asarray(data)" + "data=np.asarray(data)\n", + "loaddata=np.asarray(loaddata)" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 17, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:29:49.092444Z", - "start_time": "2018-10-09T07:29:49.075100Z" + "end_time": "2018-10-18T07:44:08.263505Z", + "start_time": "2018-10-18T07:44:08.258110Z" } }, "outputs": [], @@ -993,11 +961,11 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 37, "metadata": { "ExecuteTime": { - "end_time": "2018-10-09T07:31:00.313516Z", - "start_time": "2018-10-09T07:31:00.294893Z" + "end_time": "2018-10-18T08:08:13.648718Z", + "start_time": "2018-10-18T08:08:13.643729Z" } }, "outputs": [], @@ -1007,27 +975,27 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 57, "metadata": { "ExecuteTime": { - "end_time": "2018-08-13T17:36:08.917766Z", - "start_time": "2018-08-13T17:36:08.776044Z" + "end_time": "2018-10-18T08:16:20.091737Z", + "start_time": "2018-10-18T08:16:19.962113Z" } }, "outputs": [ { "data": { "text/plain": [ - "[]" + "[]" ] }, - "execution_count": 133, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1039,9 +1007,106 @@ "source": [ "plt.plot(ctrl)\n", "plt.plot(data[:,0])\n", - "plt.plot(data[:,1])\n", - "plt.plot(data[:,2])\n", - "plt.plot(data[:,3])" + "plt.plot(loaddata[:,0])\n", + "plt.plot(correctload)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:12:13.920438Z", + "start_time": "2018-10-18T08:12:13.909313Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n", + " -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", + " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(((loaddata[:,0]>>10) & 1)*2-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:16:11.118761Z", + "start_time": "2018-10-18T08:16:11.113816Z" + } + }, + "outputs": [], + "source": [ + "sign = -((loaddata[:,0]>>10) & 1)*2+1\n", + "correctload = (loaddata[:,0]&1023) * sign" ] }, { From 0569fd7011ad7832c1caebb8b9eeaac40685adad Mon Sep 17 00:00:00 2001 From: Georg Martius Date: Thu, 25 Apr 2019 14:18:53 +0200 Subject: [PATCH 16/18] updated readme --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 5e6ce01..25b3a7f 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,13 @@ HumaRobotics Dynamixel Library ######################################################## -Changes in this fork +Changes in this fork (by Sebastian Blaes) ========================== -2018-07-10: Ported library to Python 3. Added bulk read. + * Ported library to Python 3. + * Added bulk read (for all MX types) + * support for control modes (position, velocity, torque) added + * added a notebook (ipynb) for quick testing Original ========================== From 94059f66fd851787726f2c95f1ce02adbaef86cb Mon Sep 17 00:00:00 2001 From: sblaes Date: Wed, 18 Sep 2019 14:26:34 +0200 Subject: [PATCH 17/18] made communication more robust by ignoring most of the errors and trying to recover to a working state --- dxl/dxlchain.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 929351e..908f692 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -356,7 +356,7 @@ def set_control_mode(self, id, mode): self.set_reg(id, "torque_control_mode_enable", 1) m.control_mode = mode else: - logging.warning("Set Torque mode failed: Motor id {}" % (id)) + logging.warning(f"Set Torque mode failed: Motor id {id}") @@ -439,8 +439,18 @@ def bulk_read(self, ids, reg_names): return res - def bulk_multi_read(self, ids=None, user_regs=None): + while True: + try: + res = self._bulk_multi_read(ids, user_regs) + return res + except Exception as e: + logging.error(e) + self.port.flush() + while self.port.inWaiting() > 0: + self.port.read() + + def _bulk_multi_read(self, ids=None, user_regs=None): ids = self.get_motors(ids) # returns all motors if ids is None From ac0c574714f445854886df2a6bd192946fe4c3ad Mon Sep 17 00:00:00 2001 From: Sebastian Blaes Date: Mon, 7 Oct 2019 12:37:11 +0200 Subject: [PATCH 18/18] removed doubled code --- dxl/dxlchain.py | 69 ------------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index 8b87d6f..908f692 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -519,75 +519,6 @@ def _bulk_multi_read(self, ids=None, user_regs=None): return dict(res) - def bulk_multi_read(self, ids=None, user_regs=None): - - ids = self.get_motors(ids) # returns all motors if ids is None - - "Needs to be consecutive list of registers" - regs = ['goal_pos', - 'moving_speed', - 'torque_limit', - 'present_position', - 'present_speed', - 'present_load', - 'present_voltage', - 'present_temp' - ] - - if user_regs is not None: - regs = user_regs - - payload = [Dxl.CMD_BULK_READ, 0x00] - - tot_sizes = dict() - - for id in ids: - - if id not in self.motors.keys(): - raise DxlConfigurationException("Motor ID %d cannot be found in chain" % id) - - m = self.motors[id] - - tot_size = 0 - for reg in regs: - - if reg not in m.registers.keys(): - raise DxlConfigurationException( - "Read %s impossible on chain, register absent from motor ID %d" % (reg, id)) - - r = m.registers[reg] - tot_size += r.size - tot_sizes[id] = tot_size - - payload.append(tot_size) - payload.append(id) - fst_addr = m.registers[regs[0]].address # address of first register - payload.append(fst_addr) - - self.send(Dxl.BROADCAST, payload) - - # Retrieve response. packages from motors come unordered one after another - res = [] - - for _ in ids: - (nid, data) = self.recv() - - if len(data) != tot_sizes[nid]: - raise DxlCommunicationException( - 'Motor ID %d did not retrieve expected register size %d: got %d bytes' % ( - nid, tot_sizes[nid], len(data))) - - m = self.motors[nid] - blob = (nid, {}) - counter = 0 - for reg in regs: - r = m.registers[reg] - blob[1][reg] = r.fromdxl(data[counter:counter+r.size]) - counter += r.size - res.append(blob) - - return dict(res) - def sync_read_pos(self, ids=None): return self._sync_read_X_wrapper(ids, 'present_position')