17 from geometry_msgs.msg
import Twist
18 from std_msgs.msg
import Float32MultiArray, Int16MultiArray
21 from tf.transformations
import euler_matrix
22 from tf
import TransformListener
23 from scipy.optimize
import minimize
31 return np.array([msg.x, msg.y, msg.z])
36 rospy.Subscriber(
"net_force", Twist, self.
force_cb, queue_size=1)
38 self.
thruster_pub = rospy.Publisher(
"thruster_forces", Float32MultiArray, queue_size=5)
39 self.
pwm_pub = rospy.Publisher(
"command/pwm", Int16MultiArray, queue_size=5)
43 config_path = rospy.get_param(
"~vehicle_config")
44 with open(config_path,
'r')
as stream:
45 config_file = yaml.safe_load(stream)
47 thruster_info = config_file[
'thrusters']
50 com = np.array(config_file[
"com"])
51 self.
max_force = config_file[
"thruster"][
"max_force"]
54 for i, thruster
in enumerate(thruster_info):
55 pose = np.array(thruster[
"pose"])
56 rot_mat = euler_matrix(*pose[3:])
57 body_force = np.dot(rot_mat, np.array([1, 0, 0, 0]))[:3]
58 body_torque = np.cross(pose[:3]- com, body_force)
69 for i
in range(len(thruster_info)):
89 if (abs(forces[i]) < self.
pwm_file[
"MIN_THRUST"]):
92 elif (forces[i] > 0
and forces[i] <= self.
pwm_file[
"STARTUP_THRUST"]):
94 pwm = (int)(self.
pwm_file[
"SU_THRUST"][
"POS_SLOPE"] * forces[i] + self.
pwm_file[
"SU_THRUST"][
"POS_YINT"])
96 pwm = (int)(-self.
pwm_file[
"SU_THRUST"][
"POS_SLOPE"] * forces[i] + self.
pwm_file[
"SU_THRUST"][
"NEG_YINT"])
98 elif (forces[i] > 0
and forces[i] > self.
pwm_file[
"STARTUP_THRUST"]):
100 pwm = (int)(self.
pwm_file[
"THRUST"][
"POS_SLOPE"] * forces[i] + self.
pwm_file[
"THRUST"][
"POS_YINT"])
102 pwm = (int)(-self.
pwm_file[
"THRUST"][
"POS_SLOPE"] * forces[i] + self.
pwm_file[
"THRUST"][
"NEG_YINT"])
104 elif (forces[i] < 0
and forces[i] >= -self.
pwm_file[
"STARTUP_THRUST"]):
106 pwm = (int)(self.
pwm_file[
"SU_THRUST"][
"NEG_SLOPE"] * forces[i] + self.
pwm_file[
"SU_THRUST"][
"NEG_YINT"])
108 pwm = (int)(-self.
pwm_file[
"SU_THRUST"][
"NEG_SLOPE"] * forces[i] + self.
pwm_file[
"SU_THRUST"][
"POS_YINT"])
110 elif (forces[i] < 0
and forces[i] < -self.
pwm_file[
"STARTUP_THRUST"]):
112 pwm = (int)(self.
pwm_file[
"THRUST"][
"NEG_SLOPE"] * forces[i] + self.
pwm_file[
"THRUST"][
"NEG_YINT"])
114 pwm = (int)(-self.
pwm_file[
"THRUST"][
"NEG_SLOPE"] * forces[i] + self.
pwm_file[
"THRUST"][
"POS_YINT"])
119 pwm_values.append(pwm)
121 msg = Int16MultiArray()
122 msg.data = pwm_values
133 trans, _ = self.
listener.lookupTransform(
"world",
"%s/thruster_%d" % (self.
tf_namespace, i), rospy.Time(0))
136 except Exception
as ex:
138 if (rospy.get_rostime() - self.
start_time).secs > 1:
139 rospy.logerr(str(ex))
144 return np.sum(residual ** 2)
152 return np.sum(thruster_forces ** 2)
155 return 2 * thruster_forces
159 total_cost = self.
force_cost(thruster_forces, desired_state)
165 total_cost_jac = self.
force_cost_jac(thruster_forces, desired_state)
167 return total_cost_jac
170 desired_state = np.zeros(6)
179 if self.
force_cost(res.x, desired_state) > 0.05:
180 rospy.logwarn_throttle(1,
"Unable to exert requested force")
182 msg = Float32MultiArray()
190 if __name__ ==
'__main__':
191 rospy.init_node(
"thruster_solver")