blob: 07c5ea03d9d421e2f1c240626418f6ac5dd03683 [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>
7
8#include "aos/linux_code/init.h"
9#include "aos/common/time.h"
10#include "aos/common/logging/logging.h"
11#include "aos/common/logging/queue_logging.h"
12#include "aos/vision/events/udp.h"
13
14#include "y2016/vision/vision.q.h"
15#include "y2016/vision/vision_data.pb.h"
16#include "y2016/vision/stereo_geometry.h"
17#include "y2016/constants.h"
18
19namespace y2016 {
20namespace vision {
21
Austin Schuh11945742016-04-13 22:18:36 -070022::aos::vision::Vector<2> CreateCenterFromTarget(double lx, double ly, double rx,
23 double ry) {
ben54dbccb2016-03-20 14:42:28 -070024 return ::aos::vision::Vector<2>((lx + rx) / 2.0, (ly + ry) / 2.0);
25}
26
27double TargetWidth(double lx, double ly, double rx, double ry) {
28 double dx = lx - rx;
29 double dy = ly - ry;
Austin Schuh098e4872016-03-20 16:51:24 -070030 return ::std::hypot(dx, dy);
ben54dbccb2016-03-20 14:42:28 -070031}
32
33void SelectTargets(const VisionData &left_target,
34 const VisionData &right_target,
35 ::aos::vision::Vector<2> *center_left,
36 ::aos::vision::Vector<2> *center_right) {
37 // No good targets. Let the caller decide defaults.
38 if (right_target.target_size() == 0 || left_target.target_size() == 0) {
39 return;
40 }
41
42 // Only one option, we have to go with it.
43 if (right_target.target_size() == 1 && left_target.target_size() == 1) {
44 *center_left =
45 CreateCenterFromTarget(left_target.target(0).left_corner_x(),
46 left_target.target(0).left_corner_y(),
47 left_target.target(0).right_corner_x(),
48 left_target.target(0).right_corner_y());
49 *center_right =
50 CreateCenterFromTarget(right_target.target(0).left_corner_x(),
51 right_target.target(0).left_corner_y(),
52 right_target.target(0).right_corner_x(),
53 right_target.target(0).right_corner_y());
54 return;
55 }
56
57 // Now we have to make a decision.
Austin Schuh9f59c8a2016-03-20 21:09:05 -070058 double min_angle = -1.0;
ben54dbccb2016-03-20 14:42:28 -070059 int left_index = 0;
60 // First pick the widest target from the left.
61 for (int i = 0; i < left_target.target_size(); i++) {
Austin Schuh9f59c8a2016-03-20 21:09:05 -070062 const double h = left_target.target(i).left_corner_y() -
63 left_target.target(i).right_corner_y();
64 const double wid1 = TargetWidth(left_target.target(i).left_corner_x(),
65 left_target.target(i).left_corner_y(),
66 left_target.target(i).right_corner_x(),
67 left_target.target(i).right_corner_y());
68 const double angle = h / wid1;
69 if (min_angle == -1.0 || ::std::abs(angle) < ::std::abs(min_angle)) {
70 min_angle = angle;
ben54dbccb2016-03-20 14:42:28 -070071 left_index = i;
72 }
73 }
74 // Calculate the angle of the bottom edge for the left.
75 double h = left_target.target(left_index).left_corner_y() -
76 left_target.target(left_index).right_corner_y();
Austin Schuh9f59c8a2016-03-20 21:09:05 -070077
78 double good_ang = min_angle;
ben54dbccb2016-03-20 14:42:28 -070079 double min_ang_err = -1.0;
80 int right_index = -1;
Austin Schuh11945742016-04-13 22:18:36 -070081 // Now pick the bottom edge angle from the right that lines up best with the
82 // left.
ben54dbccb2016-03-20 14:42:28 -070083 for (int j = 0; j < right_target.target_size(); j++) {
84 double wid2 = TargetWidth(right_target.target(j).left_corner_x(),
Austin Schuh11945742016-04-13 22:18:36 -070085 right_target.target(j).left_corner_y(),
86 right_target.target(j).right_corner_x(),
87 right_target.target(j).right_corner_y());
ben54dbccb2016-03-20 14:42:28 -070088 h = right_target.target(j).left_corner_y() -
89 right_target.target(j).right_corner_y();
Austin Schuh11945742016-04-13 22:18:36 -070090 double ang = h / wid2;
ben54dbccb2016-03-20 14:42:28 -070091 double ang_err = ::std::abs(good_ang - ang);
92 if (min_ang_err == -1.0 || min_ang_err > ang_err) {
93 min_ang_err = ang_err;
94 right_index = j;
95 }
96 }
97
98 *center_left =
99 CreateCenterFromTarget(left_target.target(left_index).left_corner_x(),
100 left_target.target(left_index).left_corner_y(),
101 left_target.target(left_index).right_corner_x(),
102 left_target.target(left_index).right_corner_y());
103 *center_right =
104 CreateCenterFromTarget(right_target.target(right_index).left_corner_x(),
105 right_target.target(right_index).left_corner_y(),
106 right_target.target(right_index).right_corner_x(),
107 right_target.target(right_index).right_corner_y());
108}
109
Austin Schuh11945742016-04-13 22:18:36 -0700110class CameraHandler {
111 public:
112 void Received(const VisionData &target, ::aos::time::Time now) {
113 if (current_.received) {
114 last_ = current_;
115 }
116 current_.target = target;
117 current_.rx_time = now;
118 current_.capture_time =
119 now - ::aos::time::Time::InNS(target.send_timestamp() -
120 target.image_timestamp());
121 current_.received = true;
122 }
123
124 void CheckStale(::aos::time::Time now) {
125 if (now > current_.rx_time + ::aos::time::Time::InMS(50)) {
126 current_.received = false;
127 last_.received = false;
128 }
129 }
130
131 bool received_both() const { return current_.received && last_.received; }
132
133 bool is_valid() const {
134 return current_.target.target_size() > 0 && last_.target.target_size() > 0;
135 }
136
137 const VisionData &target() const { return current_.target; }
138 const VisionData &last_target() const { return last_.target; }
139
140 ::aos::time::Time capture_time() const { return current_.capture_time; }
141 ::aos::time::Time last_capture_time() const { return last_.capture_time; }
142
143 private:
144 struct TargetWithTimes {
145 VisionData target;
146 ::aos::time::Time rx_time{0, 0};
147 ::aos::time::Time capture_time{0, 0};
148 bool received = false;
149 };
150
151 TargetWithTimes current_;
152 TargetWithTimes last_;
153};
154
155::aos::vision::Vector<2> CalculateFiltered(
156 const CameraHandler &older, const CameraHandler &newer,
157 const ::aos::vision::Vector<2> &newer_center,
158 const ::aos::vision::Vector<2> &last_newer_center) {
159 const double age_ratio =
160 (older.capture_time() - newer.last_capture_time()).ToSeconds() /
161 (newer.capture_time() - newer.last_capture_time()).ToSeconds();
162 return ::aos::vision::Vector<2>(
163 newer_center.x() * age_ratio + (1 - age_ratio) * last_newer_center.x(),
164 newer_center.y() * age_ratio + (1 - age_ratio) * last_newer_center.y());
165}
ben54dbccb2016-03-20 14:42:28 -0700166
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400167void Main() {
168 StereoGeometry stereo(constants::GetValues().vision_name);
169 LOG(INFO, "calibration: %s\n",
170 stereo.calibration().ShortDebugString().c_str());
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400171
Austin Schuh11945742016-04-13 22:18:36 -0700172 CameraHandler left;
173 CameraHandler right;
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700174
175 ::aos::vision::RXUdpSocket recv(8080);
176 char rawData[65507];
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700177
178 while (true) {
179 // TODO(austin): Don't malloc.
180 VisionData target;
181 int size = recv.Recv(rawData, 65507);
Austin Schuh11945742016-04-13 22:18:36 -0700182 ::aos::time::Time now = ::aos::time::Time::Now();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700183
184 if (target.ParseFromArray(rawData, size)) {
185 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700186 left.Received(target, now);
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700187 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700188 right.Received(target, now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400189 }
190 } else {
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700191 LOG(ERROR, "oh noes: parse error\n");
192 continue;
193 }
194
Austin Schuh11945742016-04-13 22:18:36 -0700195 left.CheckStale(now);
196 right.CheckStale(now);
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400197
Austin Schuh11945742016-04-13 22:18:36 -0700198 if (left.received_both() && right.received_both()) {
199 const bool left_image_valid = left.is_valid();
200 const bool right_image_valid = right.is_valid();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700201
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400202 auto new_vision_status = vision_status.MakeMessage();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700203 new_vision_status->left_image_valid = left_image_valid;
204 new_vision_status->right_image_valid = right_image_valid;
205 if (left_image_valid && right_image_valid) {
Austin Schuh11945742016-04-13 22:18:36 -0700206 ::aos::vision::Vector<2> center_left(0.0, 0.0);
207 ::aos::vision::Vector<2> center_right(0.0, 0.0);
208 SelectTargets(left.target(), right.target(), &center_left,
209 &center_right);
210
211 // TODO(Ben): Remember this from last time instead of recalculating it
212 // each time.
213 ::aos::vision::Vector<2> last_center_left(0.0, 0.0);
214 ::aos::vision::Vector<2> last_center_right(0.0, 0.0);
215 SelectTargets(left.last_target(), right.last_target(),
216 &last_center_left, &last_center_right);
217
218 ::aos::vision::Vector<2> filtered_center_left(0.0, 0.0);
219 ::aos::vision::Vector<2> filtered_center_right(0.0, 0.0);
220 if (left.capture_time() < right.capture_time()) {
221 filtered_center_left = center_left;
222 new_vision_status->target_time = left.capture_time().ToNSec();
223 filtered_center_right =
224 CalculateFiltered(left, right, center_right, last_center_right);
225 } else {
226 filtered_center_right = center_right;
227 new_vision_status->target_time = right.capture_time().ToNSec();
228 filtered_center_right =
229 CalculateFiltered(right, left, center_left, last_center_left);
230 }
231
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700232 double distance, horizontal_angle, vertical_angle;
Austin Schuh11945742016-04-13 22:18:36 -0700233 stereo.Process(filtered_center_left, filtered_center_right, &distance,
234 &horizontal_angle, &vertical_angle);
235 new_vision_status->left_image_timestamp =
236 left.target().image_timestamp();
237 new_vision_status->right_image_timestamp =
238 right.target().image_timestamp();
239 new_vision_status->left_send_timestamp = left.target().send_timestamp();
240 new_vision_status->right_send_timestamp = right.target().send_timestamp();
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700241 new_vision_status->horizontal_angle = horizontal_angle;
242 new_vision_status->vertical_angle = vertical_angle;
243 new_vision_status->distance = distance;
244 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400245 LOG_STRUCT(DEBUG, "vision", *new_vision_status);
246
247 if (!new_vision_status.Send()) {
248 LOG(ERROR, "Failed to send vision information\n");
249 }
250 }
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700251
252 if (target.camera_index() == 0) {
Austin Schuh11945742016-04-13 22:18:36 -0700253 LOG(DEBUG, "left_target: %s\n", left.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700254 } else {
Austin Schuh11945742016-04-13 22:18:36 -0700255 LOG(DEBUG, "right_target: %s\n",
256 right.target().ShortDebugString().c_str());
Austin Schuhc65b0ea2016-03-16 22:09:19 -0700257 }
Brian Silverman2ccf8c52016-03-15 00:22:26 -0400258 }
259}
260
261} // namespace vision
262} // namespace y2016
263
264int main(int /*argc*/, char ** /*argv*/) {
265 ::aos::InitNRT();
266 ::y2016::vision::Main();
267}