Added service to set waypoints

This commit is contained in:
cesar 2022-03-18 11:50:09 -03:00
parent 0c0764387b
commit 4fbed7a57c
16 changed files with 495 additions and 174 deletions

270
config/px4_config.yaml Normal file
View File

@ -0,0 +1,270 @@
# Common configuration for PX4 autopilot
#
# node:
startup_px4_usb_quirk: true
# --- system plugins ---
# sys_status & sys_time connection options
conn:
heartbeat_rate: 1.0 # send hertbeat rate in Hertz
timeout: 10.0 # hertbeat timeout in seconds
timesync_rate: 10.0 # TIMESYNC rate in Hertz (feature disabled if 0.0)
system_time_rate: 1.0 # send system time to FCU rate in Hertz (disabled if 0.0)
# sys_status
sys:
min_voltage: 10.0 # diagnostics min voltage
disable_diag: false # disable all sys_status diagnostics, except heartbeat
# sys_time
time:
time_ref_source: "fcu" # time_reference source
timesync_mode: MAVLINK
timesync_avg_alpha: 0.6 # timesync averaging factor
# --- mavros plugins (alphabetical order) ---
# 3dr_radio
tdr_radio:
low_rssi: 40 # raw rssi lower level for diagnostics
# actuator_control
# None
# command
cmd:
use_comp_id_system_control: false # quirk for some old FCUs
# dummy
# None
# ftp
# None
# global_position
global_position:
frame_id: "map" # origin frame
child_frame_id: "base_link" # body-fixed frame
rot_covariance: 99999.0 # covariance for attitude?
gps_uere: 1.0 # User Equivalent Range Error (UERE) of GPS sensor (m)
use_relative_alt: true # use relative altitude for local coordinates
tf:
send: false # send TF?
frame_id: "map" # TF frame_id
global_frame_id: "earth" # TF earth frame_id
child_frame_id: "base_link" # TF child_frame_id
# imu_pub
imu:
frame_id: "base_link"
# need find actual values
linear_acceleration_stdev: 0.0003
angular_velocity_stdev: 0.0003490659 // 0.02 degrees
orientation_stdev: 1.0
magnetic_stdev: 0.0
# local_position
local_position:
frame_id: "map"
tf:
send: false
frame_id: "map"
child_frame_id: "base_link"
send_fcu: false
# param
# None, used for FCU params
# rc_io
# None
# safety_area
safety_area:
p1: {x: 1.0, y: 1.0, z: 1.0}
p2: {x: -1.0, y: -1.0, z: -1.0}
# setpoint_accel
setpoint_accel:
send_force: false
# setpoint_attitude
setpoint_attitude:
reverse_thrust: false # allow reversed thrust
use_quaternion: true # enable PoseStamped topic subscriber
tf:
listen: false # enable tf listener (disable topic subscribers)
frame_id: "map"
child_frame_id: "target_attitude"
rate_limit: 50.0
setpoint_raw:
thrust_scaling: 1.0 # used in setpoint_raw attitude callback.
# Note: PX4 expects normalized thrust values between 0 and 1, which means that
# the scaling needs to be unitary and the inputs should be 0..1 as well.
# setpoint_position
setpoint_position:
tf:
listen: false # enable tf listener (disable topic subscribers)
frame_id: "map"
child_frame_id: "target_position"
rate_limit: 50.0
mav_frame: LOCAL_NED
# setpoint_velocity
setpoint_velocity:
mav_frame: LOCAL_NED
# vfr_hud
# None
# waypoint
mission:
pull_after_gcs: true # update mission if gcs updates
# --- mavros extras plugins (same order) ---
# adsb
# None
# debug_value
# None
# distance_sensor
## Currently available orientations:
# Check http://wiki.ros.org/mavros/Enumerations
##
distance_sensor:
hrlv_ez4_pub:
id: 0
frame_id: "hrlv_ez4_sonar"
orientation: PITCH_270 # RPY:{0.0, 270.0, 0.0} - downward-facing
field_of_view: 0.0 # XXX TODO
send_tf: true
sensor_position: {x: 0.0, y: 0.0, z: -0.1}
lidarlite_pub:
id: 1
frame_id: "lidarlite_laser"
orientation: PITCH_270
field_of_view: 0.0 # XXX TODO
send_tf: true
sensor_position: {x: 0.0, y: 0.0, z: -0.1}
sonar_1_sub:
subscriber: true
id: 2
orientation: PITCH_270
laser_1_sub:
subscriber: true
id: 3
orientation: PITCH_270
# image_pub
image:
frame_id: "px4flow"
# fake_gps
fake_gps:
# select data source
use_mocap: true # ~mocap/pose
mocap_transform: false # ~mocap/tf instead of pose
use_vision: false # ~vision (pose)
# origin (default: Zürich)
geo_origin:
lat: 47.3667 # latitude [degrees]
lon: 8.5500 # longitude [degrees]
alt: 408.0 # altitude (height over the WGS-84 ellipsoid) [meters]
eph: 2.0
epv: 2.0
satellites_visible: 5 # virtual number of visible satellites
fix_type: 3 # type of GPS fix (default: 3D)
tf:
listen: false
send: false # send TF?
frame_id: "map" # TF frame_id
child_frame_id: "fix" # TF child_frame_id
rate_limit: 10.0 # TF rate
gps_rate: 5.0 # GPS data publishing rate
# landing_target
landing_target:
listen_lt: false
mav_frame: "LOCAL_NED"
land_target_type: "VISION_FIDUCIAL"
image:
width: 640 # [pixels]
height: 480
camera:
fov_x: 2.0071286398 # default: 115 [degrees]
fov_y: 2.0071286398
tf:
send: true
listen: false
frame_id: "landing_target"
child_frame_id: "camera_center"
rate_limit: 10.0
target_size: {x: 0.3, y: 0.3}
# mocap_pose_estimate
mocap:
# select mocap source
use_tf: false # ~mocap/tf
use_pose: true # ~mocap/pose
# odom
odometry:
in:
frame_id: "odom"
child_frame_id: "base_link"
frame_tf:
local_frame: "local_origin_ned"
body_frame_orientation: "flu"
out:
frame_tf:
# available: check MAV_FRAME odometry local frames in
# https://mavlink.io/en/messages/common.html
local_frame: "vision_ned"
# available: ned, frd or flu (though only the tf to frd is supported)
body_frame_orientation: "frd"
# px4flow
px4flow:
frame_id: "px4flow"
ranger_fov: 0.118682 # 6.8 degrees at 5 meters, 31 degrees at 1 meter
ranger_min_range: 0.3 # meters
ranger_max_range: 5.0 # meters
# vision_pose_estimate
vision_pose:
tf:
listen: false # enable tf listener (disable topic subscribers)
frame_id: "map"
child_frame_id: "vision_estimate"
rate_limit: 10.0
# vision_speed_estimate
vision_speed:
listen_twist: true # enable listen to twist topic, else listen to vec3d topic
twist_cov: true # enable listen to twist with covariance topic
# vibration
vibration:
frame_id: "base_link"
# wheel_odometry
wheel_odometry:
count: 2 # number of wheels to compute odometry
use_rpm: false # use wheel's RPM instead of cumulative distance to compute odometry
wheel0: {x: 0.0, y: -0.15, radius: 0.05} # x-, y-offset (m,NED) and radius (m)
wheel1: {x: 0.0, y: 0.15, radius: 0.05} # x-, y-offset (m,NED) and radius (m)
send_raw: true # send wheel's RPM and cumulative distance (~/wheel_odometry/rpm, ~/wheel_odometry/distance)
send_twist: false # send geometry_msgs/TwistWithCovarianceStamped instead of nav_msgs/Odometry
frame_id: "map" # origin frame
child_frame_id: "base_link" # body-fixed frame
vel_error: 0.1 # wheel velocity measurement error 1-std (m/s)
tf:
send: true
frame_id: "map"
child_frame_id: "base_link"
# vim:set ts=2 sw=2 et:

View File

@ -0,0 +1,15 @@
plugin_blacklist:
# common
- safety_area
# extras
- image_pub
- vibration
- distance_sensor
- rangefinder
- wheel_odometry
- mission
- tdr_radio
- fake_gps
plugin_whitelist: []
#- 'sys_*'

View File

@ -1,9 +0,0 @@
digraph G {
"local_origin" -> "local_origin_ned"[label="Broadcaster: /mavros\nAverage rate: 10000.000 Hz\nMost recent transform: 0.000 ( 124.428 sec old)\nBuffer length: 0.000 sec\n"];
"fcu" -> "fcu_frd"[label="Broadcaster: /mavros\nAverage rate: 10000.000 Hz\nMost recent transform: 0.000 ( 124.428 sec old)\nBuffer length: 0.000 sec\n"];
"fcu_frd" -> "fcu"[label="Broadcaster: /mavros\nAverage rate: 10000.000 Hz\nMost recent transform: 0.000 ( 124.428 sec old)\nBuffer length: 0.000 sec\n"];
edge [style=invis];
subgraph cluster_legend { style=bold; color=black; label ="view_frames Result";
"Recorded at time: 124.428"[ shape=plaintext ] ;
}->"local_origin";
}

Binary file not shown.

View File

@ -14,6 +14,7 @@ Launch file to use klausen oscillaton damping ctrl in Gazebo
pkg="oscillation_ctrl"
type="MoCap_Localization_noTether.py"
name="localize_node"
launch-prefix="xterm -e"
/>
<!-- Cortex bridge launch -->
@ -21,4 +22,6 @@ Launch file to use klausen oscillaton damping ctrl in Gazebo
<!-- MAVROS launch -->
<include file="$(find mavros)/launch/px4.launch"/>
<arg name="pluginlists_yaml" value="$(find oscillation_ctrl)/config/px4_pluginlists.yaml" />
<arg name="config_yaml" value="$(find oscillation_ctrl)/config/px4_config.yaml" />
</launch>

View File

@ -22,6 +22,11 @@ Launch file to use klausen oscillaton damping ctrl in Gazebo
name="linkStates_node"
launch-prefix="xterm -e"
/>
<node
pkg="oscillation_ctrl"
type="wpoint_tracker.py"
name="waypoints_server"
/>
<node
pkg="oscillation_ctrl"
type="ref_signalGen.py"

View File

@ -11,7 +11,6 @@ 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
from std_msgs.msg import Bool
class Main:
@ -53,6 +52,9 @@ class Main:
self.phidot_max = 3.0
self.thetadot_max = 3.0
# Vehicle is spawned with yaw offset for convenience, need to deal with that
self.yaw_offset = 0.0
# variables for message gen
self.status = tethered_status()
self.status.drone_id = 'spiri_with_tether::spiri::base'
@ -97,18 +99,18 @@ class Main:
output = value
return output
def euler_array(self,pose):
def euler_array(self,orientation):
"""
Takes in pose msg object and outputs array of euler angs:
q[0] = Roll
q[1] = Pitch
q[2] = Yaw
eul[0] = Roll
eul[1] = Pitch
eul[2] = Yaw
"""
q = euler_from_quaternion([pose.orientation.x,
pose.orientation.y,
pose.orientation.z,
pose.orientation.w])
return q
eul = euler_from_quaternion([orientation.x,
orientation.y,
orientation.z,
orientation.w])
return eul
# Get link states (drone and pload) and determine angle between them
def link_state(self,pub_timer):
@ -125,9 +127,9 @@ class Main:
# 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)
# Get orientation of drone in euler angles to determine yaw offset
drone_Eul = self.euler_array(drone_P.link_state.pose.orientation)
# Check if payload is part of simulation
if not drone_P.success:
self.status.drone_id = 'spiri::base'
@ -138,6 +140,9 @@ class Main:
if not self.has_run == 1:
if self.pload == True:
# Determine yaw offset
self.yaw_offset = drone_Eul[2]
# 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 +
@ -158,6 +163,19 @@ class Main:
drone_Py = drone_P.link_state.pose.position.y
drone_Pz = drone_P.link_state.pose.position.z
# Get drone orientation
drone_q = [drone_P.link_state.pose.orientation.x,drone_P.link_state.pose.orientation.y,
drone_P.link_state.pose.orientation.z,drone_P.link_state.pose.orientation.w]
# offset orientation by yaw offset
q_offset = quaternion_from_euler(0,0,-self.yaw_offset)
#drone_q = quaternion_multiply(drone_q,q_offset)
drone_P.link_state.pose.orientation.x = drone_q[0]
drone_P.link_state.pose.orientation.y = drone_q[1]
drone_P.link_state.pose.orientation.z = drone_q[2]
drone_P.link_state.pose.orientation.w = drone_q[3]
if self.pload == True: # If there is payload, determine the variables
# Pload
pload_Px = pload_P.link_state.pose.position.x
@ -195,7 +213,7 @@ class Main:
# 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"Roll: "+str(round(drone_Eul[0],2)),"\nPitch: "+str(round(drone_Eul[1],2)),"\nYaw: "+str(round(drone_Eul[2],4))
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))

View File

@ -9,7 +9,7 @@ import time
import math
from tf.transformations import *
from oscillation_ctrl.msg import tethered_status
from geometry_msgs.msg import PoseStamped
from geometry_msgs.msg import PoseStamped, Point
from std_msgs.msg import Bool
class Main:
@ -19,8 +19,6 @@ class Main:
# 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
@ -41,19 +39,10 @@ class Main:
### -*-*-*- END -*-*-*- ###
# initialize variables
#self.buff_pose1 = PoseStamped()
self.drone_pose = PoseStamped()
self.buff_pose = PoseStamped()
# Debugging
# self.buff_pose.pose.position.y = 0
# self.buff_pose.pose.position.x = 0
# self.buff_pose.pose.position.z = 0
# self.buff_pose.pose.orientation.y = 0
# self.buff_pose.pose.orientation.x = 0
# self.buff_pose.pose.orientation.z = 0
# self.buff_pose.pose.orientation.w = 1
# self.buff_pose.header.frame_id = 'map'
self.eul = [0.0,0.0,0.0]
# Max dot values to prevent 'blowup'
self.phidot_max = 3.0
@ -71,12 +60,6 @@ class Main:
# 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)
### Since there is no tether, we can publish directly to mavros
self.pose_pub = rospy.Publisher('/mavros/vision_pose/pose', PoseStamped, queue_size=1)
@ -104,18 +87,17 @@ class Main:
output = value
return output
def euler_array(self,pose):
def euler_array(self):
"""
Takes in pose msg object and outputs array of euler angs:
q[0] = Roll
q[1] = Pitch
q[2] = Yaw
eul[0] = Roll
eul[1] = Pitch
eul[2] = Yaw
"""
q = euler_from_quaternion([pose.orientation.x,
pose.orientation.y,
pose.orientation.z,
pose.orientation.w])
return q
self.eul = euler_from_quaternion([self.drone_pose.pose.orientation.x,
self.drone_pose.pose.orientation.y,
self.drone_pose.pose.orientation.z,
self.drone_pose.pose.orientation.w])
def FRD_Transform(self):
'''
@ -123,7 +105,20 @@ class Main:
'''
self.drone_pose = self.buff_pose
self.drone_pose.header.frame_id = "map"
self.drone_pose.header.frame_id = "/map"
# self.drone_pose.pose.position.x = 0
# self.drone_pose.pose.position.y = 0
# self.drone_pose.pose.position.z = 0.5
#Keep the w same and change x, y, and z as above.
# self.drone_pose.pose.orientation.x = 0
# self.drone_pose.pose.orientation.y = 0
# self.drone_pose.pose.orientation.z = 0
# self.drone_pose.pose.orientation.w = 1
self.euler_array() # get euler angles of orientation for user
# self.drone_pose.pose.position.x = self.buff_pose.pose.position.y
# self.drone_pose.pose.position.y = self.buff_pose.pose.position.x
# self.drone_pose.pose.position.z = -self.buff_pose.pose.position.z
@ -157,6 +152,9 @@ class Main:
print "drone pos.x: " + str(round(self.drone_pose.pose.position.x,2))
print "drone pos.y: " + str(round(self.drone_pose.pose.position.y,2))
print "drone pos.z: " + str(round(self.drone_pose.pose.position.z,2))
print "Roll: " + str(round(self.eul[0]*180/3.14,2))
print "Pitch: " + str(round(self.eul[1]*180/3.14,2))
print "Yaw: " + str(round(self.eul[2]*180/3.14,2))
if __name__=="__main__":

View File

@ -308,9 +308,6 @@ class Main:
# Attitude control
self.quaternion.header.stamp = rospy.Time.now()
# self.quaternion.pose.position.x = 0
# self.quaternion.pose.position.y = 0
# self.quaternion.pose.position.z = 5.0
self.quaternion.pose.orientation.x = q[0]
self.quaternion.pose.orientation.y = q[1]
self.quaternion.pose.orientation.z = q[2]

View File

@ -2,7 +2,6 @@
* @file offb_node.cpp
* @brief Offboard control example node, written with MAVROS version 0.19.x, PX4 Pro Flight
* Stack and tested in Gazebo SITL
* Cesar: Now used for path following on topic \path
*/
#include <ros/ros.h>

View File

@ -5,8 +5,9 @@
#include <ros/ros.h>
#include <sensor_msgs/NavSatFix.h>
#include <offboard_ex/RefSignal.h>
#include <offboard_ex/EulerAngles.h>
#include <oscillation_ctrl/RefSignal.h>
#include <oscillation_ctrl/EulerAngles.h>
#include <oscillation_ctrl/WaypointTrack.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Quaternion.h>
#include <geometry_msgs/Vector3.h>
@ -31,7 +32,7 @@ geometry_msgs::PoseStamped desPose;
tf2::Quaternion q_cmd /*What I send*/, q_Jae; // What should be sent;
double rol_K, pch_K, yaw_K, rol_J, pch_J, yaw_J; // Same logic, K = cmd
geometry_msgs::Vector3 K_ang, J_ang; // Needed to populate EulerAngle msg
offboard_ex::EulerAngles eulAng;
oscillation_ctrl::EulerAngles eulAng;
mavros_msgs::AttitudeTarget thrust;
@ -42,12 +43,9 @@ void state_cb(const mavros_msgs::State::ConstPtr& msg){
}
geometry_msgs::Quaternion q_des;
double alt_des; // Check desired height
// Cb to determine desired pose *Only needed for orientation
void q_cb(const geometry_msgs::PoseStamped::ConstPtr& msg){
desPose = *msg;
alt_des = desPose.pose.position.z;
q_des = desPose.pose.orientation;
tf2::convert(q_des,q_cmd); // Convert msg type to tf2::quaternion type
}
@ -83,7 +81,6 @@ void thrust_cb(const mavros_msgs::AttitudeTarget msg){
tf2::convert(thrust.orientation,q_Jae); // Convert from msg to quat
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "path_follow");
@ -116,7 +113,7 @@ int main(int argc, char **argv)
("mavros/setpoint_raw/attitude",10);
// Publish attitude cmds in euler angles
ros::Publisher euler_pub = nh.advertise<offboard_ex::EulerAngles>
ros::Publisher euler_pub = nh.advertise<oscillation_ctrl::EulerAngles>
("command/euler_angles",10);
// Service Clients
@ -126,6 +123,8 @@ int main(int argc, char **argv)
("mavros/set_mode");
ros::ServiceClient takeoff_cl = nh.serviceClient<mavros_msgs::CommandTOL>
("mavros/cmd/takeoff");
ros::ServiceClient waypoint_cl = nh.serviceClient<oscillation_ctrl::WaypointTrack>
("status/waypoint_tracker");
//the setpoint publishing rate MUST be faster than 2Hz... PX4 timeout = 500 ms
ros::Rate rate(20.0);
@ -140,18 +139,24 @@ int main(int argc, char **argv)
/*********** Initiate variables ************************/
// Needed for attitude command
mavros_msgs::AttitudeTarget attitude;
attitude.header.frame_id = "map"; // NED Ref Frame(?) #TODO
attitude.type_mask = 1|2|4; // Ignore body rates
// Retrieve parameters from Ctrl_param.yaml file
/*std::vector<double> waypoints;
nh.getParam("sim/waypoints",waypoints);
// Need to send pose commands until throttle has been established
buff_pose.pose.position.x = waypoints(1);
buff_pose.pose.position.y = waypoints(2);
buff_pose.pose.position.z = waypoints(3);*/
// Retrieve desired waypoints
oscillation_ctrl::WaypointTrack wpoint_srv;
wpoint_srv.request.query = false;
if (waypoint_cl.call(wpoint_srv))
{
ROS_INFO("Waypoints received");
}
// populate desired waypoints - this is only for original hover as
// a change of waypoints is handled by ref_signal.py
buff_pose.pose.position.x = wpoint_srv.response.xd.x;
buff_pose.pose.position.y = wpoint_srv.response.xd.y;
buff_pose.pose.position.z = wpoint_srv.response.xd.z;
double alt_des = buff_pose.pose.position.z; // Desired height
// Desired mode is offboard
mavros_msgs::SetMode offb_set_mode;

View File

@ -8,22 +8,17 @@ import numpy as np
import time
import math
from pymavlink import mavutil
from scipy import signal
from scipy.integrate import odeint
from oscillation_ctrl.msg import tethered_status, RefSignal
from oscillation_ctrl.srv import WaypointTrack, WaypointTrackRequest
from controller_msgs.msg import FlatTarget
from geometry_msgs.msg import Pose, Vector3, PoseStamped, Point
from geometry_msgs.msg import TwistStamped
from geometry_msgs.msg import Pose, Vector3, PoseStamped, Point, TwistStamped
from sensor_msgs.msg import Imu
from mavros_msgs.msg import State
from pymavlink import mavutil
class Main:
def __init__(self):
@ -35,6 +30,7 @@ class Main:
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()
rospy.loginfo(self.tstart)
self.dt = 1.0/rate
# self.dt = 0.5
@ -71,6 +67,33 @@ class Main:
if rospy.has_param('sim/tether_length'):
self.tetherL = rospy.get_param('sim/tether_length') # Tether length
self.param_exists = True
elif rospy.get_time() - self.tstart >= 3.0:
self.tetherL = 0.0
break
# --------------------------------------------------------------------------------#
# SUBSCRIBERS #
# --------------------------------------------------------------------------------#
# Topic, msg type, and class callback method
rospy.Subscriber('/status/twoBody_status', tethered_status, self.linkState_cb)
rospy.Subscriber('/mavros/local_position/velocity_body', TwistStamped, self.droneVel_cb)
rospy.Subscriber('/mavros/imu/data', Imu, self.droneAcc_cb)
rospy.Subscriber('/mavros/state', State, self.state_cb)
# --------------------------------------------------------------------------------#
# PUBLISHERS
# --------------------------------------------------------------------------------#
# Publish desired path to compute attitudes
self.pub_path = rospy.Publisher('/reference/path',FlatTarget,queue_size = 10)
# Needed for geometric controller to compute thrust
self.pub_ref = rospy.Publisher('/reference/flatsetpoint',FlatTarget,queue_size = 10)
# timer(s), used to control method loop freq(s) as defined by the rate(s)
self.pub_tim = rospy.Timer(rospy.Duration(1.0/rate), self.publisher)
# --------------------------------------------------------------------------------#
# FEEDBACK AND INPUT SHAPING
# --------------------------------------------------------------------------------#
# Smooth path variables
self.EPS_F = np.zeros(9) # Final Epsilon/ signal
@ -79,7 +102,12 @@ class Main:
# Constants for smooth path generation
self.w_tune = 3.13 # 3.13 works well? #########################################################################
self.epsilon = 0.7 # Damping ratio
self.wn = math.sqrt(9.81/self.tetherL)
# need exception if we do not have tether:
if self.tetherL == 0.0:
self.wn = self.w_tune
else:
self.wn = math.sqrt(9.81/self.tetherL)
self.wd = self.wn*math.sqrt(1 - self.epsilon**2)
self.k4 = 4*self.epsilon*self.w_tune
self.k3 = ((2 + 4*self.epsilon**2)*self.w_tune**2)/self.k4
@ -100,9 +128,9 @@ class Main:
#elif rospy.get_time() - self.tstart >= 3.0:
# self.xd = np.array([[0],[0],[5.0]]) # make our own if there are no waypoints
self.xd = Point()
self.get_xd = rospy.ServiceProxy('status/waypoint_tracker',WaypointTrack)
#self.xd = Point()
self.get_xd = rospy.ServiceProxy('/status/waypoint_tracker',WaypointTrack)
self.empty_point = Point() # Needed to query waypoint_server
self.des_waypoints = True # True = changing waypoints
@ -111,10 +139,6 @@ class Main:
self.y0 = [0, 0, 0, 0]
self.z0 = [0, 0, 0, 0]
# --------------------------------------------------------------------------------#
# FEEDBACK AND INPUT SHAPING
# --------------------------------------------------------------------------------#
# Constants for feedback
self.Gd = 0.325
self.td = 2*self.Gd*math.pi/self.wd
@ -148,30 +172,6 @@ class Main:
self.sk = len(self.SF_delay_x[0]) # from Klausen 2017
self.ak = np.zeros(len(self.SF_delay_x[0]))
self.s_gain = 0.0 # Gain found from sigmoid
# --------------------------------------------------------------------------------#
# SUBSCRIBERS #
# --------------------------------------------------------------------------------#
# Topic, msg type, and class callback method
rospy.Subscriber('/status/twoBody_status', tethered_status, self.linkState_cb)
rospy.Subscriber('/mavros/local_position/velocity_body', TwistStamped, self.droneVel_cb)
rospy.Subscriber('/mavros/imu/data', Imu, self.droneAcc_cb)
rospy.Subscriber('/mavros/state', State, self.state_cb)
#rospy.Subscriber('/reference/waypoints',Position, self.waypoints_cb) ### Change to position
# --------------------------------------------------------------------------------#
# PUBLISHERS
# --------------------------------------------------------------------------------#
# Publish desired path to compute attitudes
self.pub_path = rospy.Publisher('/reference/path',FlatTarget,queue_size = 10)
# Needed for geometric controller to compute thrust
self.pub_ref = rospy.Publisher('/reference/flatsetpoint',FlatTarget,queue_size = 10)
# timer(s), used to control method loop freq(s) as defined by the rate(s)
self.pub_tim = rospy.Timer(rospy.Duration(1.0/rate), self.publisher)
# ------------------------------------ _init_ ends ------------------------------ #
# --------------------------------------------------------------------------------#
# CALLBACK FUNCTIONS
# --------------------------------------------------------------------------------#
@ -230,13 +230,18 @@ class Main:
pass
def waypoints_srv_cb(self):
rospy.wait_for_service('status/waypoint_tracker')
rospy.wait_for_service('/status/waypoint_tracker')
try:
self.xd = self.get_xd(True)
# if self.des_waypoints == True:
resp = self.get_xd(False,self.empty_point)
self.xd = resp.xd
except ValueError:
pass
#################################################################
#TODO Will need to add a function that gets a message from #
# a node which lets refsignal_gen.py know there has been a #
# change in xd and therefore runs waypoints_srv_cb again #
#################################################################
# --------------------------------------------------------------------------------#
# ALGORITHM
@ -322,9 +327,9 @@ class Main:
else:
print('No delay')
# Convolution and input shape filter, and feedback
# Convolution of shape filter and trajectory, and feedback
def convo(self):
self.waypoints_srv_cb()
# needed for delay function
# 1 = determine shapefilter array
# 2 = determine theta/phi_fb
@ -332,9 +337,9 @@ class Main:
feedBack = 2
# SOLVE ODE (get ref pos, vel, accel)
self.x = odeint(self.statespace,self.x0,self.t,args=(self.xd[0],))
self.y = odeint(self.statespace,self.y0,self.t,args=(self.xd[1],))
self.z = odeint(self.statespace,self.z0,self.t,args=(self.xd[2],))
self.x = odeint(self.statespace,self.x0,self.t,args=(self.xd.x,))
self.y = odeint(self.statespace,self.y0,self.t,args=(self.xd.y,))
self.z = odeint(self.statespace,self.z0,self.t,args=(self.xd.z,))
for i in range(1,len(self.y0)):
self.x[:,i] = np.clip(self.x[:,i], a_min = -self.max[i], a_max = self.max[i])
@ -343,7 +348,7 @@ class Main:
for j in range(3): # 3 is due to pos, vel, acc. NOT due x, y, z
self.delay(j,shapeFil) # Determine the delay array
self.delay(j,shapeFil) # Determine the delay (shapefilter) array
if self.SF_idx < len(self.SF_delay_x[0]):
self.EPS_I[3*j] = self.x[1,j]
@ -363,9 +368,21 @@ class Main:
# Populate EPS_F buffer with desired change based on feedback
self.EPS_F[i] = self.EPS_I[i] + EPS_D[i] #+ EPS_D[i]
# self.x0 = [self.dr_pos.position.x, self.dr_vel.x, self.dr_acc.x, self.x[1,3]]
# self.y0 = [self.dr_pos.position.y, self.dr_vel.y, self.dr_acc.y, self.y[1,3]]
# self.z0 = [self.dr_pos.position.z, self.dr_vel.z, self.dr_acc.z, self.z[1,3]]
# Populate msg with epsilon_final
self.ref_sig.header.stamp = rospy.Time.now()
self.ref_sig.type_mask = 2 # Need typemask = 2 to use correct attitude controller - Jaeyoung Lin
self.ref_sig.position.x = self.EPS_F[0]
self.ref_sig.position.y = self.EPS_F[1]
self.ref_sig.position.z = self.EPS_F[2] + 0.5
self.ref_sig.velocity.x = self.EPS_F[3]
self.ref_sig.velocity.y = self.EPS_F[4]
self.ref_sig.velocity.z = self.EPS_F[5]
self.ref_sig.acceleration.x = self.EPS_F[6]
self.ref_sig.acceleration.y = self.EPS_F[7]
self.ref_sig.acceleration.z = self.EPS_F[8]
self.x0 = [self.dr_pos.position.x, self.x[1,1], self.x[1,2], self.x[1,3]]
self.y0 = [self.dr_pos.position.y, self.y[1,1], self.y[1,2], self.y[1,3]]
@ -389,37 +406,31 @@ class Main:
return EPS_D
def publisher(self,pub_tim):
# Determine final ref signal
self.convo()
# Populate msg with epsilon_final
self.ref_sig.position.x = self.EPS_F[0]
self.ref_sig.position.y = self.EPS_F[1]
def screen_output(self):
self.ref_sig.velocity.x = self.EPS_F[3]
self.ref_sig.velocity.y = self.EPS_F[4]
self.ref_sig.velocity.z = self.EPS_F[5]
self.ref_sig.acceleration.x = self.EPS_F[6]
self.ref_sig.acceleration.y = self.EPS_F[7]
self.ref_sig.acceleration.z = self.EPS_F[8]
# Need typemask = 2 to use correct attitude controller - Jaeyoung Lin
self.ref_sig.type_mask = 2
# Publish command
self.ref_sig.header.stamp = rospy.Time.now()
self.pub_path.publish(self.ref_sig)
self.pub_ref.publish(self.ref_sig)
# Feedback to user
rospy.loginfo(' Var | x | y | z ')
rospy.loginfo('Pos: %.2f %.2f %.2f',self.EPS_F[0],self.EPS_F[1],self.EPS_F[2])
rospy.loginfo('Vel: %.2f %.2f %.2f',self.EPS_F[3],self.EPS_F[4],self.EPS_F[5])
rospy.loginfo('Acc: %.2f %.2f %.2f',self.EPS_F[6],self.EPS_F[7],self.EPS_F[8])
rospy.loginfo('_______________________')
def publisher(self,pub_tim):
# Determine final ref signal
self.convo()
# Publish reference signal
self.pub_path.publish(self.ref_sig)
self.pub_ref.publish(self.ref_sig)
# Give user feedback on published message:
rospy.loginfo(' Var | x | y | z ')
rospy.loginfo('Pos: %.2f %.2f %.2f',self.EPS_F[0],self.EPS_F[1],self.EPS_F[2])
rospy.loginfo('Vel: %.2f %.2f %.2f',self.EPS_F[3],self.EPS_F[4],self.EPS_F[5])
rospy.loginfo('Acc: %.2f %.2f %.2f',self.EPS_F[6],self.EPS_F[7],self.EPS_F[8])
rospy.loginfo('_______________________')
if __name__=="__main__":

View File

@ -5,7 +5,7 @@
### Script to generate set points which form a square with 2m side lengths
import rospy, tf
import time
from geometry_msgs.msg import PoseStamped
from geometry_msgs.msg import Point
from std_msgs.msg import Bool
class Main:
@ -13,11 +13,11 @@ class Main:
def __init__(self):
# variable(s)
self.Point = PoseStamped()
self.Point = Point()
# Init x, y, & z coordinates
self.Point.pose.position.x = 0
self.Point.pose.position.y = 0
self.Point.pose.position.z = 3.5
self.Point.x = 0
self.Point.y = 0
self.Point.z = 3.5
self.xarray = [1,2,2,2,1,0,0]
self.yarray = [0,0,1,2,2,2,1]
@ -30,7 +30,7 @@ class Main:
rospy.Subscriber('/status/path_follow',Bool, self.wait_cb)
# publisher(s), with specified topic, message type, and queue_size
self.pub_square = rospy.Publisher('/reference/waypoints', PoseStamped, queue_size = 5)
self.pub_square = rospy.Publisher('/reference/waypoints', Point, queue_size = 5)
# rate(s)
pub_rate = 1 # rate for the publisher method, specified in Hz
@ -49,28 +49,28 @@ class Main:
else:
# Check if i is too large. loop back to first point
if self.i >= len(self.xarray):
self.Point.pose.position.x = 0
self.Point.pose.position.y = 0
self.Point.x = 0
self.Point.y = 0
else:
self.Point.pose.position.x = self.xarray[self.i]
self.Point.pose.position.y = self.yarray[self.i]
self.Point.x = self.xarray[self.i]
self.Point.y = self.yarray[self.i]
rospy.loginfo("Sending [Point x] %d [Point y] %d",
self.Point.pose.position.x, self.Point.pose.position.y)
self.Point.x, self.Point.y)
# Published desired msgs
self.Point.header.stamp = rospy.Time.now()
# self.Point.header.stamp = rospy.Time.now()
self.pub_square.publish(self.Point)
self.j += 1
self.i = self.j // self.buffer
# self.Point.header.stamp = rospy.Time.now()
# self.Point.pose.position.x = self.xarray[self.i]
# self.Point.pose.position.y = self.yarray[self.i]
# self.Point.x = self.xarray[self.i]
# self.Point.y = self.yarray[self.i]
# rospy.loginfo("Sending [Point x] %d [Point y] %d",
# self.Point.pose.position.x, self.Point.pose.position.y)
# self.Point.x, self.Point.y)
# Published desired msgs
# self.pub_square.publish(self.Point)

View File

@ -14,9 +14,9 @@ class Main:
# variable(s)
self.Point = Point()
# Init x, y, & z coordinates
self.Point.pose.position.x = 1
self.Point.pose.position.y = 0
self.Point.pose.position.z = 4.0
self.Point.x = 1
self.Point.y = 0
self.Point.z = 4.0
self.bool = False
@ -41,13 +41,13 @@ class Main:
if self.bool == False:
rospy.loginfo('Waiting...')
else:
self.Point.header.stamp = rospy.Time.now()
#self.Point.header.stamp = rospy.Time.now()
# Published desired msgs
self.pub_step.publish(self.Point)
rospy.loginfo("Sending [Point x] %d [Point y] %d",
self.Point.pose.position.x, self.Point.pose.position.y)
self.Point.x, self.Point.y)
if __name__=="__main__":

View File

@ -36,6 +36,7 @@ class Main:
self.xd.x = 0.0
self.xd.y = 0.0
self.xd.z = 5.0
break
# service(s)
@ -47,7 +48,12 @@ class Main:
def waypoint_relay(self,req):
self.resp.xd = self.xd
#TODO Need to add check to make sure query is boolean
if req.query:
self.xd = req.xd
self.resp.xd = self.xd
else:
self.resp.xd = self.xd
return self.resp
# Change desired position if there is a change
@ -63,7 +69,9 @@ if __name__=="__main__":
rospy.init_node('waypoints_server',anonymous=False)
try:
obj = Main() # create class object
s = rospy.Service('status/waypoint_tracker',WaypointTrack,obj.waypoint_relay)
s = rospy.Service('status/waypoint_tracker',WaypointTrack,obj.waypoint_relay)
rospy.loginfo_once('waypoints_server has started with:\nxd.x = %.1f\nxd.y = %.1f\nxd.z = %.1f', obj.xd.x, obj.xd.y, obj.xd.z)
rospy.spin() # loop until shutdown signal
except rospy.ROSInterruptException:

View File

@ -1,4 +1,5 @@
bool query # bool to request waypoints
bool query # bool to request or set waypoints
geometry_msgs/Point xd # waypoints
---
geometry_msgs/Point xd # waypoints