feat: require measured calibration target border

This commit is contained in:
fy59 2026-09-03 07:09:29 +02:00
parent b9e4309842
commit c8447c66c4
7 changed files with 19 additions and 8 deletions

View file

@ -59,6 +59,7 @@ Tooling continues to validate the frozen Science v1 requirements, including:
- exact ChArUco 9 x 7 / DICT_5X5_100 target identity;
- 30.000 mm squares and 21.000 mm markers;
- ten physical square measurements and instrument resolution;
- measured free white border of at least 30 mm, supplied explicitly by the session;
- immutable target, optical-state, solver and evidence digests;
- accepted and rejected view evidence;
- field-region, distance and angle diversity;

View file

@ -519,6 +519,8 @@ Calibration Evidence Solver v1 implements the external OpenCV 5.x evidence path
Calibration Solver Preflight v1. It remains outside the Lardon3D runtime and Project DB and its
deterministic synthetic CPU1 self-test is validated.
The solver session contract now also requires explicit measured `white_border >= 30 mm` evidence. This closes the previous gap between the physical target requirement and the future coordinator input; no default border width is inferred.
A bounded Tooling correction aligned planarity handling with Calibration Science v1: Science v1
defines a categorical physical planarity attestation, not a numeric flatness threshold. Tooling
therefore rejects invented finite `target_flatness_mm` values. `L3DCALB1` v1 and Calibration

View file

@ -27,6 +27,7 @@ User-facing TUI/control language PASS
External SSD controller VALIDATED
Calibration Solver Preflight v1 PASS
Calibration Evidence Solver v1 IMPLEMENTED/VALIDATED
Calibration solver white-border evidence PASS/FROZEN
Calibration Tooling planarity alignment PASS/FROZEN
```

View file

@ -8,6 +8,7 @@ CALIBRATION_TOOLING_V1=PASS/FROZEN
CALIBRATION_TOOLING_PLANARITY_ALIGNMENT=PASS/FROZEN
CALIBRATION_BOOTSTRAP_V1=PASS/FROZEN
CALIBRATION_EVIDENCE_SOLVER_V1=IMPLEMENTED/VALIDATED
CALIBRATION_SOLVER_WHITE_BORDER_V1=PASS/FROZEN
CALIBRATION_WORKFLOW=IN_PROGRESS
CURRENT_CALIBRATION_NEXT=WORKFLOW_COORDINATOR
```
@ -22,6 +23,8 @@ A bounded corrective review established that Calibration Science v1 defines targ
The external `tools/calibration_evidence_solver/` implementation is present and validated by its deterministic synthetic CPU1 self-test. It remains external to the Lardon3D runtime and Project DB.
Its session v1 now requires an explicit measured `white_border` of at least 30 mm. No default border width may be invented by the solver or the future coordinator.
## CURRENT
Historical S21 and A6000 Engine Bay campaigns:

View file

@ -22,6 +22,7 @@ Un fichier texte ASCII, sans commentaire ni espace dans les chemins, contient :
L3DCAL_SESSION_V1
target <target-id> <generator-sha256> DICT_5X5_100 9 7 30 21
measurement <instrument-id> <resolution-mm> <10-real-square-mm-values>
white_border <measured-free-white-border-mm>
planarity PASS <planarity-evidence-sha256>
decoder <decoder-identity> <decoder-version>
optical_state <optical-state-sha256> <complete-state-token>
@ -37,9 +38,10 @@ Il y a entre 1 et 4096 lignes `image`, chaque source SHA est unique, et chaque
image a exactement une preuve pré-solve, clipping et coordinate. Les dix
mesures réelles doivent être à 0.30 mm de 30.000 mm et leur étendue ne doit pas
dépasser 0.20 mm; l'instrument doit annoncer une résolution de 0.1 mm ou
meilleure. `planarity` est une attestation PASS hachée : Science v1 impose une
planche rigide plane mais ne fixe pas de seuil numérique que l'outil pourrait
inventer. `optical_state` est un jeton atomique complet du manifeste d'état
meilleure. `white_border` archive la bordure blanche physique libre mesurée et
doit être d'au moins 30 mm; aucune valeur par défaut n'est inventée.
`planarity` est une attestation PASS hachée : Science v1 impose une planche
rigide plane mais ne fixe pas de seuil numérique que l'outil pourrait inventer. `optical_state` est un jeton atomique complet du manifeste d'état
optique; aucune valeur inconnue n'est acceptée par convention.
Les `coordinate_point` consomment exactement les `N` comparaisons annoncées.

View file

@ -50,10 +50,10 @@ constexpr int kSquaresX = 9, kSquaresY = 7;
struct SessionEvidence {
std::string target_id, instrument, decoder, decoder_version;
std::array<unsigned char, 32> generator_hash{}, planarity_hash{};
double resolution_mm = -1, planarity_pass = 0;
double resolution_mm = -1, planarity_pass = 0, white_border_mm = -1;
std::array<double, 10> measurements{};
bool target = false, measurement = false, planarity = false;
bool decoder_set = false, optical_state = false;
bool white_border = false, decoder_set = false, optical_state = false;
std::string optical_state_fields;
};
@ -140,6 +140,7 @@ bool read_session(const std::string& path,std::vector<View>* views,std::array<un
if(!(s>>m)||!finite(m)){*why="measurement values";return false;}
}
evidence->measurement=true;
} else if(tag=="white_border") { if(evidence->white_border || !(s>>evidence->white_border_mm) || !finite(evidence->white_border_mm) || evidence->white_border_mm < 30.0) {*why="white border evidence";return false;} evidence->white_border=true;
} else if(tag=="planarity") { std::string result,h; if(evidence->planarity || !(s>>result>>h) || !hex(h,&evidence->planarity_hash) || (result!="PASS"&&result!="FAIL")) {*why="planarity evidence";return false;} evidence->planarity=true; evidence->planarity_pass=result=="PASS";
} else if(tag=="decoder") { if(evidence->decoder_set || !(s>>evidence->decoder>>evidence->decoder_version) || evidence->decoder.size()>128 || evidence->decoder_version.size()>128 || !json_token(evidence->decoder) || !json_token(evidence->decoder_version)) {*why="decoder identity/version";return false;} evidence->decoder_set=true;
} else if(tag=="optical_state") { std::string h; if(evidence->optical_state || !(s>>h>>evidence->optical_state_fields) || !hex(h,optical) || evidence->optical_state_fields.size()>1024 || !json_token(evidence->optical_state_fields) || evidence->optical_state_fields=="UNKNOWN") {*why="optical-state evidence";return false;} evidence->optical_state=true; have=true;
@ -150,7 +151,7 @@ bool read_session(const std::string& path,std::vector<View>* views,std::array<un
else if(tag=="distance") { std::string h;std::array<unsigned char,32>a{};double meters=0;int band=-1;if(!(s>>h>>meters>>band)||!hex(h,&a)||!finite(meters)||meters<=0||band<0||band>2){*why="distance evidence";return false;}View*v=find_view(views,a);if(!v||v->measured_distance>0){*why="distance source";return false;}v->measured_distance=meters;v->declared_distance_band=band; }
else {*why="unknown session record";return false;}
}
if(!have||!evidence->target||!evidence->measurement||!evidence->planarity||!evidence->planarity_pass||!evidence->decoder_set||!evidence->optical_state||views->empty()||views->size()>kMaxViews){*why="required session evidence";return false;}
if(!have||!evidence->target||!evidence->measurement||!evidence->white_border||!evidence->planarity||!evidence->planarity_pass||!evidence->decoder_set||!evidence->optical_state||views->empty()||views->size()>kMaxViews){*why="required session evidence";return false;}
double lo=std::numeric_limits<double>::infinity(),hi=-lo;for(double m:evidence->measurements){lo=std::min(lo,m);hi=std::max(hi,m);if(std::abs(m-30.0)>.30){*why="target measurement tolerance";return false;}}if(hi-lo>.20){*why="target measurement range";return false;}
std::sort(views->begin(),views->end(),[](const View&a,const View&b){return a.hash<b.hash;});
for(size_t i=1;i<views->size();++i) {
@ -335,7 +336,7 @@ bool synthetic_test() {
double delta=0;for(auto ray:std::array<cv::Point2d,5>{{{0,0},{-.7,-.7},{.7,-.7},{-.7,.7},{.7,.7}}}){auto eval=[](const Params&p,cv::Point2d q){double r2=q.x*q.x+q.y*q.y,rad=1+p.k1*r2+p.k2*r2*r2;return cv::Point2d(p.fx*(q.x*rad+2*p.p1*q.x*q.y+p.p2*(r2+2*q.x*q.x))+p.cx,p.fy*(q.y*rad+p.p1*(r2+2*q.y*q.y)+2*p.p2*q.x*q.y)+p.cy);};delta=std::max(delta,cv::norm(eval(a.p,ray)-eval(f.p,ray)));}
double iq=0,oq=0;for(const auto&x:v){iq=std::max(iq,x.image_conversion_max);oq=std::max(oq,x.object_conversion_max);}
double rr=0,rm=0;size_t rh=0,rc=0;if(!residuals(&v,all,a,&rr,&rm,&rh,&rc,&why))return false;
SessionEvidence evidence; evidence.target=true;evidence.measurement=true;evidence.planarity=true;evidence.planarity_pass=1;evidence.decoder_set=true;evidence.optical_state=true;evidence.target_id="synthetic";evidence.instrument="synthetic";evidence.decoder="synthetic_decoder";evidence.decoder_version="1";evidence.optical_state_fields="synthetic_only";evidence.resolution_mm=.1;evidence.measurements.fill(30.0);
SessionEvidence evidence; evidence.target=true;evidence.measurement=true;evidence.white_border=true;evidence.planarity=true;evidence.planarity_pass=1;evidence.decoder_set=true;evidence.optical_state=true;evidence.target_id="synthetic";evidence.instrument="synthetic";evidence.decoder="synthetic_decoder";evidence.decoder_version="1";evidence.optical_state_fields="synthetic_only";evidence.resolution_mm=.1;evidence.white_border_mm=30.0;evidence.measurements.fill(30.0);
Solve runs[3]={a,b,c};std::array<unsigned char,32> optical{};std::string bundle_why;
const std::filesystem::path left=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_left";
const std::filesystem::path right=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_right";std::error_code ec;std::filesystem::remove_all(left.string()+".bundle",ec);std::filesystem::remove_all(right.string()+".bundle",ec);
@ -355,6 +356,7 @@ bool manifest_negative_tests() {
x<<"L3DCAL_SESSION_V1\n"
<<"target board "<<h<<" DICT_5X5_100 9 7 30 21\n"
<<"measurement caliper 0.1 30 30 30 30 30 30 30 30 30 30\n"
<<"white_border 30\n"
<<"planarity PASS "<<h<<"\n"
<<"decoder qualified_decoder 1\n"
<<"optical_state "<<h<<" body_objective_zoom_focus_stabilization_format_pipeline\n"
@ -374,7 +376,7 @@ bool manifest_negative_tests() {
const std::string good=make("");
bool ok=parse(good); if(!ok)std::cerr<<"negative good\n";
auto altered=[&](const std::string& from,const std::string& to){std::string t=good;const size_t n=t.find(from);if(n==std::string::npos)return false;t.replace(n,from.size(),to);return !parse(t);};
const bool n1=altered("target board", "target_bad board"),n2=altered("planarity PASS", "planarity FAIL"),n3=altered(" 20 0 0\ncoordinate_point", " 19 0 0\ncoordinate_point"),n4=altered("qualified_decoder 1 0 100", "qualified_decoder 1 90 100");ok=ok&&n1&&n2&&n3&&n4;
const bool n1=altered("target board", "target_bad board"),n2=altered("planarity PASS", "planarity FAIL"),n3=altered(" 20 0 0\ncoordinate_point", " 19 0 0\ncoordinate_point"),n4=altered("qualified_decoder 1 0 100", "qualified_decoder 1 90 100"),n5=altered("white_border 30", "white_border 29");ok=ok&&n1&&n2&&n3&&n4&&n5;
std::error_code ec;std::filesystem::remove(p,ec);return ok;
}
} // namespace