diff --git a/README.rst b/README.rst index 72b4bed..25b3a7f 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,17 @@ HumaRobotics Dynamixel Library ######################################################## +Changes in this fork (by Sebastian Blaes) +========================== + + * 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 +========================== + 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. diff --git a/dxl/dxlchain.py b/dxl/dxlchain.py index e5248b0..908f692 100644 --- a/dxl/dxlchain.py +++ b/dxl/dxlchain.py @@ -8,6 +8,7 @@ from dxlregisters import * from dxlmotors import * from dxlsensors import * +from post_threading import Post import sys import serial @@ -17,12 +18,8 @@ import json import array from collections import OrderedDict -from post_threading import Post - - - class DxlChain: """ Manages a list of Dynamixel motors on the same serial link. @@ -88,9 +85,12 @@ 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)])) + + # 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() @@ -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') @@ -172,7 +188,7 @@ def _ping_broadcast(self): (id,data)=self._recv() l.append(id) except DxlCommunicationException: - break + break return l @@ -183,9 +199,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): @@ -204,27 +220,75 @@ 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 + 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,regs[0],tot_size,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(): - 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,63 +296,286 @@ 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 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: + self.determine_control_mode(id) + + 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 + # 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")) + 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(f"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] + + 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] + 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' % ( + nid, reg_name, r.size, len(data))) + + res.append((nid, r.fromdxl(data))) + + 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 + + "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') + + def sync_read_speed(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_speed') + + def sync_read_load(self, ids=None): + return self._sync_read_X_wrapper(ids, 'present_load') + + 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 + return dict(self.bulk_read(ids, [register] * 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)""" + """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 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] @@ -300,42 +587,37 @@ 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 + 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)""" + # 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) - 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) - + + 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,reg.address,reg.size] + payload= [Dxl.CMD_SYNC_WRITE,r.address,r.size] for i in range(0,len(ids)): id=ids[i] - pos=positions[i] + val=vals[i] payload.append(id) - payload.extend(reg.todxl(pos)) - - self.send(Dxl.BROADCAST,payload) - + 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""" reg=self.motors[id].registers[name] @@ -397,7 +679,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 +690,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 +711,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 +722,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)) @@ -454,6 +736,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): @@ -532,4 +815,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 7f8e7ff..3985575 100644 --- a/dxl/dxlcontrollers.py +++ b/dxl/dxlcontrollers.py @@ -43,4 +43,3 @@ def __init__(self): self.sort() - \ No newline at end of file diff --git a/dxl/dxlcore.py b/dxl/dxlcore.py index c09f6f3..b765177 100644 --- a/dxl/dxlcore.py +++ b/dxl/dxlcore.py @@ -24,7 +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 # read from multiple devices with different register def get_model_name(model_number): @@ -57,7 +59,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 +67,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..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() @@ -94,8 +101,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 +110,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 +120,8 @@ 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" @@ -134,9 +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" @@ -144,19 +160,17 @@ class DxlMotorMX12W(DxlMotorAXMX): 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" @@ -164,7 +178,7 @@ class DxlMotorMX28(DxlMotorAXMX): 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') @@ -175,8 +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" @@ -184,7 +197,7 @@ class DxlMotorMX64(DxlMotorAXMX): 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') @@ -192,11 +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" @@ -204,7 +216,7 @@ class DxlMotorRX64(DxlMotorAXMX): 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') @@ -216,15 +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') diff --git a/dxl/dxlsensors.py b/dxl/dxlsensors.py index 505967d..3ac6d4f 100644 --- a/dxl/dxlsensors.py +++ b/dxl/dxlsensors.py @@ -65,4 +65,3 @@ def __init__(self): self.sort() - \ No newline at end of file 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 diff --git a/dynamixel-hr-python3.ipynb b/dynamixel-hr-python3.ipynb new file mode 100644 index 0000000..f8ee2c0 --- /dev/null +++ b/dynamixel-hr-python3.ipynb @@ -0,0 +1,1384 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:27.301109Z", + "start_time": "2018-10-18T08:07:27.296851Z" + } + }, + "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": 25, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:27.443580Z", + "start_time": "2018-10-18T08:07:27.439735Z" + } + }, + "outputs": [], + "source": [ + "import dxlchain as Dxl\n", + "import logging\n", + "import numpy as np\n", + "import time" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:27.588040Z", + "start_time": "2018-10-18T08:07:27.584370Z" + } + }, + "outputs": [], + "source": [ + "logger = logging.getLogger()\n", + "# logger.setLevel('DEBUG')\n", + "#logger.setLevel('WARN')" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:27.879947Z", + "start_time": "2018-10-18T08:07:27.872520Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from importlib import reload\n", + "reload(Dxl)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:44.879462Z", + "start_time": "2018-10-18T08:07:44.875023Z" + } + }, + "outputs": [], + "source": [ + "# Open the serial device\n", + "chain=Dxl.DxlChain(\"/dev/ttyACM0\",rate=1000000, timeout=0.4)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:45.815680Z", + "start_time": "2018-10-18T08:07:45.402646Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4]\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": 32, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:46.828541Z", + "start_time": "2018-10-18T08:07:46.820019Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: ,\n", + " 2: ,\n", + " 3: ,\n", + " 4: }" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.motors" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:07:48.062391Z", + "start_time": "2018-10-18T08:07:48.055137Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 0, 0, 0]" + ] + }, + "execution_count": 33, + "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, + "metadata": {}, + "outputs": [], + "source": [ + "chain.set_reg(5, 'return_delay', 0)" + ] + }, + { + "cell_type": "code", + "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, 'moving_speed', 500)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2018-08-14T16:56:54.123080Z", + "start_time": "2018-08-14T16:56:54.116451Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4095" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_reg(4, 'ccw_angle_limit')" + ] + }, + { + "cell_type": "code", + "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": 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)" + ] + }, + { + "cell_type": "code", + "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, 'goal_pos', [2000] * len(motors))" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: {'goal_pos': 102,\n", + " 'moving_speed': 100,\n", + " 'present_load': 0,\n", + " 'present_position': 101,\n", + " 'present_speed': 0,\n", + " 'present_temp': 37,\n", + " 'present_voltage': 119,\n", + " 'torque_limit': 1023},\n", + " 2: {'goal_pos': 101,\n", + " 'moving_speed': 0,\n", + " 'present_load': 0,\n", + " 'present_position': 102,\n", + " 'present_speed': 0,\n", + " 'present_temp': 39,\n", + " 'present_voltage': 119,\n", + " 'torque_limit': 1023},\n", + " 3: {'goal_pos': 103,\n", + " 'moving_speed': 0,\n", + " 'present_load': 0,\n", + " 'present_position': 103,\n", + " 'present_speed': 0,\n", + " 'present_temp': 39,\n", + " 'present_voltage': 118,\n", + " 'torque_limit': 1023},\n", + " 4: {'goal_pos': 100,\n", + " 'moving_speed': 0,\n", + " 'present_load': 24,\n", + " 'present_position': 102,\n", + " 'present_speed': 0,\n", + " 'present_temp': 40,\n", + " 'present_voltage': 119,\n", + " 'torque_limit': 1023}}" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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, + "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, + "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": 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, + "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": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T07:44:14.641236Z", + "start_time": "2018-10-18T07:44:14.633337Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 1397, 2: 1574, 3: 1551, 4: 2288}" + ] + }, + "execution_count": 19, + "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": 56, + "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', 0),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 0),\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', 0),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 102),\n", + " ('moving_speed', 100),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 102),\n", + " ('present_speed', 0),\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", + " ('torque_control_mode_enable', 0),\n", + " ('goal_torque', 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', 0),\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', 0),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 101),\n", + " ('moving_speed', 0),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 101),\n", + " ('present_speed', 0),\n", + " ('present_load', 0),\n", + " ('present_voltage', 119),\n", + " ('present_temp', 39),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 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", + " ('id', 3),\n", + " ('baud_rate', 1),\n", + " ('return_delay', 0),\n", + " ('cw_angle_limit', 0),\n", + " ('ccw_angle_limit', 0),\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', 0),\n", + " ('led', 0),\n", + " ('d_gain', 0),\n", + " ('i_gain', 0),\n", + " ('p_gain', 32),\n", + " ('goal_pos', 103),\n", + " ('moving_speed', 0),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 103),\n", + " ('present_speed', 0),\n", + " ('present_load', 0),\n", + " ('present_voltage', 118),\n", + " ('present_temp', 39),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 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", + " ('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', 100),\n", + " ('moving_speed', 0),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 102),\n", + " ('present_speed', 0),\n", + " ('present_load', 24),\n", + " ('present_voltage', 119),\n", + " ('present_temp', 40),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 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', 0),\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', 100),\n", + " ('moving_speed', 0),\n", + " ('torque_limit', 1023),\n", + " ('present_position', 101),\n", + " ('present_speed', 0),\n", + " ('present_load', 0),\n", + " ('present_voltage', 121),\n", + " ('present_temp', 40),\n", + " ('registered', 0),\n", + " ('moving', 0),\n", + " ('lock', 0),\n", + " ('punch', 32)]))])" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.get_configuration()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T07:44:01.774121Z", + "start_time": "2018-10-18T07:44:01.586476Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import time\n", + "# %matplotlib " + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:08:13.515204Z", + "start_time": "2018-10-18T08:08:00.549651Z" + } + }, + "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", + " 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)\n", + "loaddata=np.asarray(loaddata)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T07:44:08.263505Z", + "start_time": "2018-10-18T07:44:08.258110Z" + } + }, + "outputs": [], + "source": [ + "chain.enable()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:08:13.648718Z", + "start_time": "2018-10-18T08:08:13.643729Z" + } + }, + "outputs": [], + "source": [ + "chain.disable()" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-18T08:16:20.091737Z", + "start_time": "2018-10-18T08:16:19.962113Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 57, + "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(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" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2018-10-09T07:33:07.633997Z", + "start_time": "2018-10-09T07:33:07.211953Z" + } + }, + "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[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_pos([43,44,36])" + ] + }, + { + "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": 58, + "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": 48, + "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": 57, + "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\", 0)" + ] + }, + { + "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 +}