Warn when the homed travel falls below zero, and say why the centre matters

This commit is contained in:
pgdalmeida 2026-09-21 23:06:37 +02:00
parent c2a2459f81
commit 57a166c9a4
Signed by: pedro.almeida
GPG Key ID: D4A6C394DF13F1D7
6 changed files with 103 additions and 1 deletions

View File

@ -178,6 +178,11 @@ period_ms = 2000
; ;
; Yaw starting point: firmware steps_per_rev=177000 over ~180 deg => ~983.33. ; Yaw starting point: firmware steps_per_rev=177000 over ~180 deg => ~983.33.
yaw_counts_per_deg = 983.33 yaw_counts_per_deg = 983.33
; MUST exceed half the homed travel: the range is centre +/- half-travel, so a
; smaller centre puts the negative end below zero. 500000 suits the Delta
; (~89k half-travel); the IOTA's higher gear ratio needs 1000000. The centre
; itself is arbitrary - only the span is physical - and a warning names it
; after each home if it is too small.
yaw_center_count = 500000 yaw_center_count = 500000
; True bearing (0 = north) the gimbal points at when parked at the centre. ; True bearing (0 = north) the gimbal points at when parked at the centre.
yaw_heading_at_center = 0.0 yaw_heading_at_center = 0.0

View File

@ -67,7 +67,7 @@ Parsed and validated by `ConfigLoader` ([src/core/Config.cpp](../src/core/Config
| `Logging` | `trace` | csv | — | Wire-trace categories, off by default (`--trace` overrides) | | `Logging` | `trace` | csv | — | Wire-trace categories, off by default (`--trace` overrides) |
| `UI` | `enable_tui` | bool | `false` | Full-screen terminal dashboard (`--tui`/`--no-tui` override; needs `WITH_TUI=ON`) | | `UI` | `enable_tui` | bool | `false` | Full-screen terminal dashboard (`--tui`/`--no-tui` override; needs `WITH_TUI=ON`) |
| `Motor` | `yaw_counts_per_deg` / `pitch_counts_per_deg` | float | `983.33` / — | Encoder counts per degree (**calibrate**; may be negative to flip) | | `Motor` | `yaw_counts_per_deg` / `pitch_counts_per_deg` | float | `983.33` / — | Encoder counts per degree (**calibrate**; may be negative to flip) |
| `Motor` | `yaw_center_count` / `pitch_center_count` | int | `500000` | Count homing **defines** as the middle of the found travel | | `Motor` | `yaw_center_count` / `pitch_center_count` | int | `500000` | Count homing **defines** as the middle of the found travel. **Must exceed half the travel**, or the negative end falls below zero — the `500000` default suits the Delta's ~89k half-travel, not a geared-up rig (the IOTA needs 1000000). A warning names it after each home |
| `Motor` | `yaw_heading_at_center` | float | `0` | True bearing (0 = north) at that centre (**calibrate**) | | `Motor` | `yaw_heading_at_center` | float | `0` | True bearing (0 = north) at that centre (**calibrate**) |
| `Motor` | `pitch_angle_at_center` | float | `0` | Elevation above level at that centre (**calibrate**; not 0 on a real rig) | | `Motor` | `pitch_angle_at_center` | float | `0` | Elevation above level at that centre (**calibrate**; not 0 on a real rig) |
| `Motor` | `yaw_soft_margin_counts` / `pitch_soft_margin_counts` | int | `5000` | Clearance kept from each hard endstop; sent to the firmware, which enforces it | | `Motor` | `yaw_soft_margin_counts` / `pitch_soft_margin_counts` | int | `5000` | Clearance kept from each hard endstop; sent to the firmware, which enforces it |

View File

@ -88,4 +88,20 @@ struct DrivetrainEcho {
CheckResult checkDrivetrainAdopted(const std::string& axis, const DrivetrainExpectation& want, CheckResult checkDrivetrainAdopted(const std::string& axis, const DrivetrainExpectation& want,
const DrivetrainEcho& got); const DrivetrainEcho& got);
// Does the homed travel fit entirely in positive counts?
//
// `center_count` defines where the middle of the found travel sits, so the range
// is center +/- half the travel. A center smaller than half the travel puts the
// negative end below zero. The 500000 default was sized for the Delta, whose
// half-travel is ~89k; a drivetrain geared up by 7x has ~605k and crosses zero.
//
// Whether the TMC5160 actually misbehaves there is NOT established - XACTUAL is
// a signed 32-bit register and the firmware round-trips the sign correctly - but
// homing is the only thing that has ever driven that far, so the region is
// untested in normal operation. The cheap fix is to not be there.
//
// `center` and `half_travel` are in counts. A non-positive half_travel means the
// axis has not homed, and nothing is checked.
CheckResult checkTravelFitsPositive(const std::string& axis, long center, long half_travel);
} // namespace fgc } // namespace fgc

View File

@ -11,6 +11,7 @@
#include "fgc/CameraProfile.h" #include "fgc/CameraProfile.h"
#include "fgc/AxisSpeed.h" #include "fgc/AxisSpeed.h"
#include "fgc/FirmwareConfig.h" #include "fgc/FirmwareConfig.h"
#include "fgc/HardwareChecks.h"
#include "fgc/HardwarePresence.h" #include "fgc/HardwarePresence.h"
#include "fgc/TmcCurrent.h" #include "fgc/TmcCurrent.h"
#include "fgc/ICameraSource.h" #include "fgc/ICameraSource.h"
@ -1498,6 +1499,15 @@ struct Application::Impl {
const long want_margin = const long want_margin =
is_yaw ? cfg.yaw_soft_margin_counts : cfg.pitch_soft_margin_counts; is_yaw ? cfg.yaw_soft_margin_counts : cfg.pitch_soft_margin_counts;
// The centre is arbitrary, but it has to be big enough that the whole
// travel stays positive: the range is centre +/- half the travel, so
// a centre below half-travel puts the negative end under zero. The
// 500000 default was sized for the Delta's ~89k half-travel and does
// not survive a gear ratio 7x higher.
const auto fits = checkTravelFitsPositive(name, map.center_count,
(a.lim_pos - a.lim_neg) / 2);
if (fits.ran && !fits.pass) LOG_WARN << fits.detail;
// One encoder quantum of slack: ENC_CONST makes X_ENC advance in 12.8 // One encoder quantum of slack: ENC_CONST makes X_ENC advance in 12.8
// microstep steps, so the midpoint can land a count or so off. // microstep steps, so the midpoint can land a count or so off.
const long mid = a.lim_neg + (a.lim_pos - a.lim_neg) / 2; const long mid = a.lim_neg + (a.lim_pos - a.lim_neg) / 2;

View File

@ -184,4 +184,28 @@ CheckResult checkDeclaredValues(const std::string& axis, const DeclaredValue* va
return r; return r;
} }
CheckResult checkTravelFitsPositive(const std::string& axis, long center, long half_travel) {
CheckResult r;
if (half_travel <= 0) {
r.ran = false;
r.detail = axis + ": not homed, so the travel range is not known yet";
return r;
}
const long lo = center - half_travel;
if (lo >= 0) {
r.detail = axis + ": travel is entirely positive (" + std::to_string(lo) + " .. " +
std::to_string(center + half_travel) + ")";
return r;
}
r.pass = false;
r.detail = axis + ": homed travel reaches " + std::to_string(lo) +
" -- below zero. [Motor] " + axis + "_center_count is " +
std::to_string(center) + " but half the travel is " +
std::to_string(half_travel) + ", so raise the centre above that (a round "
"number with headroom, e.g. " +
std::to_string(((half_travel / 1000000) + 1) * 1000000) +
"). The centre is arbitrary; only the span is physical";
return r;
}
} // namespace fgc } // namespace fgc

View File

@ -174,3 +174,50 @@ TEST_CASE("a real mismatch alongside an absent field still names the mismatch")
CHECK_FALSE(r.pass); CHECK_FALSE(r.pass);
CHECK(r.detail.find("GLOBALSCALER wanted 80, firmware has 255") != std::string::npos); CHECK(r.detail.find("GLOBALSCALER wanted 80, firmware has 255") != std::string::npos);
} }
// --- the travel must fit in positive counts -------------------------------
TEST_CASE("the Delta's centre comfortably holds its travel") {
// ~89k half-travel against a 500000 centre: 411k .. 589k, all positive.
auto r = checkTravelFitsPositive("yaw", 500000, 89000);
CHECK(r.ran);
CHECK(r.pass);
}
TEST_CASE("the IOTA's gear ratio pushes the same centre below zero") {
// The real case: half-travel 604749 against the inherited 500000 default
// gives lim_neg -104749, which is what tower 121 actually homed to.
auto r = checkTravelFitsPositive("yaw", 500000, 604749);
CHECK(r.ran);
CHECK_FALSE(r.pass);
CHECK(r.detail.find("-104749") != std::string::npos);
CHECK(r.detail.find("yaw_center_count") != std::string::npos);
// The suggestion is the next round million above half the travel - 1000000
// here, which is the value actually adopted. Not 2000000: over-suggesting
// wastes range for no reason.
CHECK(r.detail.find("1000000") != std::string::npos);
}
TEST_CASE("raising the centre fixes it") {
auto r = checkTravelFitsPositive("yaw", 1000000, 604749);
CHECK(r.pass);
CHECK(r.detail.find("395251") != std::string::npos);
}
TEST_CASE("the suggestion scales with the drivetrain, not a fixed constant") {
// A ratio big enough that 1000000 would not be enough either.
auto r = checkTravelFitsPositive("yaw", 500000, 1400000);
CHECK_FALSE(r.pass);
CHECK(r.detail.find("2000000") != std::string::npos);
}
TEST_CASE("exactly touching zero is accepted, one count below is not") {
CHECK(checkTravelFitsPositive("yaw", 604749, 604749).pass);
CHECK_FALSE(checkTravelFitsPositive("yaw", 604748, 604749).pass);
}
TEST_CASE("an un-homed axis is not checked") {
auto r = checkTravelFitsPositive("pitch", 500000, 0);
CHECK_FALSE(r.ran);
CHECK(r.pass);
}