PX4 simulation stack
simple_sweep_demo.cpp
Go to the documentation of this file.
1 
7 // C/C++ libraries
8 #include <cmath>
9 #include <string>
10 #include <cstdlib>
11 
12 // roscpp
13 #include <ros/ros.h>
14 #include <ros/topic.h>
15 
16 // laser assembler
17 #include <laser_assembler/AssembleScans2.h>
18 
19 // geometry msgs
20 #include <geometry_msgs/PoseStamped.h>
21 #include <geometry_msgs/TwistStamped.h>
22 
23 // sensor msgs
24 #include <sensor_msgs/NavSatFix.h>
25 #include <sensor_msgs/PointCloud2.h>
26 
27 // mavros msgs
28 #include <mavros_msgs/CommandBool.h>
29 #include <mavros_msgs/SetMode.h>
30 #include <mavros_msgs/State.h>
31 #include <mavros_msgs/CommandTOL.h>
32 #include <mavros_msgs/CommandHome.h>
33 
34 // pcl
35 #include <pcl/io/pcd_io.h>
36 #include <pcl/point_types.h>
37 
38 #include <pcl_conversions/pcl_conversions.h>
39 
40 bool isGoalReached(const geometry_msgs::PoseStamped &curr_pose,
41  const geometry_msgs::PoseStamped &goal_pose, double threshold=0.5)
42 {
43  geometry_msgs::Point curr = curr_pose.pose.position;
44  geometry_msgs::Point goal = goal_pose.pose.position;
45  return std::sqrt(std::pow(goal.x - curr.x, 2)
46  + std::pow(goal.y - curr.y, 2)
47  + std::pow(goal.z - curr.z, 2)) > threshold ? false : true;
48 }
49 
57 std::array<std::vector<double>, 2> linearInterp(const std::array<double, 2> &p1,
58  const std::array<double, 2> &p2, const double step)
59 {
60  // Gradient
61  double a = (p1.at(1) - p2.at(1)) / (p1.at(0) - p2.at(0));
62  // Intercept
63  double b = p1.at(1) - a*p1.at(0);
64 
65  // Number of steps
66  int num_steps = std::floor((p2.at(0) - p1.at(0))/step);
67 
68  // Initialize container for interpolated points
69  std::vector<double> points_x(num_steps+1);
70 
71  // Set interpolated points
72  points_x.front() = p1.at(0);
73  for(int i=1; i<num_steps; ++i)
74  {
75  points_x.at(i) = step * i + p1.at(0);
76  }
77  points_x.back() = p2.at(0);
78 
79  // Initialize container for interpolated points
80  std::vector<double> points_y(num_steps+1);
81 
82  // Set interpolated points
83  points_y.front() = p1.at(1);
84  for(int i=1; i<num_steps; ++i)
85  {
86  points_y.at(i) = a*(p1.at(0) + i*step) + b;
87  }
88  points_y.back() = p2.at(1);
89 
90  // Initialize container for vector of points
91  std::array<std::vector<double>, 2> points;
92  points.front() = points_x;
93  points.back() = points_y;
94 
95  return points;
96 }
97 
105 std::vector<geometry_msgs::PoseStamped> getBilinearPath(const geometry_msgs::PoseStamped &start,
106  const geometry_msgs::PoseStamped &goal,
107  const double step=0.05)
108 {
109  std::vector<geometry_msgs::PoseStamped> bilinear_path;
110 
111  // Store x-y and x-z coordinates of start point
112  std::array<double, 2> start_xy = {start.pose.position.x, start.pose.position.y};
113  std::array<double, 2> start_xz = {start.pose.position.x, start.pose.position.z};
114  // Store x-y and x-z coordinates of goal point
115  std::array<double, 2> goal_xy = {goal.pose.position.x, goal.pose.position.y};
116  std::array<double, 2> goal_xz = {goal.pose.position.x, goal.pose.position.z};
117 
118  // x-y and x-z coordinates of interpolated points
119  std::array<std::vector<double>, 2> points_xy = linearInterp(start_xy, goal_xy, step);
120  std::array<std::vector<double>, 2> points_xz = linearInterp(start_xz, goal_xz, step);
121 
122  // Number of generated points by interpolation
123  int num_points = points_xy.at(0).size();
124 
125  try
126  {
127  // Generate PoseStamped message from std::array
128  for(int i=0; i<num_points; ++i)
129  {
130  geometry_msgs::PoseStamped pose;
131  pose.pose.orientation = start.pose.orientation;
132  pose.pose.position.x = points_xy.front().at(i);
133  pose.pose.position.y = points_xy.back().at(i);
134  pose.pose.position.z = points_xz.back().at(i);
135 
136  bilinear_path.push_back(pose);
137  }
138  }
139  catch (std::out_of_range &ex)
140  {
141  ROS_ERROR("%s", ex.what());
142  }
143 
144  return bilinear_path;
145 }
146 
147 int main(int argc, char **argv)
148 {
149  ros::init(argc, argv, "simple_sweep_demo");
150  ros::NodeHandle nh("~");
151 
152  // current vehicle state
153  mavros_msgs::State current_state;
154  // current local position
155  geometry_msgs::PoseStamped local_pos;
156  // current global position
157  sensor_msgs::NavSatFix global_pos;
158  // current velocity of vehicle
159  geometry_msgs::TwistStamped fcu_vel;
160 
161  // Subscriber
162  ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
163  ("/mavros/state", 10,
164  [&current_state](const mavros_msgs::State::ConstPtr& msg){current_state=*msg;});
165  ros::Subscriber local_pos_sub = nh.subscribe<geometry_msgs::PoseStamped>
166  ("/mavros/local_position/pose", 100,
167  [&local_pos](const geometry_msgs::PoseStamped::ConstPtr& msg){local_pos=*msg;});
168  ros::Subscriber curr_gpos_sub = nh.subscribe<sensor_msgs::NavSatFix>
169  ("/mavros/global_position/global", 10,
170  [&global_pos](const sensor_msgs::NavSatFix::ConstPtr& msg){global_pos=*msg;});
171  ros::Subscriber fcu_vel_sub = nh.subscribe<geometry_msgs::TwistStamped>
172  ("/mavros/local_position/velocity", 100,
173  [&fcu_vel](const geometry_msgs::TwistStamped::ConstPtr& msg){fcu_vel=*msg;});
174 
175  // Publisher
176  ros::Publisher target_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
177  ("/mavros/setpoint_position/local", 10);
178  ros::Publisher point_cloud_pub = nh.advertise<sensor_msgs::PointCloud2>
179  ("/cloud", 10);
180 
181  // Service Client
182  ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
183  ("/mavros/cmd/arming");
184  ros::ServiceClient set_hp_client = nh.serviceClient<mavros_msgs::CommandHome>
185  ("/mavros/cmd/set_home");
186  ros::ServiceClient takeoff_client = nh.serviceClient<mavros_msgs::CommandTOL>
187  ("/mavros/cmd/takeoff");
188  ros::ServiceClient landing_client = nh.serviceClient<mavros_msgs::CommandTOL>
189  ("/mavros/cmd/land");
190  ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
191  ("/mavros/set_mode");
192  ros::ServiceClient pc_gen_client = nh.serviceClient<laser_assembler::AssembleScans2>
193  ("/assemble_scans2");
194 
195  // Takeoff height
196  float takeoff_height;
197  nh.param<float>("takeoff_height", takeoff_height, 5.5);
198 
199  ROS_INFO("Takeoff height: %f", takeoff_height);
200 
201  // Flight length
202  float flight_length;
203  nh.param<float>("flight_length", flight_length, 20.0);
204 
205  ROS_INFO("Flight Length: %f", flight_length);
206 
207  // wait for laser assembler
208  ros::service::waitForService("/assemble_scans2");
209  laser_assembler::AssembleScans2 pc_gen_cmd;
210 
211  //the setpoint publishing rate MUST be faster than 2Hz
212  ros::Rate rate(20.0);
213 
214  // wait for FCU connection
215  while(ros::ok() and current_state.connected){
216  ros::spinOnce();
217  rate.sleep();
218  }
219 
220  // wait for message arriving from /mavros/global_position/global
221  ROS_INFO("Waiting for message from /mavros/global_position/global");
222  const std::string gpos_topic = "/mavros/global_position/global";
223  sensor_msgs::NavSatFix init_gpos = *ros::topic::waitForMessage<sensor_msgs::NavSatFix>(gpos_topic);
224  double init_latitude = init_gpos.latitude;
225  double init_longitude = init_gpos.longitude;
226  double init_altitude = init_gpos.altitude;
227  ROS_INFO("Initial Lat: %f Lon: %f Alt: %f", init_latitude, init_longitude, init_altitude);
228 
229  // arm vehicle
230  mavros_msgs::CommandBool arm_cmd;
231  arm_cmd.request.value = true;
232  while(ros::ok() and not(arming_client.call(arm_cmd))){
233  ros::spinOnce();
234  rate.sleep();
235  }
236  ROS_INFO("Vehicle armed.");
237 
238  // takeoff
239  mavros_msgs::CommandTOL takeoff_cmd;
240  takeoff_cmd.request.altitude = init_altitude + takeoff_height;
241  takeoff_cmd.request.longitude = init_longitude;
242  takeoff_cmd.request.latitude = init_latitude;
243  ROS_DEBUG("set lat: %f, lon: %f, alt: %f", init_latitude, init_longitude, init_altitude);
244 
245  while(ros::ok() and not(takeoff_client.call(takeoff_cmd)) and
246  takeoff_cmd.response.success){
247  ros::spinOnce();
248  rate.sleep();
249  }
250  while(ros::ok() and local_pos.pose.position.z < takeoff_height-0.1){
251  ros::spinOnce();
252  rate.sleep();
253  ROS_DEBUG("Current lat: %f, lon: %f, alt: %f", global_pos.latitude,
254  global_pos.longitude,
255  global_pos.altitude);
256  }
257  ROS_INFO("Vehicle tookoff.");
258 
259  pc_gen_cmd.request.begin = ros::Time::now();
260 
261  // move along y axis
262  geometry_msgs::PoseStamped target_pos_msg;
263  target_pos_msg.pose.position.x = 0;
264  target_pos_msg.pose.position.y = flight_length;
265  target_pos_msg.pose.position.z = takeoff_height;
266  target_pos_msg.pose.orientation.x = local_pos.pose.orientation.x;
267  target_pos_msg.pose.orientation.y = local_pos.pose.orientation.y;
268  target_pos_msg.pose.orientation.z = local_pos.pose.orientation.z;
269  target_pos_msg.pose.orientation.w = local_pos.pose.orientation.w;
270 
271  for(int i=0; i<100; ++i)
272  {
273  target_pos_pub.publish(target_pos_msg);
274  }
275 
276  // set mode as offboard
277  mavros_msgs::SetMode offb_set_mode;
278  offb_set_mode.request.custom_mode = "OFFBOARD";
279  while(ros::ok() and not(set_mode_client.call(offb_set_mode))){
280  ros::spinOnce();
281  rate.sleep();
282  }
283  ROS_INFO("Offboard enabled.");
284 
285  // Get interpolated path from two waypoints
286  std::vector<geometry_msgs::PoseStamped> bilinear_path;
287  bilinear_path = getBilinearPath(local_pos, target_pos_msg, 0.01);
288  ROS_INFO("Vehicle moving forward...");
289  // Publish all interpolated points
290  for(auto pose: bilinear_path)
291  {
292  // Publish same message till drone arrives to local goal
293  while(ros::ok() and not isGoalReached(local_pos, pose))
294  {
295  target_pos_pub.publish(pose);
296  ros::spinOnce();
297  rate.sleep();
298  }
299 
300  // interrupt path if ros node is in shutdown
301  if(not ros::ok())
302  {
303  break;
304  }
305  }
306  ROS_INFO("Vehicle arrived destination.");
307 
308  pc_gen_cmd.request.end = ros::Time::now();
309  ROS_INFO("End buffering laser scan data.");
310 
311  // land
312  mavros_msgs::CommandTOL landing_cmd;
313  landing_cmd.request.altitude = global_pos.altitude - takeoff_height + 0.5;
314  landing_cmd.request.longitude = global_pos.longitude;
315  landing_cmd.request.latitude = global_pos.latitude;
316 
317  while(ros::ok() and not(landing_client.call(landing_cmd)) and
318  landing_cmd.response.success){
319  ros::spinOnce();
320  rate.sleep();
321  }
322  ROS_INFO("Vehicle landing...");
323 
324  while(ros::ok() and local_pos.pose.position.z > 0.1){
325  ros::spinOnce();
326  rate.sleep();
327  }
328  ROS_INFO("Vehicle landed.");
329 
330  // disram
331  arm_cmd.request.value = false;
332  while(ros::ok() and !arming_client.call(arm_cmd) && arm_cmd.response.success){
333  }
334  ROS_INFO("Vehicle disarmed");
335 
336  if (pc_gen_client.call(pc_gen_cmd)){
337 
338  sensor_msgs::PointCloud2 ros_cloud = pc_gen_cmd.response.cloud;
339 
340  ROS_INFO("Got cloud with %lu points.", ros_cloud.data.size());
341  for(int i=0; i<10; ++i){
342  point_cloud_pub.publish(ros_cloud);
343  ros::spinOnce();
344  rate.sleep();
345  }
346 
347  // convert ROS pointcloud2 to PCL pointcloud
348  pcl::PointCloud<pcl::PointXYZ> pcl_cloud;
349  pcl::fromROSMsg(ros_cloud, pcl_cloud);
350 
351  // store pointcloud in $HOME
352  std::string home_dir = std::getenv("HOME");
353  try{
354  pcl::io::savePCDFileASCII(home_dir+"/sweep.pcd", pcl_cloud);
355  ROS_INFO("Pointcloud saved to \"$HOME/sweep.pcd\".");
356  }catch(const pcl::IOException& e){
357  ROS_ERROR("%s", e.what());
358  return 1;
359  }
360 
361  }else{
362  ROS_INFO("Service \"assemble_scan\" call failed.");
363  }
364 
365  return 0;
366 }
bool isGoalReached(const geometry_msgs::PoseStamped &curr_pose, const geometry_msgs::PoseStamped &goal_pose, double threshold=0.5)
mavros_msgs::State current_state
Definition: offb_node.cpp:8
int main(int argc, char **argv)
std::array< std::vector< double >, 2 > linearInterp(const std::array< double, 2 > &p1, const std::array< double, 2 > &p2, const double step)
Perform linear interpolation.
std::vector< geometry_msgs::PoseStamped > getBilinearPath(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal, const double step=0.05)
Return interpolated path using bilinear interpolation from two waypoints.