moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
servo_keyboard_input.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2023, PickNik LLC
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of PickNik LLC nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Title : servo_keyboard_input.cpp
36 * Project : moveit_servo
37 * Created : 05/31/2021
38 * Author : Adam Pettinger, V Mohammed Ibrahim
39 */
40
41#include <chrono>
42#include <control_msgs/msg/joint_jog.hpp>
43#include <cstdint>
44#include <geometry_msgs/msg/twist_stamped.hpp>
45#include <moveit_msgs/srv/servo_command_type.hpp>
46#include <rclcpp/executors/single_threaded_executor.hpp>
47#include <rclcpp/rclcpp.hpp>
48#include <signal.h>
49#include <stdio.h>
50#ifndef WIN32
51#include <termios.h>
52#include <unistd.h>
53#else
54#include <conio.h>
55#endif
56
57// Define used keys
58namespace
59{
60constexpr int8_t KEYCODE_RIGHT = 0x43;
61constexpr int8_t KEYCODE_LEFT = 0x44;
62constexpr int8_t KEYCODE_UP = 0x41;
63constexpr int8_t KEYCODE_DOWN = 0x42;
64constexpr int8_t KEYCODE_PERIOD = 0x2E;
65constexpr int8_t KEYCODE_SEMICOLON = 0x3B;
66constexpr int8_t KEYCODE_1 = 0x31;
67constexpr int8_t KEYCODE_2 = 0x32;
68constexpr int8_t KEYCODE_3 = 0x33;
69constexpr int8_t KEYCODE_4 = 0x34;
70constexpr int8_t KEYCODE_5 = 0x35;
71constexpr int8_t KEYCODE_6 = 0x36;
72constexpr int8_t KEYCODE_7 = 0x37;
73constexpr int8_t KEYCODE_Q = 0x71;
74constexpr int8_t KEYCODE_R = 0x72;
75constexpr int8_t KEYCODE_J = 0x6A;
76constexpr int8_t KEYCODE_T = 0x74;
77constexpr int8_t KEYCODE_W = 0x77;
78constexpr int8_t KEYCODE_E = 0x65;
79} // namespace
80
81// Some constants used in the Servo Teleop demo
82namespace
83{
84const std::string TWIST_TOPIC = "/servo_node/delta_twist_cmds";
85const std::string JOINT_TOPIC = "/servo_node/delta_joint_cmds";
86const size_t ROS_QUEUE_SIZE = 10;
87const std::string PLANNING_FRAME_ID = "panda_link0";
88const std::string EE_FRAME_ID = "panda_link8";
89} // namespace
90
91// A class for reading the key inputs from the terminal
93{
94public:
95 KeyboardReader() : file_descriptor_(0)
96 {
97#ifndef WIN32
98 // get the console in raw mode
99 tcgetattr(file_descriptor_, &cooked_);
100 struct termios raw;
101 memcpy(&raw, &cooked_, sizeof(struct termios));
102 raw.c_lflag &= ~(ICANON | ECHO);
103 // Setting a new line, then end of file
104 raw.c_cc[VEOL] = 1;
105 raw.c_cc[VEOF] = 2;
106 tcsetattr(file_descriptor_, TCSANOW, &raw);
107#endif
108 }
109 void readOne(char* c)
110 {
111#ifndef WIN32
112 int rc = read(file_descriptor_, c, 1);
113 if (rc < 0)
114 {
115 throw std::runtime_error("read failed");
116 }
117#else
118 *c = static_cast<char>(_getch());
119#endif
120 }
121 void shutdown()
122 {
123#ifndef WIN32
124 tcsetattr(file_descriptor_, TCSANOW, &cooked_);
125#endif
126 }
127
128private:
129 int file_descriptor_;
130#ifndef WIN32
131 struct termios cooked_;
132#endif
133};
134
135// Converts key-presses to Twist or Jog commands for Servo, in lieu of a controller
137{
138public:
140 int keyLoop();
141
142private:
143 void spin();
144
145 rclcpp::Node::SharedPtr nh_;
146
147 rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr twist_pub_;
148 rclcpp::Publisher<control_msgs::msg::JointJog>::SharedPtr joint_pub_;
149 rclcpp::Client<moveit_msgs::srv::ServoCommandType>::SharedPtr switch_input_;
150
151 std::shared_ptr<moveit_msgs::srv::ServoCommandType::Request> request_;
152 double joint_vel_cmd_;
153 std::string command_frame_id_;
154};
155
156KeyboardServo::KeyboardServo() : joint_vel_cmd_(1.0), command_frame_id_{ "panda_link0" }
157{
158 nh_ = rclcpp::Node::make_shared("servo_keyboard_input");
159
160 twist_pub_ = nh_->create_publisher<geometry_msgs::msg::TwistStamped>(TWIST_TOPIC, ROS_QUEUE_SIZE);
161 joint_pub_ = nh_->create_publisher<control_msgs::msg::JointJog>(JOINT_TOPIC, ROS_QUEUE_SIZE);
162
163 // Client for switching input types
164 switch_input_ = nh_->create_client<moveit_msgs::srv::ServoCommandType>("servo_node/switch_command_type");
165}
166
168
169void quit(int sig)
170{
171 (void)sig;
172 input.shutdown();
173 rclcpp::shutdown();
174 exit(0);
175}
176
177int main(int argc, char** argv)
178{
179 rclcpp::init(argc, argv);
180 KeyboardServo keyboard_servo;
181
182 signal(SIGINT, quit);
183
184 int rc = keyboard_servo.keyLoop();
185 input.shutdown();
186 rclcpp::shutdown();
187
188 return rc;
189}
190
191void KeyboardServo::spin()
192{
193 rclcpp::executors::SingleThreadedExecutor executor;
194 executor.add_node(nh_);
195 while (rclcpp::ok())
196 {
197 executor.spin_some();
198 }
199}
200
202{
203 char c;
204 bool publish_twist = false;
205 bool publish_joint = false;
206
207 std::thread{ [this]() { return spin(); } }.detach();
208
209 puts("Reading from keyboard");
210 puts("---------------------------");
211 puts("All commands are in the planning frame");
212 puts("Use arrow keys and the '.' and ';' keys to Cartesian jog");
213 puts("Use 1|2|3|4|5|6|7 keys to joint jog. 'r' to reverse the direction of jogging.");
214 puts("Use 'j' to select joint jog. ");
215 puts("Use 't' to select twist ");
216 puts("Use 'w' and 'e' to switch between sending command in planning frame or end effector frame");
217 puts("'Q' to quit.");
218
219 for (;;)
220 {
221 // get the next event from the keyboard
222 try
223 {
224 input.readOne(&c);
225 }
226 catch (const std::runtime_error&)
227 {
228 perror("read():");
229 return -1;
230 }
231
232 RCLCPP_DEBUG(nh_->get_logger(), "value: 0x%02X\n", c);
233
234 // // Create the messages we might publish
235 auto twist_msg = std::make_unique<geometry_msgs::msg::TwistStamped>();
236 auto joint_msg = std::make_unique<control_msgs::msg::JointJog>();
237
238 joint_msg->joint_names.resize(7);
239 joint_msg->joint_names = { "panda_joint1", "panda_joint2", "panda_joint3", "panda_joint4",
240 "panda_joint5", "panda_joint6", "panda_joint7" };
241
242 joint_msg->velocities.resize(7);
243 std::fill(joint_msg->velocities.begin(), joint_msg->velocities.end(), 0.0);
244 // Use read key-press
245 switch (c)
246 {
247 case KEYCODE_LEFT:
248 RCLCPP_DEBUG(nh_->get_logger(), "LEFT");
249 twist_msg->twist.linear.y = -0.5;
250 publish_twist = true;
251 break;
252 case KEYCODE_RIGHT:
253 RCLCPP_DEBUG(nh_->get_logger(), "RIGHT");
254 twist_msg->twist.linear.y = 0.5;
255 publish_twist = true;
256 break;
257 case KEYCODE_UP:
258 RCLCPP_DEBUG(nh_->get_logger(), "UP");
259 twist_msg->twist.linear.x = 0.5;
260 publish_twist = true;
261 break;
262 case KEYCODE_DOWN:
263 RCLCPP_DEBUG(nh_->get_logger(), "DOWN");
264 twist_msg->twist.linear.x = -0.5;
265 publish_twist = true;
266 break;
267 case KEYCODE_PERIOD:
268 RCLCPP_DEBUG(nh_->get_logger(), "PERIOD");
269 twist_msg->twist.linear.z = -0.5;
270 publish_twist = true;
271 break;
272 case KEYCODE_SEMICOLON:
273 RCLCPP_DEBUG(nh_->get_logger(), "SEMICOLON");
274 twist_msg->twist.linear.z = 0.5;
275 publish_twist = true;
276 break;
277 case KEYCODE_1:
278 RCLCPP_DEBUG(nh_->get_logger(), "1");
279 joint_msg->velocities[0] = joint_vel_cmd_;
280 publish_joint = true;
281 break;
282 case KEYCODE_2:
283 RCLCPP_DEBUG(nh_->get_logger(), "2");
284 joint_msg->velocities[1] = joint_vel_cmd_;
285 publish_joint = true;
286 break;
287 case KEYCODE_3:
288 RCLCPP_DEBUG(nh_->get_logger(), "3");
289 joint_msg->velocities[2] = joint_vel_cmd_;
290 publish_joint = true;
291 break;
292 case KEYCODE_4:
293 RCLCPP_DEBUG(nh_->get_logger(), "4");
294 joint_msg->velocities[3] = joint_vel_cmd_;
295 publish_joint = true;
296 break;
297 case KEYCODE_5:
298 RCLCPP_DEBUG(nh_->get_logger(), "5");
299 joint_msg->velocities[4] = joint_vel_cmd_;
300 publish_joint = true;
301 break;
302 case KEYCODE_6:
303 RCLCPP_DEBUG(nh_->get_logger(), "6");
304 joint_msg->velocities[5] = joint_vel_cmd_;
305 publish_joint = true;
306 break;
307 case KEYCODE_7:
308 RCLCPP_DEBUG(nh_->get_logger(), "7");
309 joint_msg->velocities[6] = joint_vel_cmd_;
310 publish_joint = true;
311 break;
312 case KEYCODE_R:
313 RCLCPP_DEBUG(nh_->get_logger(), "r");
314 joint_vel_cmd_ *= -1;
315 break;
316 case KEYCODE_J:
317 RCLCPP_DEBUG(nh_->get_logger(), "j");
318 request_ = std::make_shared<moveit_msgs::srv::ServoCommandType::Request>();
319 request_->command_type = moveit_msgs::srv::ServoCommandType::Request::JOINT_JOG;
320 if (switch_input_->wait_for_service(std::chrono::seconds(1)))
321 {
322 auto result = switch_input_->async_send_request(request_);
323 if (result.get()->success)
324 {
325 RCLCPP_INFO_STREAM(nh_->get_logger(), "Switched to input type: JointJog");
326 }
327 else
328 {
329 RCLCPP_WARN_STREAM(nh_->get_logger(), "Could not switch input to: JointJog");
330 }
331 }
332 break;
333 case KEYCODE_T:
334 RCLCPP_DEBUG(nh_->get_logger(), "t");
335 request_ = std::make_shared<moveit_msgs::srv::ServoCommandType::Request>();
336 request_->command_type = moveit_msgs::srv::ServoCommandType::Request::TWIST;
337 if (switch_input_->wait_for_service(std::chrono::seconds(1)))
338 {
339 auto result = switch_input_->async_send_request(request_);
340 if (result.get()->success)
341 {
342 RCLCPP_INFO_STREAM(nh_->get_logger(), "Switched to input type: Twist");
343 }
344 else
345 {
346 RCLCPP_WARN_STREAM(nh_->get_logger(), "Could not switch input to: Twist");
347 }
348 }
349 break;
350 case KEYCODE_W:
351 RCLCPP_DEBUG(nh_->get_logger(), "w");
352 RCLCPP_INFO_STREAM(nh_->get_logger(), "Command frame set to: " << PLANNING_FRAME_ID);
353 command_frame_id_ = PLANNING_FRAME_ID;
354 break;
355 case KEYCODE_E:
356 RCLCPP_DEBUG(nh_->get_logger(), "e");
357 RCLCPP_INFO_STREAM(nh_->get_logger(), "Command frame set to: " << EE_FRAME_ID);
358 command_frame_id_ = EE_FRAME_ID;
359 break;
360 case KEYCODE_Q:
361 RCLCPP_DEBUG(nh_->get_logger(), "quit");
362 return 0;
363 }
364
365 // If a key requiring a publish was pressed, publish the message now
366 if (publish_twist)
367 {
368 twist_msg->header.stamp = nh_->now();
369 twist_msg->header.frame_id = command_frame_id_;
370 twist_pub_->publish(std::move(twist_msg));
371 publish_twist = false;
372 }
373 else if (publish_joint)
374 {
375 joint_msg->header.stamp = nh_->now();
376 joint_msg->header.frame_id = PLANNING_FRAME_ID;
377 joint_pub_->publish(std::move(joint_msg));
378 publish_joint = false;
379 }
380 }
381
382 return 0;
383}
int main(int argc, char **argv)
KeyboardReader input
void quit(int sig)