PX4 Teleop
px4_teleop_joy.cpp
Go to the documentation of this file.
1 
7 // C++ libraries
8 #include <cmath>
9 #include <stdexcept>
10 #include <string>
11 #include <vector>
12 
13 // roscpp
14 #include <ros/package.h>
15 #include <ros/ros.h>
16 
17 // sensor_msgs
18 #include <sensor_msgs/Joy.h>
19 
20 // geometry_msgs
21 #include <geometry_msgs/TwistStamped.h>
22 #include <geometry_msgs/Vector3.h>
23 
24 // mavros_msgs
25 #include <mavros_msgs/CommandBool.h>
26 #include <mavros_msgs/CommandHome.h>
27 #include <mavros_msgs/CommandTOL.h>
28 #include <mavros_msgs/SetMode.h>
29 #include <mavros_msgs/State.h>
30 
31 // header from px4_teleop_cmds
32 #include <px4_teleop_cmds.hpp>
33 
34 // yaml-cpp
35 #include <yaml-cpp/yaml.h>
36 
38 const int RC_MODE_ONE = 1;
40 const int RC_MODE_TWO = 2;
41 
43 const std::string px4_teleop_path = ros::package::getPath("px4_teleop");
44 
46 mavros_msgs::State current_state;
47 
52 void state_cb(const mavros_msgs::State::ConstPtr& msg)
53 {
54  current_state = *msg;
55 }
56 
58 sensor_msgs::NavSatFix curr_gpos;
59 
64 void curr_gpos_cb(const sensor_msgs::NavSatFix::ConstPtr& msg)
65 {
66  curr_gpos = *msg;
67 }
68 
70 geometry_msgs::PoseStamped local_pos;
71 
76 void local_pos_cb(const geometry_msgs::PoseStamped::ConstPtr& msg)
77 {
78  local_pos = *msg;
79 }
80 
82 sensor_msgs::Joy joy_msg;
87 void joy_cb(const sensor_msgs::Joy::ConstPtr& msg)
88 {
89  joy_msg = *msg;
90 }
91 
92 int main(int argc, char** argv)
93 {
94  ros::init(argc, argv, "px4_teleop_joy");
95  ros::NodeHandle nh("~");
96 
97  ros::Rate rate(20);
98 
99  // Publisher
100  ros::Publisher cmd_vel_pub = nh.advertise<geometry_msgs::TwistStamped>("/mavros/setpoint_velocity/cmd_vel", 100);
101 
102  // Subscriber
103  ros::Subscriber joy_sub = nh.subscribe<sensor_msgs::Joy>("/joy", 100, joy_cb);
104  ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>("/mavros/state", 100, state_cb);
105  ros::Subscriber curr_gpos_sub =
106  nh.subscribe<sensor_msgs::NavSatFix>("/mavros/global_position/pose", 10, curr_gpos_cb);
107  ros::Subscriber local_pos_sub =
108  nh.subscribe<geometry_msgs::PoseStamped>("/mavros/local_position/pose", 100, local_pos_cb);
109 
110  // Survice Client
111  ros::ServiceClient set_hp_client = nh.serviceClient<mavros_msgs::CommandHome>("/mavros/cmd/set_home");
112  ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>("/mavros/cmd/arming");
113  ros::ServiceClient takeoff_client = nh.serviceClient<mavros_msgs::CommandTOL>("/mavros/cmd/takeoff");
114  ros::ServiceClient landing_client = nh.serviceClient<mavros_msgs::CommandTOL>("/mavros/cmd/land");
115  ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>("/mavros/set_mode");
116 
117  // Config file path
118  std::string joy_config_path;
119  nh.param<std::string>("joy_config_path", joy_config_path, px4_teleop_path + "/config/f710.yaml");
120 
121  // Binding file path
122  std::string joy_binding_path;
123  nh.param<std::string>("joy_binding_path", joy_binding_path, px4_teleop_path + "/config/f310_binding.yaml");
124 
125  // Rc mode
126  int joy_rc_mode;
127  nh.param<int>("joy_rc_mode", joy_rc_mode, 1);
128 
129  // Takeoff height
130  float takeoff_height;
131  nh.param<float>("takeoff_height", takeoff_height, 2.0);
132 
133  // MAV_FRAME
134  std::string mav_frame;
135  nh.param<std::string>("/mavros/setpoint_velocity/mav_frame", mav_frame, "LOCAL_NED");
136 
137  ROS_INFO("RC Mode: %d", joy_rc_mode);
138  ROS_INFO("Config: %s", joy_config_path.c_str());
139  ROS_INFO("MAV_FRAME: %s", mav_frame.c_str());
140 
141  // Read config file
142  YAML::Node config;
143  try
144  {
145  config = YAML::LoadFile(joy_config_path);
146  }
147  catch (YAML::Exception& e)
148  {
149  ROS_ERROR("Failed to read config file");
150  return -1;
151  }
152 
153  // Read config file
154  YAML::Node binding;
155  try
156  {
157  binding = YAML::LoadFile(joy_binding_path);
158  }
159  catch (YAML::Exception& e)
160  {
161  ROS_ERROR("Failed to read binding file");
162  return -1;
163  }
164 
165  // Read setting
166  switch (joy_rc_mode)
167  {
168  case RC_MODE_ONE:
169  config = config["mode1"];
170  break;
171  case RC_MODE_TWO:
172  config = config["mode2"];
173  break;
174  }
175 
176  // Wait for connection
177  while (ros::ok() and current_state.connected)
178  {
179  ros::spinOnce();
180  rate.sleep();
181  }
182 
183  // Wait for /mavros/global_position/global
184  ROS_INFO("Waiting for message from /mavros/global_position/global");
185  const std::string topic = "/mavros/global_position/global";
186  sensor_msgs::NavSatFix init_gpos = *ros::topic::waitForMessage<sensor_msgs::NavSatFix>(topic);
187  double init_latitude = init_gpos.latitude;
188  double init_longitude = init_gpos.longitude;
189  double init_altitude = init_gpos.altitude;
190  ROS_INFO("Initial Lat:%f Lon:%f Alt:%f", init_latitude, init_longitude, init_altitude);
191 
192  // set home position as current position
193  mavros_msgs::CommandHome set_hp_cmd;
194  set_hp_cmd.request.current_gps = true;
195  while (not(set_hp_client.call(set_hp_cmd)) and set_hp_cmd.response.success)
196  {
197  ros::spinOnce();
198  rate.sleep();
199  }
200  ROS_INFO("HP set.");
201 
202  // set mode as offboard
203  mavros_msgs::SetMode offb_set_mode;
204  offb_set_mode.request.custom_mode = "OFFBOARD";
205 
206  while (not(set_mode_client.call(offb_set_mode)))
207  {
208  ros::spinOnce();
209  rate.sleep();
210  }
211  ROS_INFO("Offboard enabled.");
212 
213  ros::Time last_request = ros::Time::now();
214  ros::Time button_last_event = ros::Time::now();
215 
216  // print button binding
217  printf("\n\n");
218  ROS_INFO("===== BUTTON BINDING =====");
219  ROS_INFO("ARM \t: %s",
220  binding["buttons"][config["button_map"]["arm"].as<std::string>()].as<std::string>().c_str());
221  ROS_INFO("TAKEOFF \t: %s",
222  binding["buttons"][config["button_map"]["takeoff"].as<std::string>()].as<std::string>().c_str());
223  ROS_INFO("LAND \t: %s",
224  binding["buttons"][config["button_map"]["land"].as<std::string>()].as<std::string>().c_str());
225  ROS_INFO("DISARM \t: %s",
226  binding["buttons"][config["button_map"]["disarm"].as<std::string>()].as<std::string>().c_str());
227  ROS_INFO("DEADMAN \t: %s",
228  binding["buttons"][config["button_map"]["deadman"].as<std::string>()].as<std::string>().c_str());
229 
230  // print axes binding
231  ROS_INFO("===== AXES BINDING =====");
232  ROS_INFO("PITCH \t: %s", binding["axes"][config["axes_map"]["pitch"].as<std::string>()].as<std::string>().c_str());
233  ROS_INFO("YAW \t: %s", binding["axes"][config["axes_map"]["yaw"].as<std::string>()].as<std::string>().c_str());
234  ROS_INFO("ROLL \t: %s", binding["axes"][config["axes_map"]["roll"].as<std::string>()].as<std::string>().c_str());
235  ROS_INFO("THROTTLE\t: %s",
236  binding["axes"][config["axes_map"]["throttle"].as<std::string>()].as<std::string>().c_str());
237 
238  // print axes scaling
239  ROS_INFO("===== AXES SCALING =====");
240  ROS_INFO("PITCH \t: %f", config["axes_scale"]["pitch"].as<double>());
241  ROS_INFO("YAW \t: %f", config["axes_scale"]["yaw"].as<double>());
242  ROS_INFO("ROLL \t: %f", config["axes_scale"]["roll"].as<double>());
243  ROS_INFO("THROTTLE\t: %f", config["axes_scale"]["throttle"].as<double>());
244  printf("\n\n");
245 
246  // Initialize variables related to joy
247  std::vector<float> joy_axes;
248  std::vector<int32_t> joy_button;
249 
250  while (ros::ok())
251  {
252  ros::spinOnce();
253  rate.sleep();
254 
255  joy_axes = joy_msg.axes;
256  joy_button = joy_msg.buttons;
257 
258  if (current_state.mode != "OFFBOARD" and (ros::Time::now() - last_request > ros::Duration(0.1)))
259  {
260  if ((set_mode_client.call(offb_set_mode)))
261  {
262  ROS_DEBUG("Offboard enabled.");
263  }
264  last_request = ros::Time::now();
265  }
266 
267  if(joy_axes.size()==0 or joy_button.size()==0)
268  {
269  continue;
270  }
271 
272  geometry_msgs::TwistStamped cmd_vel_msg;
273  try
274  {
275 
276  double lin_x = config["axes_scale"]["pitch"].as<double>() * joy_axes.at(config["axes_map"]["pitch"].as<int>());
277  double lin_y = config["axes_scale"]["roll"].as<double>() * joy_axes.at(config["axes_map"]["roll"].as<int>());
278  double lin_z =
279  config["axes_scale"]["throttle"].as<double>() * joy_axes.at(config["axes_map"]["throttle"].as<int>());
280  double ang_z = config["axes_scale"]["yaw"].as<double>() * joy_axes.at(config["axes_map"]["yaw"].as<int>());
281 
282  cmd_vel_msg.twist.linear.x = lin_x;
283  cmd_vel_msg.twist.linear.y = lin_y;
284  cmd_vel_msg.twist.linear.z = lin_z;
285  cmd_vel_msg.twist.angular.z = ang_z;
286 
287  cmd_vel_pub.publish(cmd_vel_msg);
288 
289  if (ros::Time::now() - button_last_event > ros::Duration(0.1))
290  {
291  if (joy_button.at(config["button_map"]["deadman"].as<int>()))
292  {
293  if (joy_button.at(config["button_map"]["arm"].as<int>()))
294  {
295  arm(arming_client, current_state);
296  }
297  else if (joy_button.at(config["button_map"]["disarm"].as<int>()))
298  {
299  disarm(arming_client);
300  }
301  else if (joy_button.at(config["button_map"]["takeoff"].as<int>()))
302  {
303  takeoff(takeoff_client, current_state, local_pos, init_gpos, takeoff_height);
304  }
305  else if (joy_button.at(config["button_map"]["land"].as<int>()))
306  {
307  land(landing_client, local_pos, curr_gpos, init_gpos);
308  }
309  else if (joy_button.at(config["button_map"]["exit"].as<int>()))
310  {
311  break;
312  }
313  button_last_event = ros::Time::now();
314  }
315  }
316  }
317  catch (std::out_of_range& e)
318  {
319  ROS_ERROR("%s", e.what());
320  continue;
321  }
322  }
323 
324  ROS_INFO("Exitting px4_teleop.");
325  land(landing_client, local_pos, curr_gpos, init_gpos);
326 
327  return 0;
328 }
void local_pos_cb(const geometry_msgs::PoseStamped::ConstPtr &msg)
Callback function for local position subscriber.
void disarm(ros::ServiceClient &client)
Disarm vehicle.
void takeoff(ros::ServiceClient &client, mavros_msgs::State &state, geometry_msgs::PoseStamped &lpos, sensor_msgs::NavSatFix &hp, double height)
Take off vehicle.
void land(ros::ServiceClient &client, geometry_msgs::PoseStamped &lpos, sensor_msgs::NavSatFix &gpos, sensor_msgs::NavSatFix &hp)
Land vehicle.
mavros_msgs::State current_state
Storage for vehicle state.
int main(int argc, char **argv)
void state_cb(const mavros_msgs::State::ConstPtr &msg)
Callback function for state subscriber.
const int RC_MODE_TWO
Rc mode constant.
sensor_msgs::NavSatFix curr_gpos
Storage for current global position.
const int RC_MODE_ONE
Rc mode constant.
void arm(ros::ServiceClient &client, mavros_msgs::State &state)
Arm vehicle.
geometry_msgs::PoseStamped local_pos
Storage for local position.
void joy_cb(const sensor_msgs::Joy::ConstPtr &msg)
Callback function for joystick input subscriber.
sensor_msgs::Joy joy_msg
Storage for joystick input.
const std::string px4_teleop_path
Storage for path of this package.
void curr_gpos_cb(const sensor_msgs::NavSatFix::ConstPtr &msg)
Callback function for global position subscriber.
Command functions for px4_teleop package.