svenzva_compliance_controller.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2017 Svenzva Robotics
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials provided
16 # with the distribution.
17 # * Neither the name of Svenzva Robotics LLC nor the names of its
18 # contributors may be used to endorse or promote products derived
19 # from this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 
34 from __future__ import division
35 from threading import Thread
36 
37 import rospy
38 import actionlib
39 import math
40 import pandas as pd
41 import numpy
42 
43 from std_msgs.msg import Float64, Int32, Float64MultiArray
44 from svenzva_msgs.msg import MotorState, MotorStateList, GripperFeedback, GripperResult, GripperAction
45 from sensor_msgs.msg import JointState
46 
48 
49  """
50  Teaching mode should be enabled when the motors are context-switched into force control mode.
51  This mode allows full movement and compliance from a human.
52 
53  Otherwise, it is assumed the arm is in current-based position control mode, where compliance is
54  intelligently turned on while the arm is not carying out a trajectory, or has failed in doing so.
55 
56  """
57  def __init__(self, controller_namespace, mx_io, teaching_mode=False):
58  self.mx_io = mx_io
59  self.motor_state = MotorStateList()
60  self.last_motor_state = MotorStateList()
61  rospy.Subscriber("revel/motor_states", MotorStateList, self.motor_state_cb, queue_size=1)
62  rospy.Subscriber("revel/model_efforts", JointState, self.model_effort_cb)
63  self.test_pub = rospy.Publisher("/revel/smoothed_current", Float64)
64  self.gr = [4,6,6,3,4,1,1]
65  self.smoothed_torque = [0, 0, 0, 0, 0, 0, 0]
66 
67  self.model_torque = [0, 0, 0, 0, 0, 0, 0]
68  self.last_model_torque = [0, 0, 0, 0, 0, 0, 0]
69  self.teaching_mode = teaching_mode
70  self.max_current = False
71  self.pos_active = False
72  self.pre_error = 0
73  self.torque_window = list()
74  self.motor_boost = 0
75 
76  def motor_state_cb(self, data):
77  self.last_motor_state = self.motor_state
78  self.motor_state = data
79 
80  torque_ar = [0 for x in range(7)]
81  for i,state in enumerate(data.motor_states):
82  torque_ar[i] = state.load
83  if len(self.torque_window) >= 6:
84  self.torque_window.pop(0)
85  self.torque_window.append(torque_ar)
86 
87  #returns the current sliding window for the motor index specified
88  # where index = motor_id - 1
89  def extract_motor_window(self, index):
90  window = list()
91  for windows in self.torque_window:
92  window.append(windows[index])
93  return window
94 
95  def model_effort_cb(self, msg):
96  if not msg:
97  return
99  self.model_torque = msg.effort
100 
101  """
102  Triangular smoothing function for noisy torque reading
103  """
104  def smoothListTriangle(self, list_val,strippedXs=False,degree=3):
105  weight=[]
106  window=degree*2-1
107  smoothed=[0.0]*(len(list_val)-window)
108  for x in range(1,2*degree):
109  weight.append(degree-abs(degree-x))
110  w=numpy.array(weight)
111  for i in range(len(smoothed)):
112  smoothed[i]=sum(numpy.array(list_val[i:i+window])*w)/float(sum(w))
113  return smoothed
114 
115  def pd_smooth(self, ts):
116  data = {'score': ts}
117  df = pd.DataFrame(data)
118  return df.rolling(window=5).mean()
119  #smooth_data = pd.rolling_mean(ts,5)
120  #return smooth_data
121 
122  #current / torque based complaince
123  #threshold theoretically helps smoothness
124  #and offset makes the joint easier to move due to model errors
125  def feel_and_react_motor(self, motor_id, threshold=3, offset=0):
126  #filter_thresh = 50 # a delta larger than this value results in a discard
127  #delta_pos = -10
128  #delta_neg = 10
129  model_torque = self.model_torque[motor_id-1] + offset
130  #convert from Nm to raw current value
131  model_boost = self.get_raw_current(0.02) #self.get_raw_current(model_torque / self.gr[motor_id-1] * 0.01)
132  model_torque = self.get_raw_current(model_torque / self.gr[motor_id-1])
133 
134  if abs(self.smoothed_torque[motor_id - 1] - model_torque) > threshold:
135 
136  mag = int(math.copysign(1, model_torque - self.motor_state.motor_states[motor_id-1].load ))
137  goal = model_torque
138 
139  if motor_id is 1:
140  if mag > 0:
141  goal -= model_boost #* self.gr[motor_id - 1]
142  else:
143  goal += model_boost #* self.gr[motor_id - 1]
144  self.motor_boost = 0.02
145 
146  return (motor_id, goal)
147 
148  if motor_id is 1:
149  self.motor_boost /= 2
150  goal = self.get_raw_current(self.motor_boost)
151  return (motor_id, goal)
152  return
153 
154  #tau is torque in Nm
155  #output is as firmware expects (units of 3.36mA)
156  def get_raw_current(self, tau):
157  return int(round(tau / 1.083775 / .00336))
158 
159  def feel_and_react(self):
160 
161  if not self.teaching_mode:
162 
163  moving_status = self.mx_io.get_multi_moving_status(range(1,7))
164  is_moving = 0
165  for status in moving_status:
166  is_moving |= status[1] & 0x2
167 
168  if is_moving and not self.max_current:
169 
170  vals = []
171  #set all motor allowable currents to their maximum
172  for i in range(1,7):
173  vals.append( (i,1900) )
174  self.mx_io.set_multi_current(tuple(vals))
175  self.max_current = True
176  return
177 
178  elif is_moving and self.max_current:
179  return
180 
181  vals = []
182  vals.append(self.feel_and_react_motor(1, 1))
183  vals.append(self.feel_and_react_motor(2, 4))
184  vals.append(self.feel_and_react_motor(3, 4))
185  vals.append(self.feel_and_react_motor(4, 2))
186  vals.append(self.feel_and_react_motor(5, 2))
187  vals.append(self.feel_and_react_motor(6, 10))
188  vals = [x for x in vals if x is not None]
189 
190  if len(vals) > 0:
191  self.mx_io.set_multi_current(tuple(vals))
192 
193  return
194 
195  def rad_to_raw(self, angle):
196  return int(round( angle * 4096.0 / 6.2831853 ))
197 
198  def update_torque(self):
199  for i in range(0, 6):
200  self.smoothed_torque[i] = self.smoothListTriangle(self.extract_motor_window(i))[0]
201 
202  def start(self):
203  rospy.sleep(2.0)
204  while len(self.torque_window) < 6:
205  rospy.sleep(0.1)
206  while not rospy.is_shutdown():
207  self.update_torque()
208  self.feel_and_react()
209  rospy.sleep(0.02)
210 
def __init__(self, controller_namespace, mx_io, teaching_mode=False)


svenzva_drivers
Author(s): Max Svetlik
autogenerated on Wed Oct 28 2020 03:31:27