blob: 3967e60055ca8d2b6940f3cf5276dc7520781193 [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,
Austin Schuh6bf73052016-04-20 20:20:25 -070043 ::aos::vision::Vector<2> *center_right, double *angle_left,
44 double *angle_right) {
ben54dbccb2016-03-20 14:42:28 -070045 // No good targets. Let the caller decide defaults.
46 if (right_target.target_size() == 0 || left_target.target_size() == 0) {
47 return;
48 }
49
50 // Only one option, we have to go with it.
51 if (right_target.target_size() == 1 && left_target.target_size() == 1) {
52 *center_left =
53 CreateCenterFromTarget(left_target.target(0).left_corner_x(),
54 left_target.target(0).left_corner_y(),
55 left_target.target(0).right_corner_x(),
56 left_target.target(0).right_corner_y());
57 *center_right =
58 CreateCenterFromTarget(right_target.target(0).left_corner_x(),
59 right_target.target(0).left_corner_y(),
60 right_target.target(0).right_corner_x(),
61 right_target.target(0).right_corner_y());
62 return;
63 }
64
65 // Now we have to make a decision.
Austin Schuh9f59c8a2016-03-20 21:09:05 -070066 double min_angle = -1.0;
ben54dbccb2016-03-20 14:42:28 -070067 int left_index = 0;
68 // First pick the widest target from the left.
69 for (int i = 0; i < left_target.target_size(); i++) {
Austin Schuh9f59c8a2016-03-20 21:09:05 -070070 const double h = left_target.target(i).left_corner_y() -
71 left_target.target(i).right_corner_y();
72 const double wid1 = TargetWidth(left_target.target(i).left_corner_x(),
73 left_target.target(i).left_corner_y(),
74 left_target.target(i).right_corner_x(),
75 left_target.target(i).right_corner_y());
76 const double angle = h / wid1;
77 if (min_angle == -1.0 || ::std::abs(angle) < ::std::abs(min_angle)) {
78 min_angle = angle;
Austin Schuh6bf73052016-04-20 20:20:25 -070079 *angle_left = angle;
ben54dbccb2016-03-20 14:42:28 -070080 left_index = i;
81 }
82 }
83 // Calculate the angle of the bottom edge for the left.
84 double h = left_target.target(left_index).left_corner_y() -
85 left_target.target(left_index).right_corner_y();
Austin Schuh9f59c8a2016-03-20 21:09:05 -070086
87 double good_ang = min_angle;
ben54dbccb2016-03-20 14:42:28 -070088 double min_ang_err = -1.0;
89 int right_index = -1;
Austin Schuh11945742016-04-13 22:18:36 -070090 // Now pick the bottom edge angle from the right that lines up best with the
91 // left.
ben54dbccb2016-03-20 14:42:28 -070092 for (int j = 0; j < right_target.target_size(); j++) {
93 double wid2 = TargetWidth(right_target.target(j).left_corner_x(),
Austin Schuh11945742016-04-13 22:18:36 -070094 right_target.target(j).left_corner_y(),
95 right_target.target(j).right_corner_x(),
96 right_target.target(j).right_corner_y());
ben54dbccb2016-03-20 14:42:28 -070097 h = right_target.target(j).left_corner_y() -
98 right_target.target(j).right_corner_y();
Austin Schuh11945742016-04-13 22:18:36 -070099 double ang = h / wid2;
ben54dbccb2016-03-20 14:42:28 -0700100 double ang_err = ::std::abs(good_ang - ang);
101 if (min_ang_err == -1.0 || min_ang_err > ang_err) {
102 min_ang_err = ang_err;
103 right_index = j;
Austin Schuh6bf73052016-04-20 20:20:25 -0700104 *angle_right = ang;
ben54dbccb2016-03-20 14:42:28 -0700105 }
106 }
107
108 *center_left =
109 CreateCenterFromTarget(left_target.target(left_index).left_corner_x(),
110 left_target.target(left_index).left_corner_y(),
111 left_target.target(left_index).right_corner_x(),
112 left_target.target(left_index).right_corner_y());
113 *center_right =
114 CreateCenterFromTarget(right_target.target(right_index).left_corner_x(),
115 right_target.target(right_index).left_corner_y(),
116 right_target.target(right_index).right_corner_x(),
117 right_target.target(right_index).right_corner_y());
118}
119
Austin Schuh11945742016-04-13 22:18:36 -0700120class CameraHandler {
121 public:
122 void Received(const VisionData &target, ::aos::time::Time now) {
123 if (current_.received) {
124 last_ = current_;
125 }
126 current_.target = target;
127 current_.rx_time = now;
Austin Schuh6fe1d512016-04-24 19:12:04 -0700128 current_.capture_time = now -
129 ::aos::time::Time::InNS(target.send_timestamp() -
130 target.image_timestamp()) +
131 // It takes a bit to shoot a frame. Push the frame
132 // further back in time.
133 ::aos::time::Time::InMS(10);
Austin Schuh11945742016-04-13 22:18:36 -0700134 current_.received = true;
135 }
136
137 void CheckStale(::aos::time::Time now) {
138 if (now > current_.rx_time + ::aos::time::Time::InMS(50)) {
139 current_.received = false;
140 last_.received = false;
141 }
142 }
143
144 bool received_both() const { return current_.received && last_.received; }
145
146 bool is_valid() const {
147 return current_.target.target_size() > 0 && last_.target.target_size() > 0;
148 }
149
150 const VisionData &target() const { return current_.target; }
151 const VisionData &last_target() const { return last_.target; }
152
153 ::aos::time::Time capture_time() const { return current_.capture_time; }
154 ::aos::time::Time last_capture_time() const { return last_.capture_time; }
155
156 private:
157 struct TargetWithTimes {
158 VisionData target;
159 ::aos::time::Time rx_time{0, 0};
160 ::aos::time::Time capture_time{0, 0};
161 bool received = false;
162 };
163
164 TargetWithTimes current_;
165 TargetWithTimes last_;
166};
167
Austin Schuh6bf73052016-04-20 20:20:25 -0700168void CalculateFiltered(const CameraHandler &older, const CameraHandler &newer,
169 const ::aos::vision::Vector<2> &newer_center,
170 const ::aos::vision::Vector<2> &last_newer_center,
171 double angle, double last_angle,
172 ::aos::vision::Vector<2> *interpolated_result,
173 double *interpolated_angle) {
Austin Schuh11945742016-04-13 22:18:36 -0700174 const double age_ratio =
175 (older.capture_time() - newer.last_capture_time()).ToSeconds() /
176 (newer.capture_time() - newer.last_capture_time()).ToSeconds();
Austin Schuh6bf73052016-04-20 20:20:25 -0700177 interpolated_result->Set(
Austin Schuh11945742016-04-13 22:18:36 -0700178 newer_center.x() * age_ratio + (1 - age_ratio) * last_newer_center.x(),
179 newer_center.y() * age_ratio + (1 - age_ratio) * last_newer_center.y());
Austin Schuh6bf73052016-04-20 20:20:25 -0700180
181 *interpolated_angle = angle * age_ratio + (1 - age_ratio) * last_angle;
Austin Schuh11945742016-04-13 22:18:36 -0700182}
ben54dbccb2016-03-20 14:42:28 -0700183
Brian Silvermanbc831182016-04-16 02:06:09 -0400184// Handles calculating drivetrain offsets.
185class DrivetrainOffsetCalculator {
186 public:
187 // To be called by ::std::thread.
188 void operator()() {
189 auto &status = ::frc971::control_loops::drivetrain_queue.status;
190 while (run_) {
191 status.FetchAnother();
192
193 ::aos::MutexLocker locker(&lock_);
194 data_[data_index_].time = status->sent_time;
195 data_[data_index_].left = status->estimated_left_position;
196 data_[data_index_].right = status->estimated_right_position;
197 ++data_index_;
198 if (data_index_ == data_.size()) data_index_ = 0;
199 if (valid_data_ < data_.size()) ++valid_data_;
200 }
201 }
202
203 // Takes a vision status message with everything except
204 // drivetrain_{left,right}_position set and sets those.
205 // Returns false if it doesn't have enough data to fill them out.
206 bool CompleteVisionStatus(::y2016::vision::VisionStatus *status) {
207 if (valid_data_ == 0) return false;
208
209 const ::aos::time::Time capture_time =
210 ::aos::time::Time::InNS(status->target_time);
211 DrivetrainData before, after;
212 FindBeforeAfter(&before, &after, capture_time);
213
214 if (before.time == after.time) {
215 status->drivetrain_left_position = before.left;
216 status->drivetrain_right_position = before.right;
217 } else {
218 const double age_ratio = (capture_time - before.time).ToSeconds() /
219 (after.time - before.time).ToSeconds();
220 status->drivetrain_left_position =
221 before.left * (1 - age_ratio) + after.left * age_ratio;
222 status->drivetrain_right_position =
223 before.right * (1 - age_ratio) + after.right * age_ratio;
224 }
225
226 return true;
227 }
228
229 void Quit() { run_ = false; }
230
231 private:
232 struct DrivetrainData {
233 ::aos::time::Time time;
234 double left, right;
235 };
236
237 // Fills out before and after with the data surrounding capture_time.
238 // They might be identical if that's the closest approximation.
239 // Do not call this if valid_data_ is 0.
240 void FindBeforeAfter(DrivetrainData *before, DrivetrainData *after,
241 ::aos::time::Time capture_time) {
242 ::aos::MutexLocker locker(&lock_);
243 size_t location = 0;
244 while (true) {
245 // We hit the end of our data. Just fill them both out as the last data
246 // point.
247 if (location >= valid_data_) {
248 *before = *after =
249 data_[previous_index((valid_data_ + data_index_) % data_.size())];
250 return;
251 }
252
253 // The index into data_ corresponding to location positions after
254 // (data_index_ - 1).
255 const size_t index = previous_index(location + data_index_);
256
257 // If we've found the one we want.
258 if (data_[index].time > capture_time) {
259 *after = data_[index];
260 if (location == 0) {
261 // If this is the first one and it's already after, just return the
262 // same thing for both.
263 *before = data_[index];
264 } else {
265 *before = data_[previous_index(index)];
266 }
267 return;
268 }
269
270 ++location;
271 }
272 }
273
274 size_t previous_index(size_t index) const {
275 if (index == 0) {
276 return data_.size() - 1;
277 } else {
278 return index - 1;
279 }
280 }
281
282 ::std::array<DrivetrainData, 200> data_;
283 // The index into data_ the next data point is going at.
284 size_t data_index_ = 0;
285 // How many elemets of data_ are valid.
286 size_t valid_data_ = 0;
287
288 ::aos::Mutex lock_;
289
290 ::std::atomic<bool> run_{true};
291};
292
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400293void Main() {
294 StereoGeometry stereo(constants::GetValues().vision_name);
295 LOG(INFO, "calibration: %s\n",
296 stereo.calibration().ShortDebugString().c_str());
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400297
Brian Silvermanbc831182016-04-16 02:06:09 -0400298 DrivetrainOffsetCalculator drivetrain_offset;
299 ::std::thread drivetrain_offset_thread(::std::ref(drivetrain_offset));
300
Austin Schuh11945742016-04-13 22:18:36 -0700301 CameraHandler left;
302 CameraHandler right;
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700303
304 ::aos::vision::RXUdpSocket recv(8080);
305 char rawData[65507];
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700306
307 while (true) {
308 // TODO(austin): Don't malloc.
309 VisionData target;
310 int size = recv.Recv(rawData, 65507);
Austin Schuh11945742016-04-13 22:18:36 -0700311 ::aos::time::Time now = ::aos::time::Time::Now();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700312
313 if (target.ParseFromArray(rawData, size)) {
314 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700315 left.Received(target, now);
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700316 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700317 right.Received(target, now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400318 }
319 } else {
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700320 LOG(ERROR, "oh noes: parse error\n");
321 continue;
322 }
323
Austin Schuh11945742016-04-13 22:18:36 -0700324 left.CheckStale(now);
325 right.CheckStale(now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400326
Austin Schuh11945742016-04-13 22:18:36 -0700327 if (left.received_both() && right.received_both()) {
328 const bool left_image_valid = left.is_valid();
329 const bool right_image_valid = right.is_valid();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700330
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400331 auto new_vision_status = vision_status.MakeMessage();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700332 new_vision_status->left_image_valid = left_image_valid;
333 new_vision_status->right_image_valid = right_image_valid;
334 if (left_image_valid && right_image_valid) {
Austin Schuh11945742016-04-13 22:18:36 -0700335 ::aos::vision::Vector<2> center_left(0.0, 0.0);
336 ::aos::vision::Vector<2> center_right(0.0, 0.0);
Austin Schuh6bf73052016-04-20 20:20:25 -0700337 double angle_left;
338 double angle_right;
Austin Schuh11945742016-04-13 22:18:36 -0700339 SelectTargets(left.target(), right.target(), &center_left,
Austin Schuh6bf73052016-04-20 20:20:25 -0700340 &center_right, &angle_left, &angle_right);
Austin Schuh11945742016-04-13 22:18:36 -0700341
342 // TODO(Ben): Remember this from last time instead of recalculating it
343 // each time.
344 ::aos::vision::Vector<2> last_center_left(0.0, 0.0);
345 ::aos::vision::Vector<2> last_center_right(0.0, 0.0);
Austin Schuh6bf73052016-04-20 20:20:25 -0700346 double last_angle_left;
347 double last_angle_right;
Austin Schuh11945742016-04-13 22:18:36 -0700348 SelectTargets(left.last_target(), right.last_target(),
Austin Schuh6bf73052016-04-20 20:20:25 -0700349 &last_center_left, &last_center_right,
350 &last_angle_left, &last_angle_right);
Austin Schuh11945742016-04-13 22:18:36 -0700351
352 ::aos::vision::Vector<2> filtered_center_left(0.0, 0.0);
353 ::aos::vision::Vector<2> filtered_center_right(0.0, 0.0);
Austin Schuh6bf73052016-04-20 20:20:25 -0700354 double filtered_angle_left;
355 double filtered_angle_right;
Austin Schuh11945742016-04-13 22:18:36 -0700356 if (left.capture_time() < right.capture_time()) {
357 filtered_center_left = center_left;
Austin Schuh6bf73052016-04-20 20:20:25 -0700358 filtered_angle_left = angle_left;
Austin Schuh11945742016-04-13 22:18:36 -0700359 new_vision_status->target_time = left.capture_time().ToNSec();
Austin Schuh6bf73052016-04-20 20:20:25 -0700360 CalculateFiltered(left, right, center_right, last_center_right,
361 angle_right, last_angle_right,
362 &filtered_center_right, &filtered_angle_right);
Austin Schuh11945742016-04-13 22:18:36 -0700363 } else {
364 filtered_center_right = center_right;
Austin Schuh6bf73052016-04-20 20:20:25 -0700365 filtered_angle_right = angle_right;
Austin Schuh11945742016-04-13 22:18:36 -0700366 new_vision_status->target_time = right.capture_time().ToNSec();
Austin Schuh6bf73052016-04-20 20:20:25 -0700367 CalculateFiltered(right, left, center_left, last_center_left,
368 angle_left, last_angle_left, &filtered_center_left,
369 &filtered_angle_left);
Austin Schuh11945742016-04-13 22:18:36 -0700370 }
371
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700372 double distance, horizontal_angle, vertical_angle;
Austin Schuh11945742016-04-13 22:18:36 -0700373 stereo.Process(filtered_center_left, filtered_center_right, &distance,
374 &horizontal_angle, &vertical_angle);
375 new_vision_status->left_image_timestamp =
376 left.target().image_timestamp();
377 new_vision_status->right_image_timestamp =
378 right.target().image_timestamp();
379 new_vision_status->left_send_timestamp = left.target().send_timestamp();
380 new_vision_status->right_send_timestamp = right.target().send_timestamp();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700381 new_vision_status->horizontal_angle = horizontal_angle;
382 new_vision_status->vertical_angle = vertical_angle;
383 new_vision_status->distance = distance;
Austin Schuh6bf73052016-04-20 20:20:25 -0700384 new_vision_status->angle =
385 (filtered_angle_left + filtered_angle_right) / 2.0;
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700386 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400387
Brian Silvermanbc831182016-04-16 02:06:09 -0400388 if (drivetrain_offset.CompleteVisionStatus(new_vision_status.get())) {
389 LOG_STRUCT(DEBUG, "vision", *new_vision_status);
390 if (!new_vision_status.Send()) {
391 LOG(ERROR, "Failed to send vision information\n");
392 }
393 } else {
394 LOG_STRUCT(WARNING, "vision without drivetrain", *new_vision_status);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400395 }
396 }
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700397
398 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700399 LOG(DEBUG, "left_target: %s\n", left.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700400 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700401 LOG(DEBUG, "right_target: %s\n",
402 right.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700403 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400404 }
Brian Silvermanbc831182016-04-16 02:06:09 -0400405
406 drivetrain_offset.Quit();
407 drivetrain_offset_thread.join();
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400408}
409
410} // namespace vision
411} // namespace y2016
412
413int main(int /*argc*/, char ** /*argv*/) {
414 ::aos::InitNRT();
415 ::y2016::vision::Main();
416}