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 <geometry_msgs/msg/twist_stamped.hpp>
44#include <moveit_msgs/srv/servo_command_type.hpp>
45#include <rclcpp/rclcpp.hpp>
46#include <signal.h>
47#include <stdio.h>
48#ifndef WIN32
49#include <termios.h>
50#include <unistd.h>
51#else
52#include <conio.h>
53#endif
54
55// Define used keys
56namespace
57{
58constexpr int8_t KEYCODE_RIGHT = 0x43;
59constexpr int8_t KEYCODE_LEFT = 0x44;
60constexpr int8_t KEYCODE_UP = 0x41;
61constexpr int8_t KEYCODE_DOWN = 0x42;
62constexpr int8_t KEYCODE_PERIOD = 0x2E;
63constexpr int8_t KEYCODE_SEMICOLON = 0x3B;
64constexpr int8_t KEYCODE_1 = 0x31;
65constexpr int8_t KEYCODE_2 = 0x32;
66constexpr int8_t KEYCODE_3 = 0x33;
67constexpr int8_t KEYCODE_4 = 0x34;
68constexpr int8_t KEYCODE_5 = 0x35;
69constexpr int8_t KEYCODE_6 = 0x36;
70constexpr int8_t KEYCODE_7 = 0x37;
71constexpr int8_t KEYCODE_Q = 0x71;
72constexpr int8_t KEYCODE_R = 0x72;
73constexpr int8_t KEYCODE_J = 0x6A;
74constexpr int8_t KEYCODE_T = 0x74;
75constexpr int8_t KEYCODE_W = 0x77;
76constexpr int8_t KEYCODE_E = 0x65;
77} // namespace
78
79// Some constants used in the Servo Teleop demo
80namespace
81{
82const std::string TWIST_TOPIC = "/servo_node/delta_twist_cmds";
83const std::string JOINT_TOPIC = "/servo_node/delta_joint_cmds";
84const size_t ROS_QUEUE_SIZE = 10;
85const std::string PLANNING_FRAME_ID = "panda_link0";
86const std::string EE_FRAME_ID = "panda_link8";
87} // namespace
88
89// A class for reading the key inputs from the terminal
91{
92public:
93 KeyboardReader() : file_descriptor_(0)
94 {
95#ifndef WIN32
96 // get the console in raw mode
97 tcgetattr(file_descriptor_, &cooked_);
98 struct termios raw;
99 memcpy(&raw, &cooked_, sizeof(struct termios));
100 raw.c_lflag &= ~(ICANON | ECHO);
101 // Setting a new line, then end of file
102 raw.c_cc[VEOL] = 1;
103 raw.c_cc[VEOF] = 2;
104 tcsetattr(file_descriptor_, TCSANOW, &raw);
105#endif
106 }
107 void readOne(char* c)
108 {
109#ifndef WIN32
110 int rc = read(file_descriptor_, c, 1);
111 if (rc < 0)
112 {
113 throw std::runtime_error("read failed");
114 }
115#else
116 *c = static_cast<char>(_getch());
117#endif
118 }
119 void shutdown()
120 {
121#ifndef WIN32
122 tcsetattr(file_descriptor_, TCSANOW, &cooked_);
123#endif
124 }
125
126private:
127 int file_descriptor_;
128#ifndef WIN32
129 struct termios cooked_;
130#endif
131};
132
133// Converts key-presses to Twist or Jog commands for Servo, in lieu of a controller
135{
136public:
138 int keyLoop();
139
140private:
141 void spin();
142
143 rclcpp::Node::SharedPtr nh_;
144
145 rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr twist_pub_;
146 rclcpp::Publisher<control_msgs::msg::JointJog>::SharedPtr joint_pub_;
147 rclcpp::Client<moveit_msgs::srv::ServoCommandType>::SharedPtr switch_input_;
148
149 std::shared_ptr<moveit_msgs::srv::ServoCommandType::Request> request_;
150 double joint_vel_cmd_;
151 std::string command_frame_id_;
152};
153
154KeyboardServo::KeyboardServo() : joint_vel_cmd_(1.0), command_frame_id_{ "panda_link0" }
155{
156 nh_ = rclcpp::Node::make_shared("servo_keyboard_input");
157
158 twist_pub_ = nh_->create_publisher<geometry_msgs::msg::TwistStamped>(TWIST_TOPIC, ROS_QUEUE_SIZE);
159 joint_pub_ = nh_->create_publisher<control_msgs::msg::JointJog>(JOINT_TOPIC, ROS_QUEUE_SIZE);
160
161 // Client for switching input types
162 switch_input_ = nh_->create_client<moveit_msgs::srv::ServoCommandType>("servo_node/switch_command_type");
163}
164
166
167void quit(int sig)
168{
169 (void)sig;
170 input.shutdown();
171 rclcpp::shutdown();
172 exit(0);
173}
174
175int main(int argc, char** argv)
176{
177 rclcpp::init(argc, argv);
178 KeyboardServo keyboard_servo;
179
180 signal(SIGINT, quit);
181
182 int rc = keyboard_servo.keyLoop();
183 input.shutdown();
184 rclcpp::shutdown();
185
186 return rc;
187}
188
189void KeyboardServo::spin()
190{
191 while (rclcpp::ok())
192 {
193 rclcpp::spin_some(nh_);
194 }
195}
196
198{
199 char c;
200 bool publish_twist = false;
201 bool publish_joint = false;
202
203 std::thread{ [this]() { return spin(); } }.detach();
204
205 puts("Reading from keyboard");
206 puts("---------------------------");
207 puts("All commands are in the planning frame");
208 puts("Use arrow keys and the '.' and ';' keys to Cartesian jog");
209 puts("Use 1|2|3|4|5|6|7 keys to joint jog. 'r' to reverse the direction of jogging.");
210 puts("Use 'j' to select joint jog. ");
211 puts("Use 't' to select twist ");
212 puts("Use 'w' and 'e' to switch between sending command in planning frame or end effector frame");
213 puts("'Q' to quit.");
214
215 for (;;)
216 {
217 // get the next event from the keyboard
218 try
219 {
220 input.readOne(&c);
221 }
222 catch (const std::runtime_error&)
223 {
224 perror("read():");
225 return -1;
226 }
227
228 RCLCPP_DEBUG(nh_->get_logger(), "value: 0x%02X\n", c);
229
230 // // Create the messages we might publish
231 auto twist_msg = std::make_unique<geometry_msgs::msg::TwistStamped>();
232 auto joint_msg = std::make_unique<control_msgs::msg::JointJog>();
233
234 joint_msg->joint_names.resize(7);
235 joint_msg->joint_names = { "panda_joint1", "panda_joint2", "panda_joint3", "panda_joint4",
236 "panda_joint5", "panda_joint6", "panda_joint7" };
237
238 joint_msg->velocities.resize(7);
239 std::fill(joint_msg->velocities.begin(), joint_msg->velocities.end(), 0.0);
240 // Use read key-press
241 switch (c)
242 {
243 case KEYCODE_LEFT:
244 RCLCPP_DEBUG(nh_->get_logger(), "LEFT");
245 twist_msg->twist.linear.y = -0.5;
246 publish_twist = true;
247 break;
248 case KEYCODE_RIGHT:
249 RCLCPP_DEBUG(nh_->get_logger(), "RIGHT");
250 twist_msg->twist.linear.y = 0.5;
251 publish_twist = true;
252 break;
253 case KEYCODE_UP:
254 RCLCPP_DEBUG(nh_->get_logger(), "UP");
255 twist_msg->twist.linear.x = 0.5;
256 publish_twist = true;
257 break;
258 case KEYCODE_DOWN:
259 RCLCPP_DEBUG(nh_->get_logger(), "DOWN");
260 twist_msg->twist.linear.x = -0.5;
261 publish_twist = true;
262 break;
263 case KEYCODE_PERIOD:
264 RCLCPP_DEBUG(nh_->get_logger(), "PERIOD");
265 twist_msg->twist.linear.z = -0.5;
266 publish_twist = true;
267 break;
268 case KEYCODE_SEMICOLON:
269 RCLCPP_DEBUG(nh_->get_logger(), "SEMICOLON");
270 twist_msg->twist.linear.z = 0.5;
271 publish_twist = true;
272 break;
273 case KEYCODE_1:
274 RCLCPP_DEBUG(nh_->get_logger(), "1");
275 joint_msg->velocities[0] = joint_vel_cmd_;
276 publish_joint = true;
277 break;
278 case KEYCODE_2:
279 RCLCPP_DEBUG(nh_->get_logger(), "2");
280 joint_msg->velocities[1] = joint_vel_cmd_;
281 publish_joint = true;
282 break;
283 case KEYCODE_3:
284 RCLCPP_DEBUG(nh_->get_logger(), "3");
285 joint_msg->velocities[2] = joint_vel_cmd_;
286 publish_joint = true;
287 break;
288 case KEYCODE_4:
289 RCLCPP_DEBUG(nh_->get_logger(), "4");
290 joint_msg->velocities[3] = joint_vel_cmd_;
291 publish_joint = true;
292 break;
293 case KEYCODE_5:
294 RCLCPP_DEBUG(nh_->get_logger(), "5");
295 joint_msg->velocities[4] = joint_vel_cmd_;
296 publish_joint = true;
297 break;
298 case KEYCODE_6:
299 RCLCPP_DEBUG(nh_->get_logger(), "6");
300 joint_msg->velocities[5] = joint_vel_cmd_;
301 publish_joint = true;
302 break;
303 case KEYCODE_7:
304 RCLCPP_DEBUG(nh_->get_logger(), "7");
305 joint_msg->velocities[6] = joint_vel_cmd_;
306 publish_joint = true;
307 break;
308 case KEYCODE_R:
309 RCLCPP_DEBUG(nh_->get_logger(), "r");
310 joint_vel_cmd_ *= -1;
311 break;
312 case KEYCODE_J:
313 RCLCPP_DEBUG(nh_->get_logger(), "j");
314 request_ = std::make_shared<moveit_msgs::srv::ServoCommandType::Request>();
315 request_->command_type = moveit_msgs::srv::ServoCommandType::Request::JOINT_JOG;
316 if (switch_input_->wait_for_service(std::chrono::seconds(1)))
317 {
318 auto result = switch_input_->async_send_request(request_);
319 if (result.get()->success)
320 {
321 RCLCPP_INFO_STREAM(nh_->get_logger(), "Switched to input type: JointJog");
322 }
323 else
324 {
325 RCLCPP_WARN_STREAM(nh_->get_logger(), "Could not switch input to: JointJog");
326 }
327 }
328 break;
329 case KEYCODE_T:
330 RCLCPP_DEBUG(nh_->get_logger(), "t");
331 request_ = std::make_shared<moveit_msgs::srv::ServoCommandType::Request>();
332 request_->command_type = moveit_msgs::srv::ServoCommandType::Request::TWIST;
333 if (switch_input_->wait_for_service(std::chrono::seconds(1)))
334 {
335 auto result = switch_input_->async_send_request(request_);
336 if (result.get()->success)
337 {
338 RCLCPP_INFO_STREAM(nh_->get_logger(), "Switched to input type: Twist");
339 }
340 else
341 {
342 RCLCPP_WARN_STREAM(nh_->get_logger(), "Could not switch input to: Twist");
343 }
344 }
345 break;
346 case KEYCODE_W:
347 RCLCPP_DEBUG(nh_->get_logger(), "w");
348 RCLCPP_INFO_STREAM(nh_->get_logger(), "Command frame set to: " << PLANNING_FRAME_ID);
349 command_frame_id_ = PLANNING_FRAME_ID;
350 break;
351 case KEYCODE_E:
352 RCLCPP_DEBUG(nh_->get_logger(), "e");
353 RCLCPP_INFO_STREAM(nh_->get_logger(), "Command frame set to: " << EE_FRAME_ID);
354 command_frame_id_ = EE_FRAME_ID;
355 break;
356 case KEYCODE_Q:
357 RCLCPP_DEBUG(nh_->get_logger(), "quit");
358 return 0;
359 }
360
361 // If a key requiring a publish was pressed, publish the message now
362 if (publish_twist)
363 {
364 twist_msg->header.stamp = nh_->now();
365 twist_msg->header.frame_id = command_frame_id_;
366 twist_pub_->publish(std::move(twist_msg));
367 publish_twist = false;
368 }
369 else if (publish_joint)
370 {
371 joint_msg->header.stamp = nh_->now();
372 joint_msg->header.frame_id = PLANNING_FRAME_ID;
373 joint_pub_->publish(std::move(joint_msg));
374 publish_joint = false;
375 }
376 }
377
378 return 0;
379}
int main(int argc, char **argv)
KeyboardReader input
void quit(int sig)