blob: 04fbac2d376f47f00d24b559e081fa809794506b [file] [log] [blame]
Brian Silverman2ccf8c52016-03-15 00:22:26 -04001#include <stdlib.h>
2#include <netdb.h>
3#include <unistd.h>
4
5#include <vector>
6#include <memory>
Brian Silvermanbc831182016-04-16 02:06:09 -04007#include <array>
8#include <thread>
9#include <atomic>
10#include <limits>
Brian Silverman2ccf8c52016-03-15 00:22:26 -040011
12#include "aos/linux_code/init.h"
13#include "aos/common/time.h"
14#include "aos/common/logging/logging.h"
15#include "aos/common/logging/queue_logging.h"
16#include "aos/vision/events/udp.h"
Brian Silvermanbc831182016-04-16 02:06:09 -040017#include "aos/common/mutex.h"
18
19#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Brian Silverman2ccf8c52016-03-15 00:22:26 -040020
21#include "y2016/vision/vision.q.h"
22#include "y2016/vision/vision_data.pb.h"
23#include "y2016/vision/stereo_geometry.h"
24#include "y2016/constants.h"
25
26namespace y2016 {
27namespace vision {
28
Austin Schuh11945742016-04-13 22:18:36 -070029::aos::vision::Vector<2> CreateCenterFromTarget(double lx, double ly, double rx,
30 double ry) {
ben54dbccb2016-03-20 14:42:28 -070031 return ::aos::vision::Vector<2>((lx + rx) / 2.0, (ly + ry) / 2.0);
32}
33
34double TargetWidth(double lx, double ly, double rx, double ry) {
35 double dx = lx - rx;
36 double dy = ly - ry;
Austin Schuh098e4872016-03-20 16:51:24 -070037 return ::std::hypot(dx, dy);
ben54dbccb2016-03-20 14:42:28 -070038}
39
40void SelectTargets(const VisionData &left_target,
41 const VisionData &right_target,
42 ::aos::vision::Vector<2> *center_left,
43 ::aos::vision::Vector<2> *center_right) {
44 // No good targets. Let the caller decide defaults.
45 if (right_target.target_size() == 0 || left_target.target_size() == 0) {
46 return;
47 }
48
49 // Only one option, we have to go with it.
50 if (right_target.target_size() == 1 && left_target.target_size() == 1) {
51 *center_left =
52 CreateCenterFromTarget(left_target.target(0).left_corner_x(),
53 left_target.target(0).left_corner_y(),
54 left_target.target(0).right_corner_x(),
55 left_target.target(0).right_corner_y());
56 *center_right =
57 CreateCenterFromTarget(right_target.target(0).left_corner_x(),
58 right_target.target(0).left_corner_y(),
59 right_target.target(0).right_corner_x(),
60 right_target.target(0).right_corner_y());
61 return;
62 }
63
64 // Now we have to make a decision.
Austin Schuh9f59c8a2016-03-20 21:09:05 -070065 double min_angle = -1.0;
ben54dbccb2016-03-20 14:42:28 -070066 int left_index = 0;
67 // First pick the widest target from the left.
68 for (int i = 0; i < left_target.target_size(); i++) {
Austin Schuh9f59c8a2016-03-20 21:09:05 -070069 const double h = left_target.target(i).left_corner_y() -
70 left_target.target(i).right_corner_y();
71 const double wid1 = TargetWidth(left_target.target(i).left_corner_x(),
72 left_target.target(i).left_corner_y(),
73 left_target.target(i).right_corner_x(),
74 left_target.target(i).right_corner_y());
75 const double angle = h / wid1;
76 if (min_angle == -1.0 || ::std::abs(angle) < ::std::abs(min_angle)) {
77 min_angle = angle;
ben54dbccb2016-03-20 14:42:28 -070078 left_index = i;
79 }
80 }
81 // Calculate the angle of the bottom edge for the left.
82 double h = left_target.target(left_index).left_corner_y() -
83 left_target.target(left_index).right_corner_y();
Austin Schuh9f59c8a2016-03-20 21:09:05 -070084
85 double good_ang = min_angle;
ben54dbccb2016-03-20 14:42:28 -070086 double min_ang_err = -1.0;
87 int right_index = -1;
Austin Schuh11945742016-04-13 22:18:36 -070088 // Now pick the bottom edge angle from the right that lines up best with the
89 // left.
ben54dbccb2016-03-20 14:42:28 -070090 for (int j = 0; j < right_target.target_size(); j++) {
91 double wid2 = TargetWidth(right_target.target(j).left_corner_x(),
Austin Schuh11945742016-04-13 22:18:36 -070092 right_target.target(j).left_corner_y(),
93 right_target.target(j).right_corner_x(),
94 right_target.target(j).right_corner_y());
ben54dbccb2016-03-20 14:42:28 -070095 h = right_target.target(j).left_corner_y() -
96 right_target.target(j).right_corner_y();
Austin Schuh11945742016-04-13 22:18:36 -070097 double ang = h / wid2;
ben54dbccb2016-03-20 14:42:28 -070098 double ang_err = ::std::abs(good_ang - ang);
99 if (min_ang_err == -1.0 || min_ang_err > ang_err) {
100 min_ang_err = ang_err;
101 right_index = j;
102 }
103 }
104
105 *center_left =
106 CreateCenterFromTarget(left_target.target(left_index).left_corner_x(),
107 left_target.target(left_index).left_corner_y(),
108 left_target.target(left_index).right_corner_x(),
109 left_target.target(left_index).right_corner_y());
110 *center_right =
111 CreateCenterFromTarget(right_target.target(right_index).left_corner_x(),
112 right_target.target(right_index).left_corner_y(),
113 right_target.target(right_index).right_corner_x(),
114 right_target.target(right_index).right_corner_y());
115}
116
Austin Schuh11945742016-04-13 22:18:36 -0700117class CameraHandler {
118 public:
119 void Received(const VisionData &target, ::aos::time::Time now) {
120 if (current_.received) {
121 last_ = current_;
122 }
123 current_.target = target;
124 current_.rx_time = now;
125 current_.capture_time =
126 now - ::aos::time::Time::InNS(target.send_timestamp() -
127 target.image_timestamp());
128 current_.received = true;
129 }
130
131 void CheckStale(::aos::time::Time now) {
132 if (now > current_.rx_time + ::aos::time::Time::InMS(50)) {
133 current_.received = false;
134 last_.received = false;
135 }
136 }
137
138 bool received_both() const { return current_.received && last_.received; }
139
140 bool is_valid() const {
141 return current_.target.target_size() > 0 && last_.target.target_size() > 0;
142 }
143
144 const VisionData &target() const { return current_.target; }
145 const VisionData &last_target() const { return last_.target; }
146
147 ::aos::time::Time capture_time() const { return current_.capture_time; }
148 ::aos::time::Time last_capture_time() const { return last_.capture_time; }
149
150 private:
151 struct TargetWithTimes {
152 VisionData target;
153 ::aos::time::Time rx_time{0, 0};
154 ::aos::time::Time capture_time{0, 0};
155 bool received = false;
156 };
157
158 TargetWithTimes current_;
159 TargetWithTimes last_;
160};
161
162::aos::vision::Vector<2> CalculateFiltered(
163 const CameraHandler &older, const CameraHandler &newer,
164 const ::aos::vision::Vector<2> &newer_center,
165 const ::aos::vision::Vector<2> &last_newer_center) {
166 const double age_ratio =
167 (older.capture_time() - newer.last_capture_time()).ToSeconds() /
168 (newer.capture_time() - newer.last_capture_time()).ToSeconds();
169 return ::aos::vision::Vector<2>(
170 newer_center.x() * age_ratio + (1 - age_ratio) * last_newer_center.x(),
171 newer_center.y() * age_ratio + (1 - age_ratio) * last_newer_center.y());
172}
ben54dbccb2016-03-20 14:42:28 -0700173
Brian Silvermanbc831182016-04-16 02:06:09 -0400174// Handles calculating drivetrain offsets.
175class DrivetrainOffsetCalculator {
176 public:
177 // To be called by ::std::thread.
178 void operator()() {
179 auto &status = ::frc971::control_loops::drivetrain_queue.status;
180 while (run_) {
181 status.FetchAnother();
182
183 ::aos::MutexLocker locker(&lock_);
184 data_[data_index_].time = status->sent_time;
185 data_[data_index_].left = status->estimated_left_position;
186 data_[data_index_].right = status->estimated_right_position;
187 ++data_index_;
188 if (data_index_ == data_.size()) data_index_ = 0;
189 if (valid_data_ < data_.size()) ++valid_data_;
190 }
191 }
192
193 // Takes a vision status message with everything except
194 // drivetrain_{left,right}_position set and sets those.
195 // Returns false if it doesn't have enough data to fill them out.
196 bool CompleteVisionStatus(::y2016::vision::VisionStatus *status) {
197 if (valid_data_ == 0) return false;
198
199 const ::aos::time::Time capture_time =
200 ::aos::time::Time::InNS(status->target_time);
201 DrivetrainData before, after;
202 FindBeforeAfter(&before, &after, capture_time);
203
204 if (before.time == after.time) {
205 status->drivetrain_left_position = before.left;
206 status->drivetrain_right_position = before.right;
207 } else {
208 const double age_ratio = (capture_time - before.time).ToSeconds() /
209 (after.time - before.time).ToSeconds();
210 status->drivetrain_left_position =
211 before.left * (1 - age_ratio) + after.left * age_ratio;
212 status->drivetrain_right_position =
213 before.right * (1 - age_ratio) + after.right * age_ratio;
214 }
215
216 return true;
217 }
218
219 void Quit() { run_ = false; }
220
221 private:
222 struct DrivetrainData {
223 ::aos::time::Time time;
224 double left, right;
225 };
226
227 // Fills out before and after with the data surrounding capture_time.
228 // They might be identical if that's the closest approximation.
229 // Do not call this if valid_data_ is 0.
230 void FindBeforeAfter(DrivetrainData *before, DrivetrainData *after,
231 ::aos::time::Time capture_time) {
232 ::aos::MutexLocker locker(&lock_);
233 size_t location = 0;
234 while (true) {
235 // We hit the end of our data. Just fill them both out as the last data
236 // point.
237 if (location >= valid_data_) {
238 *before = *after =
239 data_[previous_index((valid_data_ + data_index_) % data_.size())];
240 return;
241 }
242
243 // The index into data_ corresponding to location positions after
244 // (data_index_ - 1).
245 const size_t index = previous_index(location + data_index_);
246
247 // If we've found the one we want.
248 if (data_[index].time > capture_time) {
249 *after = data_[index];
250 if (location == 0) {
251 // If this is the first one and it's already after, just return the
252 // same thing for both.
253 *before = data_[index];
254 } else {
255 *before = data_[previous_index(index)];
256 }
257 return;
258 }
259
260 ++location;
261 }
262 }
263
264 size_t previous_index(size_t index) const {
265 if (index == 0) {
266 return data_.size() - 1;
267 } else {
268 return index - 1;
269 }
270 }
271
272 ::std::array<DrivetrainData, 200> data_;
273 // The index into data_ the next data point is going at.
274 size_t data_index_ = 0;
275 // How many elemets of data_ are valid.
276 size_t valid_data_ = 0;
277
278 ::aos::Mutex lock_;
279
280 ::std::atomic<bool> run_{true};
281};
282
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400283void Main() {
284 StereoGeometry stereo(constants::GetValues().vision_name);
285 LOG(INFO, "calibration: %s\n",
286 stereo.calibration().ShortDebugString().c_str());
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400287
Brian Silvermanbc831182016-04-16 02:06:09 -0400288 DrivetrainOffsetCalculator drivetrain_offset;
289 ::std::thread drivetrain_offset_thread(::std::ref(drivetrain_offset));
290
Austin Schuh11945742016-04-13 22:18:36 -0700291 CameraHandler left;
292 CameraHandler right;
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700293
294 ::aos::vision::RXUdpSocket recv(8080);
295 char rawData[65507];
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700296
297 while (true) {
298 // TODO(austin): Don't malloc.
299 VisionData target;
300 int size = recv.Recv(rawData, 65507);
Austin Schuh11945742016-04-13 22:18:36 -0700301 ::aos::time::Time now = ::aos::time::Time::Now();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700302
303 if (target.ParseFromArray(rawData, size)) {
304 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700305 left.Received(target, now);
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700306 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700307 right.Received(target, now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400308 }
309 } else {
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700310 LOG(ERROR, "oh noes: parse error\n");
311 continue;
312 }
313
Austin Schuh11945742016-04-13 22:18:36 -0700314 left.CheckStale(now);
315 right.CheckStale(now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400316
Austin Schuh11945742016-04-13 22:18:36 -0700317 if (left.received_both() && right.received_both()) {
318 const bool left_image_valid = left.is_valid();
319 const bool right_image_valid = right.is_valid();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700320
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400321 auto new_vision_status = vision_status.MakeMessage();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700322 new_vision_status->left_image_valid = left_image_valid;
323 new_vision_status->right_image_valid = right_image_valid;
324 if (left_image_valid && right_image_valid) {
Austin Schuh11945742016-04-13 22:18:36 -0700325 ::aos::vision::Vector<2> center_left(0.0, 0.0);
326 ::aos::vision::Vector<2> center_right(0.0, 0.0);
327 SelectTargets(left.target(), right.target(), &center_left,
328 &center_right);
329
330 // TODO(Ben): Remember this from last time instead of recalculating it
331 // each time.
332 ::aos::vision::Vector<2> last_center_left(0.0, 0.0);
333 ::aos::vision::Vector<2> last_center_right(0.0, 0.0);
334 SelectTargets(left.last_target(), right.last_target(),
335 &last_center_left, &last_center_right);
336
337 ::aos::vision::Vector<2> filtered_center_left(0.0, 0.0);
338 ::aos::vision::Vector<2> filtered_center_right(0.0, 0.0);
339 if (left.capture_time() < right.capture_time()) {
340 filtered_center_left = center_left;
341 new_vision_status->target_time = left.capture_time().ToNSec();
342 filtered_center_right =
343 CalculateFiltered(left, right, center_right, last_center_right);
344 } else {
345 filtered_center_right = center_right;
346 new_vision_status->target_time = right.capture_time().ToNSec();
Austin Schuh691d2702016-04-16 11:25:18 -0700347 filtered_center_left =
Austin Schuh11945742016-04-13 22:18:36 -0700348 CalculateFiltered(right, left, center_left, last_center_left);
349 }
350
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700351 double distance, horizontal_angle, vertical_angle;
Austin Schuh11945742016-04-13 22:18:36 -0700352 stereo.Process(filtered_center_left, filtered_center_right, &distance,
353 &horizontal_angle, &vertical_angle);
354 new_vision_status->left_image_timestamp =
355 left.target().image_timestamp();
356 new_vision_status->right_image_timestamp =
357 right.target().image_timestamp();
358 new_vision_status->left_send_timestamp = left.target().send_timestamp();
359 new_vision_status->right_send_timestamp = right.target().send_timestamp();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700360 new_vision_status->horizontal_angle = horizontal_angle;
361 new_vision_status->vertical_angle = vertical_angle;
362 new_vision_status->distance = distance;
363 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400364
Brian Silvermanbc831182016-04-16 02:06:09 -0400365 if (drivetrain_offset.CompleteVisionStatus(new_vision_status.get())) {
366 LOG_STRUCT(DEBUG, "vision", *new_vision_status);
367 if (!new_vision_status.Send()) {
368 LOG(ERROR, "Failed to send vision information\n");
369 }
370 } else {
371 LOG_STRUCT(WARNING, "vision without drivetrain", *new_vision_status);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400372 }
373 }
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700374
375 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700376 LOG(DEBUG, "left_target: %s\n", left.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700377 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700378 LOG(DEBUG, "right_target: %s\n",
379 right.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700380 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400381 }
Brian Silvermanbc831182016-04-16 02:06:09 -0400382
383 drivetrain_offset.Quit();
384 drivetrain_offset_thread.join();
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400385}
386
387} // namespace vision
388} // namespace y2016
389
390int main(int /*argc*/, char ** /*argv*/) {
391 ::aos::InitNRT();
392 ::y2016::vision::Main();
393}