class PositionControllerNode_ChihChun(object): """ROS interface for controlling the Crazyflie 2 in Gazebo.""" # write code here to define node publishers and subscribers # publish to /crazyflie2/command/motor_speed topic # subscribe to /crazyflie2/command/trajectory and /crazyflie2/odometry def __init__(self): # Publishers: rotor velocities self.pub_rotor_vel = rospy.Publisher('/crazyflie2/command/motor_speed', Actuators, queue_size=1) # Subscribers: desired posn, current posn #self._goal_msg = MultiDOFJointTrajectory() #self.sub_goal = rospy.Subscriber('crazyflie2/command/trajectory', MultiDOFJointTrajectory, self._goal_callback) self._currpos_msg = Odometry() self.sub_currpos = rospy.Subscriber('/crazyflie2/odometry', Odometry, self._currpos_callback) # end of publisher/subscriber # initialize other parameters self.data_list = [[ 'Time', 'Desired_x', 'Desired_y', 'Desired_z', 'Actual_x', 'Actual_y', 'Actual_z', 'Desired_yaw', 'Desired_roll', 'Desired_pitch', 'Desired_yaw_r', 'Desired_climb_r', 'Actual_roll', 'Actual_pitch', 'Actual_yaw_r', 'Actual_climb_r' ]] self.start = [1.0, 1.0] self.wp_id = 1 self.previous_time = 0.0 self.change_time = -100.0 self.controller = PositionController() self.init_time = rospy.get_time() # initialize position and roll, pitch, yaw self._x = 0 self._y = 0 self._z = 0 self.roll = 0 self.pitch = 0 self.yaw = 0 self._z_old = 0.0 self._z_oold = 0.0 self.yaw_old = 0.0 def set_rotor_vel(self, pitch_c, roll_c, yaw_rate_c, p, q, r, roll, pitch, yaw, thrust): # change arg names? # get conversions rotorvel_converter = roll_pitch_yawrate_thrust_crazyflie( pitch_c, roll_c, yaw_rate_c, p, q, r, roll, pitch, yaw, thrust) rotor_velocities = rotorvel_converter.CalculateRotorVelocities() # publish rotor velocities to Actuator msg = Actuators() msg.angular_velocities = rotor_velocities msg.header.stamp = self._currpos_msg.header.stamp self.pub_rotor_vel.publish(msg) def _currpos_callback(self, msg): self._currpos_msg = msg def get_data(self): self._z_oold = self._z_old self._z_old = self._z self.yaw_old = self.yaw # self._pos = self._vicon_msg.transform.translation self._x = self._currpos_msg.pose.pose.position.x self._y = self._currpos_msg.pose.pose.position.y self._z = self._currpos_msg.pose.pose.position.z # Convert to quaternion object for use by euler_from_quaternion() quaternion = np.array([ self._currpos_msg.pose.pose.orientation.x, self._currpos_msg.pose.pose.orientation.y, self._currpos_msg.pose.pose.orientation.z, self._currpos_msg.pose.pose.orientation.w ]) # Determine the euler angles euler = euler_from_quaternion(quaternion) # change? can i use this? self.roll = euler[0] self.pitch = euler[1] self.yaw = euler[2] # Determine the euler angular rates p,q,r and thrust self.p = self._currpos_msg.twist.twist.angular.x self.q = self._currpos_msg.twist.twist.angular.y self.r = self._currpos_msg.twist.twist.angular.z #self.thrust = self._currpos_msg.twist.twist.linear.z self.x_d = 1.0 self.y_d = 1.0 self.z_d = 1.0 self.yaw_d = 0.0 self.vx_d = 0.0 self.vy_d = 0.0 self.vz_d = 0.0 # desired position #self.x_d = self._goal_msg.points.transforms.translation.x #self.y_d = self._goal_msg.points.transforms.translation.y #self.z_d = self._goal_msg.points.transforms.translation.z # desired orientation #self.quat = np.array([self._goal_msg.points.transforms.rotation.x, # self._goal_msg.points.transforms.rotation.y, # self._goal_msg.points.transforms.rotation.z, # self._goal_msg.points.transforms.rotation.w]) #euler = euler_from_quaternion(self.quat) #self.yaw_d = euler[2] # desired velocities #self.vx_d = self._goal_msg.points.velocities.linear.x # may need to change in future #self.vy_d = self._goal_msg.points.velocities.linear.y #self.vz_d = self._goal_msg.points.velocities.linear.z def iteration(self, event): current_time = rospy.get_time() - self.init_time dt = current_time - self.previous_time self.previous_time = current_time #print(dt) if dt == 0: dt = 0.01 act_climb_r = (self._z - 2 * self._z_old + self._z_oold) / (dt**2) print("z_dd_real: ", act_climb_r) #act_yaw_r = (self.yaw - self.yaw_old)/dt self.get_data() roll_c, pitch_c, z_dot_c, yaw_dot_c = self.controller.get_command( self._x, self._y, self._z, self.roll, self.pitch, self.yaw, self.x_d, self.y_d, self.z_d, self.vx_d, self.vy_d, self.vz_d, self.yaw_d) #self.set_vel(roll_c, pitch_c, z_dot_c, yaw_dot_c) self.thrust_c = z_dot_c #+ 12000 print("thrust: ", z_dot_c) self.set_rotor_vel(pitch_c, roll_c, yaw_dot_c, self.p, self.q, self.r, self.roll, self.pitch, self.yaw, self.thrust_c)
class position_controller_flock_node(object): """ROS interface for controlling up to four Cf2.0's in Gazebo and running flocking algorithm.""" def __init__(self, uav_ids, init, fin, vx_ds, vy_ds, vz_ds, yaw_ds): self.cf_ids = uav_ids self.number_of_agents = np.shape(uav_ids)[0] self.takoff_alt = 1.0 # change? self._pos = {} self._vel = {} self._quat = {} self._dist_to_goal = {} self._euler_angles = {} self._euler_angular_rates = {} self._z_old = {} self._z_oold = {} self.yaw_old = {} self.radius = 0.15 self.d_star = self.radius self.MaxVelo = 1.0 # Tune these self.c1 = 7 * 4.5 self.c2 = 9 * 4.5 self.RepulsiveGradient = 7.5 * 100 self.previous_time = 0.0 self.change_time = -100.0 self.controller = PositionController() self.init_time = rospy.get_time() ### Publish ### # waypoint messages self.goal_msg_0, self.goal_msg_1, self.goal_msg_2, self.goal_msg_3 = PoseStamped( ), PoseStamped(), PoseStamped(), PoseStamped() self.goal_msgs = { '0': self.goal_msg_0, '1': self.goal_msg_1, '2': self.goal_msg_2, '3': self.goal_msg_3 } # rotor velocities messages # self.cmdV_msg_0, self.cmdV_msg_1, self.cmdV_msg_2, self.cmdV_msg_3 = Actuators(), Actuators(), Actuators(), Actuators() # self.rotor_vel_msgs = { # '0' : self.cmdV_msg_0, # '1' : self.cmdV_msg_1, # '2' : self.cmdV_msg_2, # '3' : self.cmdV_msg_3 # } ### Subscribe ### # odometry messages self._currpos_callbacks = { '0': self._currpos_callback_0, '1': self._currpos_callback_1, '2': self._currpos_callback_2, '3': self._currpos_callback_3 } # set parameters self.initials, self.finals = {}, {} self.goal_pubs, self.cmdVtemp_pubs, self.cmdV_pubs, self.takeoffs, self.lands = {}, {}, {}, {}, {} for index, cf_id in enumerate(self.cf_ids): self.initials[str(cf_id)] = init[index][:] self._z_old[str(cf_id)] = 0.0 self._z_oold[str(cf_id)] = 0.0 self.yaw_old[str(cf_id)] = 0.0 self.finals[str(cf_id)] = fin[index][:] self.goal_pubs[str(cf_id)] = rospy.Publisher('/crazyflie2_' + str(cf_id) + '/goal', PoseStamped, queue_size=1) self.cmdV_pubs[str(cf_id)] = rospy.Publisher( '/crazyflie2_' + str(cf_id) + '/command/motor_speed', Actuators, queue_size=1) rospy.Subscriber("/crazyflie2_" + str(cf_id) + "/odometry", Odometry, self._currpos_callbacks[str(cf_id)]) self.vx_d = vx_ds self.vy_d = vy_ds self.vz_d = vz_ds self.yaw_d = yaw_ds self.takeoffed = False self.reached_1st = False self.flag = {'flying': 0, 'landed': 0, 'preland': 0} # self.vstate = { # 'takeoff' : self.do_takeoff, # 'wpnav' : self.do_wpnav, # 'land' : self.do_land # } # Position controller self.controller = PositionController() def set_rotor_vel(self, pitch_c, roll_c, yaw_rate_c, p, q, r, roll, pitch, yaw, thrust): # change arg names? # get conversions rotorvel_converter = roll_pitch_yawrate_thrust_crazyflie( pitch_c, roll_c, yaw_rate_c, p, q, r, roll, pitch, yaw, thrust) rotor_velocities = rotorvel_converter.CalculateRotorVelocities() # publish rotor velocities to Actuator msg = Actuators() msg.angular_velocities = rotor_velocities msg.header.stamp = self._currpos_msg.header.stamp self.pub_rotor_vel.publish(msg) def _currpos_callback(self, msg): self._currpos_msg = msg def get_data(self, id): self._z_oold[str(id)] = self._z_old[str(id)] self._z_old[str(id)] = self._pos[str(id)].z # current z print("self._euler_angles[str(id)]: ", self._euler_angles[str(id)]) self.yaw_old[str(id)] = self._euler_angles[str(id)][2] # current yaw def _currpos_callback_0(self, data): self._pos['0'] = data.pose.pose.position self._vel['0'] = data.twist.twist.linear self._quat['0'] = np.array([ data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w ]) # gives quaternion object #print("quat 0: ", self._quat['0']) self._euler_angles['0'] = euler_from_quaternion( self._quat['0']) # gives roll, pitch, yaw #print("euler angles 0: ", self._euler_angles['0']) self._euler_angular_rates['0'] = np.asarray( data.twist.twist.angular) # gives p, q, r def _currpos_callback_1(self, data): self._pos['1'] = data.pose.pose.position self._vel['1'] = data.twist.twist.linear self._quat['1'] = np.array([ data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w ]) # gives quaternion object self._euler_angles['1'] = euler_from_quaternion( self._quat['1']) # gives roll, pitch, yaw #print(self._euler_angles['1'], type(self._euler_angles['1'])) self._euler_angular_rates['1'] = np.asarray( data.twist.twist.angular) # gives p, q, r def _currpos_callback_2(self, data): self._pos['2'] = data.pose.pose.position self._vel['2'] = data.twist.twist.linear self._quat['2'] = np.array([ data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w ]) # gives quaternion object self._euler_angles['2'] = euler_from_quaternion( self._quat['2']) # gives roll, pitch, yaw self._euler_angular_rates['2'] = np.asarray( data.twist.twist.angular) # gives p, q, r def _currpos_callback_3(self, data): self._pos['3'] = data.pose.pose.position self._vel['3'] = data.twist.twist.linear self._quat['3'] = np.array([ data.pose.pose.orientation.x, data.pose.pose.orientation.y, data.pose.pose.orientation.z, data.pose.pose.orientation.w ]) # gives quaternion object self._euler_angles['3'] = euler_from_quaternion( self._quat['3']) # gives roll, pitch, yaw self._euler_angular_rates['3'] = np.asarray( data.twist.twist.angular) # gives p, q, r def update_pos(self, id, pos): self.goal_msgs[id].header.seq += 1 self.goal_msgs[id].header.frame_id = '/world' # change? self.goal_msgs[id].header.stamp = rospy.Time.now() self.goal_msgs[id].pose.position.x = pos[0] self.goal_msgs[id].pose.position.y = pos[1] self.goal_msgs[id].pose.position.z = pos[2] self.goal_msgs[id].pose.orientation.x = 0 self.goal_msgs[id].pose.orientation.y = 0 self.goal_msgs[id].pose.orientation.z = 0 self.goal_msgs[id].pose.orientation.w = 1 def update_rotor_vels(self, id): # this function takes in the UAV's id and computes+publishes the rotor velocities to that UAV # get z_oold, z_old, and desired position/yaw self.get_data(id) # get roll/pitch/yawrate/thrust commands from position controller roll_c, pitch_c, z_dot_c, yaw_dot_c = self.controller.get_command( self._pos[str(id)].x, self._pos[str(id)].y, self._pos[str(id)].z, # x,y,z self._euler_angles[str(id)][0], self._euler_angles[str(id)][1], self._euler_angles[str(id)][0][2], # change? roll, pitch, yaw self.initials[str(id)][0], self.initials[str(id)][1], self.initials[str(id)][2], # change? xd, yd, zd self.vx_d[int(id)], self.vy_d[int(id)], self.vz_d[int(id)], self.yaw_d[int(id)]) # obtain p,q,r/roll,pitch,yaw for UAV id from odometry subscription p = self._euler_angular_rates[str(id)][0] q = self._euler_angular_rates[str(id)][1] r = self._euler_angular_rates[str(id)][2] roll = self._euler_angles[str(id)][0] pitch = self._euler_angles[str(id)][1] yaw = self._euler_angles[str(id)][2] # convert above commands to rotor velocity commands rotorvel_converter = roll_pitch_yawrate_thrust_crazyflie( pitch_c, roll_c, yaw_dot_c, p, q, r, roll, pitch, yaw, z_dot_c) rotor_velocities = rotorvel_converter.CalculateRotorVelocities( ) # this yields a 4-element list # publish rotor velocities to Actuator rotorvel_msg = Actuators() rotorvel_msg.angular_velocities = rotor_velocities #rotorvel_msg.header.stamp = self._currpos_msg.header.stamp self.cmdV_pubs[str(cf_id)].publish(rotorvel_msg) def publish_msg(self): for cf_id in self.cf_ids: self.goal_pubs[str(cf_id)].publish(self.goal_msgs[str(cf_id)]) def do_wpnav(self): print('Navigating!!') # Check all cfs reached assigened takeoff alt if not self.takeoffed: count = 0 for cf_id in self.cf_ids: if abs(self._pos[str(cf_id)][2] - self.takoff_alt) < 0.05: count += 1 position_d = np.array([ self._pos[str(cf_id)][0], self._pos[str(cf_id)][1], self.takoff_alt ]) self.update_pos(str(cf_id), position_d) if count == self.number_of_agents: self.takeoffed = True # Check all cfs reached their initial points elif not self.reached_1st: count = 0 for cf_id in self.cf_ids: if np.linalg.norm(self._pos[str(cf_id)] - self.initials[str(cf_id)]) < 0.1: count += 1 position_d = np.array([ self.initials[str(cf_id)][0], self.initials[str(cf_id)][1], self.initials[str(cf_id)][2] ]) self.update_pos(str(cf_id), position_d) if count == self.number_of_agents: self.reached_1st = True print('Initial points reached!!') raw_input( "Press Enter to continue...") # use input() for python3 # flocking path planning else: # call flocking and get the desired position of next timestep for cf_id in self.cf_ids: other_cfs = self.cf_ids[:] other_cfs.remove(cf_id) self._dist_to_goal[str(cf_id)] = np.linalg.norm( self._pos[str(cf_id)] - self.finals[str(cf_id)]) force = -self.c1 * self._vel[str(cf_id)] - self.c2 * ( self._pos[str(cf_id)] - self.finals[str(cf_id)]) for other_cf in other_cfs: dist_v = self._pos[str(other_cf)] - self._pos[str(cf_id)] dist = np.linalg.norm(dist_v) d = 2 * self.radius + self.d_star CommunicationRadious = np.cbrt( 3 * np.square(self.MaxVelo) / (2 * self.RepulsiveGradient)) + d if dist < CommunicationRadious: ForceComponent = -self.RepulsiveGradient * np.square( dist - CommunicationRadious) force += ForceComponent * (dist_v) / dist velocity_d = self._vel[str(cf_id)] + force * self.dt position_d = self._pos[str(cf_id)] + velocity_d * self.dt position_d[2] = 1.0 # for 2D sim self.update_pos(str(cf_id), position_d) # Check all cfs reached their final points if self._dist_to_goal[max(self._dist_to_goal)] < 0.2: self.flag['preland'] = 1 def iteration(self, event): # publish goal messages for each uav self.publish_msg() # publish rotor velocities for each uav for index, cf_id in enumerate(self.cf_ids): self.update_rotor_vels(cf_id)