8 import dynamic_reconfigure.client
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
15 from tf.transformations
import quaternion_from_euler, euler_from_quaternion, quaternion_inverse, quaternion_multiply
25 """Converts a Vector3 or Quaternion message to its numpy counterpart"""
27 return np.array([msg.x, msg.y, msg.z, msg.w])
28 return np.array([msg.x, msg.y, msg.z])
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"""
34 vector = np.append(vector, 0)
36 orientation = quaternion_inverse(orientation)
37 orientationInv = quaternion_inverse(orientation)
38 newVector = quaternion_multiply(orientation, quaternion_multiply(vector, orientationInv))
44 _result = riptide_controllers.msg.CalibrateBuoyancyResult()
47 self.
position_pub = rospy.Publisher(
"position", Vector3, queue_size=1)
49 self.
off_pub = rospy.Publisher(
"off", Empty, queue_size=1)
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'])
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)
67 while not np.all(converged):
72 average_adjustment = 0
73 for _
in range(num_samples):
74 average_adjustment += get_adjustment() / num_samples
78 current_value += average_adjustment
79 apply_change(current_value)
82 converged = np.logical_or(converged, average_adjustment * last_adjustment < 0)
83 last_adjustment = average_adjustment
93 self.
client = dynamic_reconfigure.client.Client(
"controller", timeout=30)
97 rospy.loginfo(
"Starting buoyancy calibration")
98 volume = self.
mass / WATER_DENSITY
99 cob = np.copy(self.
com)
102 self.
client.update_configuration({
116 "quadratic_rot_x": 0,
117 "quadratic_rot_y": 0,
118 "quadratic_rot_z": 0,
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)))
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]
138 return -world_z_force / WATER_DENSITY / GRAVITY
143 get_volume_adjustment,
144 lambda v: self.
client.update_configuration({
"volume": v})
147 rospy.loginfo(
"Volume calibration complete")
148 buoyant_force = volume * WATER_DENSITY * GRAVITY
151 def get_cob_adjustment():
152 accel =
msg_to_numpy(rospy.wait_for_message(
"controller/requested_accel", Twist).angular)
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]
157 adjustment_x = torque[1] / body_force_z
158 adjustment_y = -torque[0] / body_force_z
160 return np.array([adjustment_x, adjustment_y])
166 lambda cob: self.
client.update_configuration({
"center_x": cob[0],
"center_y": cob[1]}),
171 rospy.loginfo(
"Buoyancy XY calibration complete")
174 self.
orientation_pub.publish(Quaternion(*quaternion_from_euler(0, -math.pi / 4, y)))
178 def get_cob_z_adjustment():
179 accel =
msg_to_numpy(rospy.wait_for_message(
"controller/requested_accel", Twist).angular)
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]
184 adjustment = -torque[1] / body_force_x
191 get_cob_z_adjustment,
192 lambda z: self.
client.update_configuration({
"center_z": z})
195 rospy.loginfo(
"Calibration complete")
197 self.
_result.buoyant_force = buoyant_force
198 self.
_result.center_of_buoyancy = cob
202 if self.
_as.is_preempt_requested():
203 rospy.loginfo(
'Preempted Calibration')
205 self.
_as.set_preempted()
209 self.
client.update_configuration({
228 if __name__ ==
'__main__':
229 rospy.init_node(
'calibrate_buoyancy')