oscillation_ctrl/src/LinkState.py

240 lines
7.1 KiB
Python
Raw Normal View History

2022-03-01 14:09:13 -04:00
#!/usr/bin/env python2.7
### Cesar Rodriguez June 2021
### Script to determine payload and drone state from Gazebo
import rospy, tf
import rosservice
import time
import math
from tf.transformations import *
from offboard_ex.msg import tethered_status
from geometry_msgs.msg import Pose
from gazebo_msgs.srv import GetLinkState
from oscillation_ctrl.srv import WaypointTrack
2022-03-01 14:09:13 -04:00
from std_msgs.msg import Bool
class Main:
def __init__(self):
# rate(s)
rate = 40 # rate for the publisher method, specified in Hz -- 20 Hz
self.dt = 1.0/rate
# Variables needed for testing start
self.tstart = rospy.get_time() # Keep track of the start time
while self.tstart == 0.0: # Need to make sure get_rostime works
self.tstart = rospy.get_time()
# How long should we wait before before starting test
self.param_exists = False
while self.param_exists == False:
if rospy.has_param('sim/wait'):
self.wait = rospy.get_param('sim/wait') # wait time
self.param_exists = True
elif rospy.get_time() - self.tstart >= 3.0:
break
# Will be set to true when test should start
self.bool = False
# initialize variables
self.phi = 0.0 # Payload angle of deflection from x-axis
self.theta = 0.0 # Payload angle of deflection from y-axis
self.tetherL = 0.0 # Tether length
self.has_run = 0 # Boolean to keep track of first run instance
self.phidot = 0.0 #
self.thetadot = 0.0 #
self.phibuf = 0.0 # Need buffers to determine their rates
self.thetabuf = 0.0 #
self.pload = True # Check if payload exists
# Max dot values to prevent 'blowup'
self.phidot_max = 3.0
self.thetadot_max = 3.0
# variables for message gen
self.status = tethered_status()
self.status.drone_id = 'spiri_with_tether::spiri::base'
self.status.pload_id = 'spiri_with_tether::mass::payload'
# service(s)
self.service1 = '/gazebo/get_link_state'
# need service list to check if models have spawned
self.service_list = rosservice.get_service_list()
# wait for service to exist
while self.service1 not in self.service_list:
print "Waiting for models to spawn..."
self.service_list = rosservice.get_service_list()
if rospy.get_time() - self.tstart >= 10.0:
break
# publisher(s)
self.publisher = rospy.Publisher('/status/twoBody_status', tethered_status, queue_size=1)
self.pub_wd = rospy.Publisher('/status/path_follow', Bool, queue_size=1)
self.pub_timer = rospy.Timer(rospy.Duration(1.0/rate), self.link_state)
self.path_timer = rospy.Timer(rospy.Duration(40.0/rate), self.path_follow)
def cutoff(self,value,ceiling):
"""
Takes in value and returns ceiling
if value > ceiling. Otherwise, it returns
value back
"""
# initilize sign
sign = 1
# check if value is negative
if value < 0.0:
sign = -1
# Cutoff value at ceiling
if (value > ceiling or value < -ceiling):
output = sign*ceiling
else:
output = value
return output
def euler_array(self,pose):
"""
Takes in pose msg object and outputs array of euler angs:
q[0] = Roll
q[1] = Pitch
q[2] = Yaw
"""
q = euler_from_quaternion([pose.orientation.x,
pose.orientation.y,
pose.orientation.z,
pose.orientation.w])
return q
# Get link states (drone and pload) and determine angle between them
def link_state(self,pub_timer):
try:
# State which service we are querying
get_P = rospy.ServiceProxy(self.service1,GetLinkState)
# Set reference frame
reference = ''
# Establish links needed --> Spiri base and payload
# P = Position vector
drone_P = get_P(self.status.drone_id,reference)
# Get orientation of drone in euler angles
drone_Eul = self.euler_array(drone_P.link_state.pose)
# Check if payload is part of simulation
if not drone_P.success:
self.status.drone_id = 'spiri::base'
drone_P = get_P(self.status.drone_id,reference) # i.e. no payload
self.pload = False
pload_P = get_P(self.status.pload_id,reference)
if not self.has_run == 1:
if self.pload == True:
# Get tether length based off initial displacement
self.tetherL = math.sqrt((drone_P.link_state.pose.position.x -
pload_P.link_state.pose.position.x)**2 +
(drone_P.link_state.pose.position.y -
pload_P.link_state.pose.position.y)**2 +
(drone_P.link_state.pose.position.z -
pload_P.link_state.pose.position.z)**2)
rospy.set_param('sim/tether_length',self.tetherL)
else:
self.tetherL = 0
self.has_run = 1
# Need to detemine their location to get angle of deflection
# Drone
drone_Px = drone_P.link_state.pose.position.x
drone_Py = drone_P.link_state.pose.position.y
drone_Pz = drone_P.link_state.pose.position.z
if self.pload == True: # If there is payload, determine the variables
# Pload
pload_Px = pload_P.link_state.pose.position.x
pload_Py = pload_P.link_state.pose.position.y
# Determine theta (pitch)
x_sep = pload_Px - drone_Px
if math.fabs(x_sep) >= self.tetherL or x_sep == 0:
self.theta = 0
else:
self.theta = math.asin(x_sep/self.tetherL)
# Determine thetadot
self.thetadot = (self.theta - self.thetabuf)/self.dt
self.thetadot = self.cutoff(self.thetadot,self.thetadot_max)
self.thetabuf = self.theta
# Determine phi (roll)
y_sep = pload_Py - drone_Py
if math.fabs(y_sep) >= self.tetherL or y_sep == 0:
self.phi = 0
else:
self.phi = math.asin(y_sep/self.tetherL)
# Determine phidot
self.phidot = (self.phi - self.phibuf)/self.dt
self.phidot = self.cutoff(self.phidot,self.phidot_max)
self.phibuf = self.phi # Update buffer
else: # Otherwise, vars = 0
x_sep = self.phi = self.phidot = self.theta = self.thetadot = 0
# Print and save results
print "\n"
rospy.loginfo("")
print"Roll: "+str(round(drone_Eul[0],2)),"\nPitch: "+str(round(drone_Eul[1],2)),"\nYaw: "+str(round(drone_Eul[2],2))
print "drone pos.x: " + str(round(drone_Px,2))
print "drone pos.y: " + str(round(drone_Py,2))
print "drone pos.z: " + str(round(drone_Pz,2))
print "phi: " + str(round(self.phi*180/3.14,3))
print "theta: " + str(round(self.theta*180/3.14,3))
# Populate message
self.status.drone_pos = drone_P.link_state.pose
self.status.pload_pos = pload_P.link_state.pose
self.status.length = self.tetherL
self.status.phi = self.phi
self.status.phidot = self.phidot
self.status.theta = self.theta
self.status.thetadot = self.thetadot
# Publish message
self.publisher.publish(self.status)
except rospy.ServiceException as e:
rospy.loginfo("Get Link State call failed: {0}".format(e))
def path_follow(self,path_timer):
now = rospy.get_time()
if now - self.tstart < self.wait:
self.bool = False
else:
self.bool = True
self.pub_wd.publish(self.bool)
if __name__=="__main__":
# Initiate ROS node
rospy.init_node('linkStates_node',anonymous=False)
try:
Main() # create class object
rospy.spin() # loop until shutdown signal
except rospy.ROSInterruptException:
pass