3eb68743e61e5385dbed13699677c9dcde5b5b9e
[bachelor-thesis/roomba_tests.git] / main.cc
1 /**
2 * @file main.cc
3 * @date 27 Nov 2010
4 * Copyright © 2010 Roland Hieber
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "svnrevision.h"
26 #include <iostream>
27 #include <stdint.h>
28 #include <external_interface/pc/pc_os_model.h>
29 #include <external_interface/pc/pc_com_uart.h>
30 #include <external_interface/pc/pc_timer.h>
31 #include <intermediate/robot/roomba/roomba.h>
32 #include <intermediate/robot/controlled_motion.h>
33 #include <QApplication>
34 #include <QInputDialog>
35
36 using namespace std;
37
38 #if QT_VERSION < 0x040500
39 #define getInt QInputDialog::getInteger
40 #else // QT_VERSION > 0x040500
41 #define getInt QInputDialog::getInt
42 #endif // QT_VERSION
43 // UART port on which we communicate with the Roomba
44 char uart[] = "/dev/ttyUSB0";
45
46 typedef wiselib::PCOsModel OsModel;
47 typedef wiselib::StandaloneMath Math;
48 typedef wiselib::PCComUartModel<OsModel, uart> RoombaUart;
49 typedef wiselib::RoombaModel<OsModel, RoombaUart> Roomba;
50 typedef wiselib::ControlledMotion<OsModel, Roomba> ControlledMotion;
51
52 /**
53 * Global stuff we need
54 */
55 OsModel::Os os;
56 OsModel::Timer::self_t timer;
57 Roomba roomba;
58 RoombaUart roomba_uart(os);
59 ControlledMotion ctrl_motion;
60 char * roomba_id;
61 char * ground_type;
62
63 /**
64 * Sensor data we need, filled in callback
65 */
66 struct SensorData {
67
68 SensorData() :
69 capacity(0), charge(0), charging(0), current(0), temperature(0),
70 voltage(0), abs_left_encoder_counts(0), abs_right_encoder_counts(0) {
71 }
72
73 uint16_t capacity, charge;
74 uint8_t charging;
75 int16_t current;
76 int8_t temperature;
77 uint16_t voltage;
78 /** absolute encoder counts; i.e. not overflown, but consecutive */
79 int abs_left_encoder_counts, abs_right_encoder_counts;
80 } sensor_data;
81
82 /**
83 * Returns the difference between two unsigned short values. The calculated
84 * value is always smaller or equal to 0x8000.
85 * This is useful if you have an overflowing counter and you want to determine
86 * when you have to "wrap over" the value.
87 */
88 int nearest_diff(unsigned short last, unsigned short current) {
89 int d = current - last;
90 if(d < -0x8000) { // overflow in positive direction
91 d = (0x10000 - last_i + this_i);
92 }
93 if(d >= 0x8000) { // overflow in negative direction
94 d = -(0x10000 - this_i + last_i);
95 }
96 }
97
98 /**
99 * Callback that fills the sensor data when data is available
100 */
101 struct DataAvailable {
102 int latest_encoder_left_, latest_encoder_right_;
103
104 DataAvailable() :
105 latest_encoder_left_(0), latest_encoder_right_(0) {
106 }
107
108 void cb(int state) {
109 if(state != Roomba::DATA_AVAILABLE) {
110 return;
111 }
112 sensor_data.capacity = roomba().capacity;
113 sensor_data.charge = roomba().charge;
114 sensor_data.charging = roomba().charging;
115 sensor_data.current = roomba().current;
116 sensor_data.voltage = roomba().voltage;
117 sensor_data.abs_left_encoder_counts += nearest_diff(latest_encoder_left_,
118 roomba().left_encoder_counts);
119 latest_encoder_left_ = roomba().left_encoder_counts;
120 sensor_data.abs_right_encoder_counts += nearest_diff(latest_encoder_right_,
121 roomba().right_encoder_counts);
122 latest_encoder_right_ = roomba().right_encoder_counts;
123 }
124 } data_available;
125
126 /**
127 * return battery status as QString
128 */
129 QString chargeText(Roomba& roomba) {
130 return QString("Battery: %1%\nPress Cancel to exit.\n\n").arg(int(float(
131 sensor_data.charge) / float(sensor_data.capacity) * 100.0));
132 }
133
134 /**
135 * drive iterations. logs values to stdout.
136 */
137 void drive(Roomba& roomba, ControlledMotion& ctrl_motion) {
138 int input_distance = 0, deviation_x = 0, deviation_y = 0, velocity = 100;
139 bool ok = false;
140
141 while(true) {
142
143 // new distance to drive
144 input_distance = getInt(0, "Input distance", chargeText(roomba)
145 + "Input new distance in mm:", input_distance,
146 numeric_limits<int>::min(), numeric_limits<int>::max(), 1, &ok);
147 if(!ok) {
148 break;
149 }
150 // new velocity
151 velocity = getInt(0, "Input velocity", chargeText(roomba)
152 + "Input drive velocity in mm/sec:", velocity, -500, 500, 10, &ok);
153 if(!ok) {
154 break;
155 }
156 ctrl_motion.move_distance(input_distance, velocity);
157 roomba.wait_for_stop();
158
159 // measured deviation
160 deviation_x = getInt(0, "Input x deviation", chargeText(roomba)
161 + "Input travelled distance on x axis in mm:", deviation_x,
162 numeric_limits<int>::min(), numeric_limits<int>::max(), 1, &ok);
163 if(!ok) {
164 break;
165 }
166 deviation_y = getInt(0, "Input y deviation", chargeText(roomba)
167 + "Input travelled distance on y axis in mm:", deviation_y,
168 numeric_limits<int>::min(), numeric_limits<int>::max(), 1, &ok);
169 if(!ok) {
170 break;
171 }
172 cout << "svn=" << SVNREVISION << " roomba_id=" << roomba_id
173 << " move=straight" << " ground_type=" << ground_type
174 << " input_distance=" << input_distance << " velocity=" << velocity
175 << " internal_distance=" << roomba.distance() << " deviation_x="
176 << deviation_x << " deviation_y=" << deviation_y
177 << " abs_encoder_ticks_left=" << sensor_data.abs_left_encoder_counts
178 << " abs_encoder_ticks_right=" << sensor_data.abs_right_encoder_counts
179 << " batt_charge=" << sensor_data.charge << " batt_capacity="
180 << sensor_data.capacity << " batt_voltage=" << sensor_data.voltage
181 << " batt_current=" << sensor_data.current << endl;
182
183 // reset, because we only need the difference between two drive commands
184 sensor_data.abs_left_encoder_counts = 0;
185 sensor_data.abs_right_encoder_counts = 0;
186 }
187 }
188
189 /**
190 * turn iterations. logs values to stdout.
191 */
192 void turn(Roomba& roomba, ControlledMotion& ctrl_motion) {
193 int cur_angle = 0, turn_angle = 0, measured_angle = 0, velocity = 100;
194 bool ok = false;
195
196 // current angle
197 cur_angle = getInt(0, "Input current orientation", chargeText(roomba)
198 + "Input current orientation in degree:", cur_angle, 0, 359, 1, &ok);
199 if(!ok) {
200 return;
201 }
202
203 while(true) {
204 // new turn velocity
205 velocity = getInt(0, "Input velocity", chargeText(roomba)
206 + "Input turn velocity in mm/sec:", velocity, -500, 500, 10, &ok);
207 if(!ok) {
208 break;
209 }
210
211 // angle to turn about
212 turn_angle = getInt(0, "Input turn angle", chargeText(roomba)
213 + "Input angle in degree to turn about:", turn_angle,
214 numeric_limits<int>::min() + 360, numeric_limits<int>::max() - 360, 1,
215 &ok);
216 if(!ok) {
217 break;
218 }
219
220 ctrl_motion.turn_about(Math::degrees_to_radians(turn_angle), velocity);
221 roomba.wait_for_stop();
222
223 // new current angle
224 measured_angle = getInt(0, "Input measured angle", chargeText(roomba)
225 + QString("Orientation should be %1 degree now.\n\n").arg((cur_angle
226 + turn_angle) % 360) + "Input measured angle in degree the Roomba has "
227 "turned:", turn_angle, 0, numeric_limits<int>::max(), 1, &ok);
228 if(!ok) {
229 break;
230 }
231
232 cout << "svn=" << SVNREVISION << " roomba_id=" << roomba_id
233 << " move=straight" << " ground_type=" << ground_type << "turn_angle="
234 << turn_angle << " measured_angle=" << measured_angle << " velocity="
235 << velocity << " internal_angle=" << roomba.angle()
236 << " abs_encoder_ticks_left=" << sensor_data.abs_left_encoder_counts
237 << " abs_encoder_ticks_right=" << sensor_data.abs_right_encoder_counts
238 << " batt_charge=" << sensor_data.charge << " batt_capacity="
239 << sensor_data.capacity << " batt_voltage=" << sensor_data.voltage
240 << " batt_current=" << sensor_data.current << endl;
241
242 // reset, because we only need the difference between two turns
243 sensor_data.abs_left_encoder_counts = 0;
244 sensor_data.abs_right_encoder_counts = 0;
245
246 // new orientation
247 cur_angle = (cur_angle + measured_angle) % 360;
248 }
249 }
250
251 /**
252 * main function
253 */
254 int main(int argc, char ** argv) {
255
256 if(argc < 4) {
257 cerr << "Usage: " << argv[0]
258 << " --turn|-t|--drive|-d roomba-id ground-type" << endl;
259 exit(-1);
260 }
261
262 // init stuff
263 QApplication app(argc, argv);
264
265 roomba_uart.set_baudrate(19200);
266 roomba_uart.enable_serial_comm();
267 roomba.init(roomba_uart, timer, Roomba::POSITION
268 | Roomba::BATTERY_AND_TEMPERATURE);
269
270 cerr << "Got roomba at " << roomba_uart.address() << endl;
271
272 roomba.reset_distance();
273 roomba.reset_angle();
274 ctrl_motion.init(roomba);
275
276 // we do not want the probably corrupted data from roomba(), instead we fill
277 // our own values when data is available
278 roomba.register_state_callback<DataAvailable, &DataAvailable::cb> (
279 &data_available);
280
281 // fill it once
282 roomba.notify_state_receivers(Roomba::DATA_AVAILABLE);
283
284 // actual tests
285 roomba_id = argv[2];
286 ground_type = argv[3];
287 if(strcmp(argv[1], "--turn") == 0 || strcmp(argv[1], "-t") == 0) {
288 turn(roomba, ctrl_motion);
289 } else if(strcmp(argv[1], "--drive") == 0 || strcmp(argv[1], "-d") == 0) {
290 drive(roomba, ctrl_motion);
291 }
292 }
This page took 0.104479 seconds and 3 git commands to generate.