root / joy / joy_node.cpp @ master
History | View | Annotate | Download (11.3 KB)
1 | ba6306a1 | Priya | /*
|
---|---|---|---|
2 | * teleop_pr2
|
||
3 | * Copyright (c) 2009, Willow Garage, Inc.
|
||
4 | * All rights reserved.
|
||
5 | *
|
||
6 | * Redistribution and use in source and binary forms, with or without
|
||
7 | * modification, are permitted provided that the following conditions are met:
|
||
8 | *
|
||
9 | * * Redistributions of source code must retain the above copyright
|
||
10 | * notice, this list of conditions and the following disclaimer.
|
||
11 | * * Redistributions in binary form must reproduce the above copyright
|
||
12 | * notice, this list of conditions and the following disclaimer in the
|
||
13 | * documentation and/or other materials provided with the distribution.
|
||
14 | * * Neither the name of the <ORGANIZATION> nor the names of its
|
||
15 | * contributors may be used to endorse or promote products derived from
|
||
16 | * this software without specific prior written permission.
|
||
17 | *
|
||
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||
19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||
22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
28 | * POSSIBILITY OF SUCH DAMAGE.
|
||
29 | */
|
||
30 | |||
31 | //\author: Blaise Gassend
|
||
32 | |||
33 | #include <unistd.h> |
||
34 | #include <math.h> |
||
35 | #include <linux/joystick.h> |
||
36 | #include <fcntl.h> |
||
37 | #include <diagnostic_updater/diagnostic_updater.h> |
||
38 | #include "ros/ros.h" |
||
39 | #include <sensor_msgs/Joy.h> |
||
40 | |||
41 | |||
42 | ///\brief Opens, reads from and publishes joystick events
|
||
43 | class Joystick |
||
44 | { |
||
45 | private:
|
||
46 | ros::NodeHandle nh_; |
||
47 | bool open_;
|
||
48 | std::string joy_dev_;
|
||
49 | double deadzone_;
|
||
50 | double autorepeat_rate_; // in Hz. 0 for no repeat. |
||
51 | double coalesce_interval_; // Defaults to 100 Hz rate limit. |
||
52 | int event_count_;
|
||
53 | int pub_count_;
|
||
54 | ros::Publisher pub_; |
||
55 | double lastDiagTime_;
|
||
56 | |||
57 | diagnostic_updater::Updater diagnostic_; |
||
58 | |||
59 | ///\brief Publishes diagnostics and status
|
||
60 | void diagnostics(diagnostic_updater::DiagnosticStatusWrapper& stat)
|
||
61 | { |
||
62 | double now = ros::Time::now().toSec();
|
||
63 | double interval = now - lastDiagTime_;
|
||
64 | if (open_)
|
||
65 | stat.summary(0, "OK"); |
||
66 | else
|
||
67 | stat.summary(2, "Joystick not open."); |
||
68 | |||
69 | stat.add("topic", pub_.getTopic());
|
||
70 | stat.add("device", joy_dev_);
|
||
71 | stat.add("dead zone", deadzone_);
|
||
72 | stat.add("autorepeat rate (Hz)", autorepeat_rate_);
|
||
73 | stat.add("coalesce interval (s)", coalesce_interval_);
|
||
74 | stat.add("recent joystick event rate (Hz)", event_count_ / interval);
|
||
75 | stat.add("recent publication rate (Hz)", pub_count_ / interval);
|
||
76 | stat.add("subscribers", pub_.getNumSubscribers());
|
||
77 | event_count_ = 0;
|
||
78 | pub_count_ = 0;
|
||
79 | lastDiagTime_ = now; |
||
80 | } |
||
81 | |||
82 | public:
|
||
83 | Joystick() : nh_(), diagnostic_() |
||
84 | {} |
||
85 | |||
86 | ///\brief Opens joystick port, reads from port and publishes while node is active
|
||
87 | int main(int argc, char **argv) |
||
88 | { |
||
89 | diagnostic_.add("Joystick Driver Status", this, &Joystick::diagnostics); |
||
90 | diagnostic_.setHardwareID("none");
|
||
91 | |||
92 | // Parameters
|
||
93 | ros::NodeHandle nh_param("~");
|
||
94 | pub_ = nh_.advertise<sensor_msgs::Joy>("joy", 1); |
||
95 | nh_param.param<std::string>("dev", joy_dev_, "/dev/input/js0"); |
||
96 | nh_param.param<double>("deadzone", deadzone_, 0.05); |
||
97 | nh_param.param<double>("autorepeat_rate", autorepeat_rate_, 0); |
||
98 | nh_param.param<double>("coalesce_interval", coalesce_interval_, 0.001); |
||
99 | |||
100 | // Checks on parameters
|
||
101 | if (autorepeat_rate_ > 1 / coalesce_interval_) |
||
102 | ROS_WARN("joy_node: autorepeat_rate (%f Hz) > 1/coalesce_interval (%f Hz) does not make sense. Timing behavior is not well defined.", autorepeat_rate_, 1/coalesce_interval_); |
||
103 | |||
104 | if (deadzone_ >= 1) |
||
105 | { |
||
106 | ROS_WARN("joy_node: deadzone greater than 1 was requested. The semantics of deadzone have changed. It is now related to the range [-1:1] instead of [-32767:32767]. For now I am dividing your deadzone by 32767, but this behavior is deprecated so you need to update your launch file.");
|
||
107 | deadzone_ /= 32767;
|
||
108 | } |
||
109 | |||
110 | if (deadzone_ > 0.9) |
||
111 | { |
||
112 | ROS_WARN("joy_node: deadzone (%f) greater than 0.9, setting it to 0.9", deadzone_);
|
||
113 | deadzone_ = 0.9; |
||
114 | } |
||
115 | |||
116 | if (deadzone_ < 0) |
||
117 | { |
||
118 | ROS_WARN("joy_node: deadzone_ (%f) less than 0, setting to 0.", deadzone_);
|
||
119 | deadzone_ = 0;
|
||
120 | } |
||
121 | |||
122 | if (autorepeat_rate_ < 0) |
||
123 | { |
||
124 | ROS_WARN("joy_node: autorepeat_rate (%f) less than 0, setting to 0.", autorepeat_rate_);
|
||
125 | autorepeat_rate_ = 0;
|
||
126 | } |
||
127 | |||
128 | if (coalesce_interval_ < 0) |
||
129 | { |
||
130 | ROS_WARN("joy_node: coalesce_interval (%f) less than 0, setting to 0.", coalesce_interval_);
|
||
131 | coalesce_interval_ = 0;
|
||
132 | } |
||
133 | |||
134 | // Parameter conversions
|
||
135 | double autorepeat_interval = 1 / autorepeat_rate_; |
||
136 | double scale = -1. / (1. - deadzone_) / 32767.; |
||
137 | double unscaled_deadzone = 32767. * deadzone_; |
||
138 | |||
139 | js_event event; |
||
140 | struct timeval tv;
|
||
141 | fd_set set; |
||
142 | int joy_fd;
|
||
143 | event_count_ = 0;
|
||
144 | pub_count_ = 0;
|
||
145 | lastDiagTime_ = ros::Time::now().toSec(); |
||
146 | |||
147 | // Big while loop opens, publishes
|
||
148 | while (nh_.ok())
|
||
149 | { |
||
150 | open_ = false;
|
||
151 | diagnostic_.force_update(); |
||
152 | bool first_fault = true; |
||
153 | while (true) |
||
154 | { |
||
155 | ros::spinOnce(); |
||
156 | if (!nh_.ok())
|
||
157 | goto cleanup;
|
||
158 | joy_fd = open(joy_dev_.c_str(), O_RDONLY); |
||
159 | if (joy_fd != -1) |
||
160 | { |
||
161 | // There seems to be a bug in the driver or something where the
|
||
162 | // initial events that are to define the initial state of the
|
||
163 | // joystick are not the values of the joystick when it was opened
|
||
164 | // but rather the values of the joystick when it was last closed.
|
||
165 | // Opening then closing and opening again is a hack to get more
|
||
166 | // accurate initial state data.
|
||
167 | close(joy_fd); |
||
168 | joy_fd = open(joy_dev_.c_str(), O_RDONLY); |
||
169 | } |
||
170 | if (joy_fd != -1) |
||
171 | break;
|
||
172 | if (first_fault)
|
||
173 | { |
||
174 | ROS_ERROR("Couldn't open joystick %s. Will retry every second.", joy_dev_.c_str());
|
||
175 | first_fault = false;
|
||
176 | } |
||
177 | sleep(1.0); |
||
178 | diagnostic_.update(); |
||
179 | } |
||
180 | |||
181 | ROS_INFO("Opened joystick: %s. deadzone_: %f.", joy_dev_.c_str(), deadzone_);
|
||
182 | open_ = true;
|
||
183 | diagnostic_.force_update(); |
||
184 | |||
185 | bool tv_set = false; |
||
186 | bool publication_pending = false; |
||
187 | tv.tv_sec = 1;
|
||
188 | tv.tv_usec = 0;
|
||
189 | sensor_msgs::Joy joy_msg; // Here because we want to reset it on device close.
|
||
190 | while (nh_.ok())
|
||
191 | { |
||
192 | ros::spinOnce(); |
||
193 | |||
194 | bool publish_now = false; |
||
195 | bool publish_soon = false; |
||
196 | FD_ZERO(&set); |
||
197 | FD_SET(joy_fd, &set); |
||
198 | |||
199 | //ROS_INFO("Select...");
|
||
200 | int select_out = select(joy_fd+1, &set, NULL, NULL, &tv); |
||
201 | //ROS_INFO("Tick...");
|
||
202 | if (select_out == -1) |
||
203 | { |
||
204 | tv.tv_sec = 0;
|
||
205 | tv.tv_usec = 0;
|
||
206 | //ROS_INFO("Select returned negative. %i", ros::isShuttingDown());
|
||
207 | continue;
|
||
208 | // break; // Joystick is probably closed. Not sure if this case is useful.
|
||
209 | } |
||
210 | |||
211 | if (FD_ISSET(joy_fd, &set))
|
||
212 | { |
||
213 | if (read(joy_fd, &event, sizeof(js_event)) == -1 && errno != EAGAIN) |
||
214 | break; // Joystick is probably closed. Definitely occurs. |
||
215 | |||
216 | //ROS_INFO("Read data...");
|
||
217 | joy_msg.header.stamp = ros::Time().now(); |
||
218 | event_count_++; |
||
219 | switch(event.type)
|
||
220 | { |
||
221 | case JS_EVENT_BUTTON:
|
||
222 | case JS_EVENT_BUTTON | JS_EVENT_INIT:
|
||
223 | if(event.number >= joy_msg.buttons.size())
|
||
224 | { |
||
225 | int old_size = joy_msg.buttons.size();
|
||
226 | joy_msg.buttons.resize(event.number+1);
|
||
227 | for(unsigned int i=old_size;i<joy_msg.buttons.size();i++) |
||
228 | joy_msg.buttons[i] = 0.0; |
||
229 | } |
||
230 | joy_msg.buttons[event.number] = (event.value ? 1 : 0); |
||
231 | // For initial events, wait a bit before sending to try to catch
|
||
232 | // all the initial events.
|
||
233 | if (!(event.type & JS_EVENT_INIT))
|
||
234 | publish_now = true;
|
||
235 | else
|
||
236 | publish_soon = true;
|
||
237 | break;
|
||
238 | case JS_EVENT_AXIS:
|
||
239 | case JS_EVENT_AXIS | JS_EVENT_INIT:
|
||
240 | if(event.number >= joy_msg.axes.size())
|
||
241 | { |
||
242 | int old_size = joy_msg.axes.size();
|
||
243 | joy_msg.axes.resize(event.number+1);
|
||
244 | for(unsigned int i=old_size;i<joy_msg.axes.size();i++) |
||
245 | joy_msg.axes[i] = 0.0; |
||
246 | } |
||
247 | if (!(event.type & JS_EVENT_INIT)) // Init event.value is wrong. |
||
248 | { |
||
249 | double val = event.value;
|
||
250 | // Allows deadzone to be "smooth"
|
||
251 | if (val > unscaled_deadzone)
|
||
252 | val -= unscaled_deadzone; |
||
253 | else if (val < -unscaled_deadzone) |
||
254 | val += unscaled_deadzone; |
||
255 | else
|
||
256 | val = 0;
|
||
257 | joy_msg.axes[event.number] = val * scale; |
||
258 | } |
||
259 | // Will wait a bit before sending to try to combine events.
|
||
260 | publish_soon = true;
|
||
261 | break;
|
||
262 | default:
|
||
263 | ROS_WARN("joy_node: Unknown event type. Please file a ticket. time=%u, value=%d, type=%Xh, number=%d", event.time, event.value, event.type, event.number);
|
||
264 | break;
|
||
265 | } |
||
266 | } |
||
267 | else if (tv_set) // Assume that the timer has expired. |
||
268 | publish_now = true;
|
||
269 | |||
270 | if (publish_now)
|
||
271 | { |
||
272 | // Assume that all the JS_EVENT_INIT messages have arrived already.
|
||
273 | // This should be the case as the kernel sends them along as soon as
|
||
274 | // the device opens.
|
||
275 | //ROS_INFO("Publish...");
|
||
276 | pub_.publish(joy_msg); |
||
277 | publish_now = false;
|
||
278 | tv_set = false;
|
||
279 | publication_pending = false;
|
||
280 | publish_soon = false;
|
||
281 | pub_count_++; |
||
282 | } |
||
283 | |||
284 | // If an axis event occurred, start a timer to combine with other
|
||
285 | // events.
|
||
286 | if (!publication_pending && publish_soon)
|
||
287 | { |
||
288 | tv.tv_sec = trunc(coalesce_interval_); |
||
289 | tv.tv_usec = (coalesce_interval_ - tv.tv_sec) * 1e6;
|
||
290 | publication_pending = true;
|
||
291 | tv_set = true;
|
||
292 | //ROS_INFO("Pub pending...");
|
||
293 | } |
||
294 | |||
295 | // If nothing is going on, start a timer to do autorepeat.
|
||
296 | if (!tv_set && autorepeat_rate_ > 0) |
||
297 | { |
||
298 | tv.tv_sec = trunc(autorepeat_interval); |
||
299 | tv.tv_usec = (autorepeat_interval - tv.tv_sec) * 1e6;
|
||
300 | tv_set = true;
|
||
301 | //ROS_INFO("Autorepeat pending... %i %i", tv.tv_sec, tv.tv_usec);
|
||
302 | } |
||
303 | |||
304 | if (!tv_set)
|
||
305 | { |
||
306 | tv.tv_sec = 1;
|
||
307 | tv.tv_usec = 0;
|
||
308 | } |
||
309 | |||
310 | diagnostic_.update(); |
||
311 | } // End of joystick open loop.
|
||
312 | |||
313 | close(joy_fd); |
||
314 | ros::spinOnce(); |
||
315 | if (nh_.ok())
|
||
316 | ROS_ERROR("Connection to joystick device lost unexpectedly. Will reopen.");
|
||
317 | } |
||
318 | |||
319 | cleanup:
|
||
320 | ROS_INFO("joy_node shut down.");
|
||
321 | |||
322 | return 0; |
||
323 | } |
||
324 | }; |
||
325 | |||
326 | int main(int argc, char **argv) |
||
327 | { |
||
328 | ros::init(argc, argv, "joy_node");
|
||
329 | Joystick j; |
||
330 | return j.main(argc, argv);
|
||
331 | } |