calibrateBuoyancy.py
Go to the documentation of this file.
1 #! /usr/bin/env python3
2 
3 # Determines the buoyancy parameter of the robot.
4 # Assumes the robot is upright
5 
6 import rospy
7 import actionlib
8 import dynamic_reconfigure.client
9 
10 from geometry_msgs.msg import Vector3, Quaternion, Twist
11 from std_msgs.msg import Empty
12 from nav_msgs.msg import Odometry
13 import riptide_controllers.msg
14 
15 from tf.transformations import quaternion_from_euler, euler_from_quaternion, quaternion_inverse, quaternion_multiply
16 
17 import math
18 import yaml
19 import numpy as np
20 
21 WATER_DENSITY = 1000
22 GRAVITY = 9.81
23 
24 def msg_to_numpy(msg):
25  """Converts a Vector3 or Quaternion message to its numpy counterpart"""
26  if hasattr(msg, "w"):
27  return np.array([msg.x, msg.y, msg.z, msg.w])
28  return np.array([msg.x, msg.y, msg.z])
29 
30 def changeFrame(orientation, vector, w2b = True):
31  """Converts vector into other frame from orientation quaternion. The w2b parameter will
32  determine if the vector is converting from world to body or body to world"""
33 
34  vector = np.append(vector, 0)
35  if w2b:
36  orientation = quaternion_inverse(orientation)
37  orientationInv = quaternion_inverse(orientation)
38  newVector = quaternion_multiply(orientation, quaternion_multiply(vector, orientationInv))
39  return newVector[:3]
40 
41 
43 
44  _result = riptide_controllers.msg.CalibrateBuoyancyResult()
45 
46  def __init__(self):
47  self.position_pub = rospy.Publisher("position", Vector3, queue_size=1)
48  self.orientation_pub = rospy.Publisher("orientation", Quaternion, queue_size=1)
49  self.off_pub = rospy.Publisher("off", Empty, queue_size=1)
50 
51  # Get the mass and COM
52  with open(rospy.get_param('~vehicle_config'), 'r') as stream:
53  vehicle = yaml.safe_load(stream)
54  self.mass = vehicle['mass']
55  self.com = np.array(vehicle['com'])
56  self.inertia = np.array(vehicle['inertia'])
57 
58  self._as = actionlib.SimpleActionServer("calibrate_buoyancy", riptide_controllers.msg.CalibrateBuoyancyAction, execute_cb=self.execute_cb, auto_start=False)
59  self._as.start()
60 
61  def tune(self, initial_value, get_adjustment, apply_change, num_samples=10, delay=4):
62  """Tunes a parameter of the robot"""
63  current_value = np.array(initial_value)
64  last_adjustment = np.zeros_like(current_value)
65  converged = np.zeros_like(current_value)
66 
67  while not np.all(converged):
68  # Wait for equilibrium
69  rospy.sleep(delay)
70 
71  # Average a few samples
72  average_adjustment = 0
73  for _ in range(num_samples):
74  average_adjustment += get_adjustment() / num_samples
75  rospy.sleep(0.2)
76 
77  # Apply change
78  current_value += average_adjustment
79  apply_change(current_value)
80 
81  # Check if the value has converged
82  converged = np.logical_or(converged, average_adjustment * last_adjustment < 0)
83  last_adjustment = average_adjustment
84 
85  if self.check_preempted():
86  return
87 
88  return current_value
89 
90 
91  def execute_cb(self, goal):
92  # Start reconfigure server and get starting config
93  self.client = dynamic_reconfigure.client.Client("controller", timeout=30)
94  self.initial_config = self.client.get_configuration()
95 
96  # Set variables to defaults
97  rospy.loginfo("Starting buoyancy calibration")
98  volume = self.mass / WATER_DENSITY
99  cob = np.copy(self.com)
100 
101  # Reset parameters to default
102  self.client.update_configuration({
103  "volume": volume,
104  "center_x": cob[0],
105  "center_y": cob[1],
106  "center_z": cob[2],
107  "linear_x": 0,
108  "linear_y": 0,
109  "linear_z": 0,
110  "linear_rot_x": 0,
111  "linear_rot_y": 0,
112  "linear_rot_z": 0,
113  "quadratic_x": 0,
114  "quadratic_y": 0,
115  "quadratic_z": 0,
116  "quadratic_rot_x": 0,
117  "quadratic_rot_y": 0,
118  "quadratic_rot_z": 0,
119  })
120 
121  # Submerge
122  odom_msg = rospy.wait_for_message("odometry/filtered", Odometry)
123  current_position = msg_to_numpy(odom_msg.pose.pose.position)
124  current_orientation = msg_to_numpy(odom_msg.pose.pose.orientation)
125  self.position_pub.publish(Vector3(current_position[0], current_position[1], -1))
126  _, _, y = euler_from_quaternion(current_orientation)
127  self.orientation_pub.publish(Quaternion(*quaternion_from_euler(0, 0, y)))
128 
129  # Wait for equilibrium
130  rospy.sleep(10)
131 
132  # Volume adjustment function
133  def get_volume_adjustment():
134  body_force = self.mass * msg_to_numpy(rospy.wait_for_message("controller/requested_accel", Twist).linear)
135  orientation = msg_to_numpy(rospy.wait_for_message("odometry/filtered", Odometry).pose.pose.orientation)
136  world_z_force = changeFrame(orientation, body_force, w2b=False)[2]
137 
138  return -world_z_force / WATER_DENSITY / GRAVITY
139 
140  # Tune volume
141  volume = self.tune(
142  volume,
143  get_volume_adjustment,
144  lambda v: self.client.update_configuration({"volume": v})
145  )
146 
147  rospy.loginfo("Volume calibration complete")
148  buoyant_force = volume * WATER_DENSITY * GRAVITY
149 
150  # COB adjustment function
151  def get_cob_adjustment():
152  accel = msg_to_numpy(rospy.wait_for_message("controller/requested_accel", Twist).angular)
153  torque = self.inertia * accel
154  orientation = msg_to_numpy(rospy.wait_for_message("odometry/filtered", Odometry).pose.pose.orientation)
155  body_force_z = changeFrame(orientation, np.array([0, 0, buoyant_force]))[2]
156 
157  adjustment_x = torque[1] / body_force_z
158  adjustment_y = -torque[0] / body_force_z
159 
160  return np.array([adjustment_x, adjustment_y])
161 
162  # Tune X and Y COB
163  self.tune(
164  cob[:2],
165  get_cob_adjustment,
166  lambda cob: self.client.update_configuration({"center_x": cob[0], "center_y": cob[1]}),
167  num_samples = 2,
168  delay = 1
169  )
170 
171  rospy.loginfo("Buoyancy XY calibration complete")
172 
173  # Adjust orientation
174  self.orientation_pub.publish(Quaternion(*quaternion_from_euler(0, -math.pi / 4, y)))
175  rospy.sleep(3)
176 
177  # Z COB function
178  def get_cob_z_adjustment():
179  accel = msg_to_numpy(rospy.wait_for_message("controller/requested_accel", Twist).angular)
180  torque = self.inertia * accel
181  orientation = msg_to_numpy(rospy.wait_for_message("odometry/filtered", Odometry).pose.pose.orientation)
182  body_force_x = changeFrame(orientation, np.array([0, 0, buoyant_force]))[0]
183 
184  adjustment = -torque[1] / body_force_x
185 
186  return adjustment
187 
188  # Tune Z COB
189  self.tune(
190  cob[2],
191  get_cob_z_adjustment,
192  lambda z: self.client.update_configuration({"center_z": z})
193  )
194 
195  rospy.loginfo("Calibration complete")
196  self.cleanup()
197  self._result.buoyant_force = buoyant_force
198  self._result.center_of_buoyancy = cob
199  self._as.set_succeeded(self._result)
200 
201  def check_preempted(self):
202  if self._as.is_preempt_requested():
203  rospy.loginfo('Preempted Calibration')
204  self.cleanup()
205  self._as.set_preempted()
206  return True
207 
208  def cleanup(self):
209  self.client.update_configuration({
210  "linear_x": self.initial_config['linear_x'],
211  "linear_y": self.initial_config['linear_y'],
212  "linear_z": self.initial_config['linear_z'],
213  "linear_rot_x": self.initial_config['linear_rot_x'],
214  "linear_rot_y": self.initial_config['linear_rot_y'],
215  "linear_rot_z": self.initial_config['linear_rot_z'],
216  "quadratic_x": self.initial_config['quadratic_x'],
217  "quadratic_y": self.initial_config['quadratic_y'],
218  "quadratic_z": self.initial_config['quadratic_z'],
219  "quadratic_rot_x": self.initial_config['quadratic_rot_x'],
220  "quadratic_rot_y": self.initial_config['quadratic_rot_y'],
221  "quadratic_rot_z": self.initial_config['quadratic_rot_z'],
222  })
223  self.off_pub.publish()
224 
225 
226 
227 
228 if __name__ == '__main__':
229  rospy.init_node('calibrate_buoyancy')
231  rospy.spin()
calibrateBuoyancy.CalibrateBuoyancyAction.__init__
def __init__(self)
Definition: calibrateBuoyancy.py:46
calibrateBuoyancy.CalibrateBuoyancyAction.mass
mass
Definition: calibrateBuoyancy.py:54
calibrateBuoyancy.CalibrateBuoyancyAction.off_pub
off_pub
Definition: calibrateBuoyancy.py:49
calibrateBuoyancy.CalibrateBuoyancyAction.tune
def tune(self, initial_value, get_adjustment, apply_change, num_samples=10, delay=4)
Definition: calibrateBuoyancy.py:61
calibrateBuoyancy.CalibrateBuoyancyAction
Definition: calibrateBuoyancy.py:42
calibrateBuoyancy.changeFrame
def changeFrame(orientation, vector, w2b=True)
Definition: calibrateBuoyancy.py:30
calibrateBuoyancy.CalibrateBuoyancyAction._result
_result
Definition: calibrateBuoyancy.py:44
calibrateBuoyancy.msg_to_numpy
def msg_to_numpy(msg)
Definition: calibrateBuoyancy.py:24
calibrateBuoyancy.CalibrateBuoyancyAction.client
client
Definition: calibrateBuoyancy.py:93
calibrateBuoyancy.CalibrateBuoyancyAction.inertia
inertia
Definition: calibrateBuoyancy.py:56
calibrateBuoyancy.CalibrateBuoyancyAction.execute_cb
def execute_cb(self, goal)
Definition: calibrateBuoyancy.py:91
actionlib::SimpleActionServer
calibrateBuoyancy.CalibrateBuoyancyAction.position_pub
position_pub
Definition: calibrateBuoyancy.py:47
calibrateBuoyancy.CalibrateBuoyancyAction.initial_config
initial_config
Definition: calibrateBuoyancy.py:94
calibrateBuoyancy.CalibrateBuoyancyAction._as
_as
Definition: calibrateBuoyancy.py:58
calibrateBuoyancy.CalibrateBuoyancyAction.orientation_pub
orientation_pub
Definition: calibrateBuoyancy.py:48
calibrateBuoyancy.CalibrateBuoyancyAction.com
com
Definition: calibrateBuoyancy.py:55
calibrateBuoyancy.CalibrateBuoyancyAction.check_preempted
def check_preempted(self)
Definition: calibrateBuoyancy.py:201
calibrateBuoyancy.CalibrateBuoyancyAction.cleanup
def cleanup(self)
Definition: calibrateBuoyancy.py:208


riptide_controllers
Author(s): Blaine Miller, Mitchell Sayre
autogenerated on Wed Mar 2 2022 00:50:23