38 if (context.intrinsic_results.empty()) {
39 result.
summary[
"status"] =
"waiting_for_intrinsic_stage";
43 if (!context.has_handeye_config()) {
44 result.
summary[
"status"] =
"missing_config";
49 const auto& cfg = context.handeye_config();
50 if (cfg.rigs.empty()) {
51 result.
summary[
"status"] =
"no_rigs_configured";
56 const auto sensor_index = build_sensor_index(context.dataset.planar_cameras);
58 context.handeye_results.clear();
59 if (!context.artifacts.is_object()) {
60 context.artifacts = nlohmann::json::object();
62 auto& handeye_artifacts = context.artifacts[
"hand_eye"];
63 if (!handeye_artifacts.is_object()) {
64 handeye_artifacts = nlohmann::json::object();
67 bool overall_success =
true;
68 bool any_success =
false;
69 nlohmann::json rigs_json = nlohmann::json::array();
71 for (
const auto& rig : cfg.rigs) {
72 nlohmann::json rig_json;
73 rig_json[
"rig_id"] = rig.rig_id;
74 rig_json[
"sensor_count"] = rig.sensors.size();
75 rig_json[
"min_angle_deg"] = rig.min_angle_deg;
77 nlohmann::json sensors_json = nlohmann::json::array();
78 bool rig_success =
true;
79 bool rig_any_sensor =
false;
81 ensure_artifact_slot(handeye_artifacts, rig.rig_id, rig.min_angle_deg, rig.options);
82 auto& sensors_artifact = handeye_artifacts[rig.rig_id][
"sensors"];
84 for (
const auto& sensor_id : rig.sensors) {
85 nlohmann::json sensor_json;
86 sensor_json[
"sensor_id"] = sensor_id;
87 sensor_json[
"requested_observations"] = rig.observations.size();
88 sensor_json[
"min_angle_deg"] = rig.min_angle_deg;
90 const auto intrinsics_it = context.intrinsic_results.find(sensor_id);
91 if (intrinsics_it == context.intrinsic_results.end()) {
92 sensor_json[
"status"] =
"missing_intrinsics";
94 sensors_json.push_back(sensor_json);
95 sensors_artifact[sensor_id] = sensor_json;
99 const auto index_it = sensor_index.find(sensor_id);
100 if (index_it == sensor_index.end()) {
101 sensor_json[
"status"] =
"missing_detections";
103 sensors_json.push_back(sensor_json);
104 sensors_artifact[sensor_id] = sensor_json;
108 const auto& intrinsics = intrinsics_it->second;
109 const auto& detection_index = index_it->second;
110 const auto& camera = intrinsics.refine_result.camera;
112 nlohmann::json view_reports = nlohmann::json::array();
113 SensorAccumulators accum;
115 for (
const auto& view_cfg : rig.observations) {
116 nlohmann::json view_json;
117 if (!view_cfg.view_id.empty()) {
118 view_json[
"id"] = view_cfg.view_id;
120 view_json[
"base_pose"] = view_cfg.base_se3_gripper;
122 const auto image_it = view_cfg.images.find(sensor_id);
123 if (image_it == view_cfg.images.end()) {
124 view_json[
"status"] =
"missing_image_reference";
125 view_reports.push_back(std::move(view_json));
129 const auto det_it = detection_index.image_lookup.find(image_it->second);
130 if (det_it == detection_index.image_lookup.end()) {
131 view_json[
"status"] =
"image_not_in_dataset";
132 view_reports.push_back(std::move(view_json));
136 const auto* image_det = det_it->second;
137 auto planar_view = make_planar_view(*image_det);
138 view_json[
"points"] = planar_view.size();
140 if (planar_view.size() < 4U) {
141 view_json[
"status"] =
"insufficient_points";
142 view_reports.push_back(std::move(view_json));
147 accum.base.push_back(view_cfg.base_se3_gripper);
148 accum.cam.push_back(cam_se3_target);
149 view_json[
"status"] =
"ok";
150 view_reports.push_back(std::move(view_json));
153 sensor_json[
"used_observations"] = accum.cam.size();
154 sensor_json[
"views"] = view_reports;
156 if (accum.cam.size() < 2U) {
157 sensor_json[
"status"] =
158 accum.cam.empty() ?
"no_observations" :
"insufficient_observations";
160 sensors_json.push_back(sensor_json);
161 sensors_artifact[sensor_id] = sensor_json;
167 rig.min_angle_deg, rig.options);
168 sensor_json[
"status"] = he_result.core.success ?
"ok" :
"optimization_failed";
169 sensor_json[
"success"] = he_result.core.success;
170 sensor_json[
"final_cost"] = he_result.core.final_cost;
171 sensor_json[
"report"] = he_result.core.report;
172 sensor_json[
"g_se3_c"] = he_result.g_se3_c;
173 if (he_result.core.covariance.size() > 0) {
174 sensor_json[
"covariance"] = he_result.core.covariance;
177 sensors_artifact[sensor_id] = sensor_json;
179 if (he_result.core.success) {
180 rig_any_sensor =
true;
181 context.handeye_results[rig.rig_id][sensor_id] = he_result;
185 }
catch (
const std::exception& ex) {
186 sensor_json[
"status"] =
"estimation_error";
187 sensor_json[
"error"] = ex.what();
188 sensors_artifact[sensor_id] = sensor_json;
192 sensors_json.push_back(sensor_json);
195 if (rig_any_sensor && rig_success) {
196 rig_json[
"status"] =
"ok";
198 }
else if (rig_any_sensor) {
199 rig_json[
"status"] =
"partial_success";
201 overall_success =
false;
203 rig_json[
"status"] =
"failed";
204 overall_success =
false;
207 rig_json[
"sensor_reports"] = std::move(sensors_json);
208 rigs_json.push_back(std::move(rig_json));
211 result.
summary[
"rigs"] = std::move(rigs_json);
212 if (any_success && overall_success) {
213 result.
summary[
"status"] =
"ok";
215 }
else if (any_success) {
216 result.
summary[
"status"] =
"partial_success";
219 result.
summary[
"status"] =
"failed";