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