Compare commits

...

2 Commits

8 changed files with 139 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

@ -271,6 +271,23 @@ again; too weak and the motor stalls on ordinary friction mid-sweep, trips the
same latch, and records a phantom endstop. The firmware's `steps_per_rev` guard same latch, and records a phantom endstop. The firmware's `steps_per_rev` guard
is what catches the second case, so it has to be set from the real travel. is what catches the second case, so it has to be set from the real travel.
> **The centre lives in `config.ini`, not here.** `install.sh` cannot write it —
> `hardware.ini` is merged *over* `config.ini` and wins, so a shared key would
> silently discard whatever `gimbal heading setup` had saved. What it does instead
> is work the requirement out from `steps_per_rev` and print it as a `NOTE` in the
> block, because the default is wrong for any geared-up rig:
>
> ```
> ; NOTE: half this travel is 605389 counts, so config.ini needs
> ; [Motor] yaw_center_count = 1000000 (or anything above 605389)
> ; The 500000 default would put the negative end at -105389.
> ```
>
> The range is centre ± half the travel, so a centre below half-travel puts the
> negative end under zero. On tower 121 the inherited `500000` homed to
> `-104749..1104749`; at `1000000` it homes to `395194..1604807`. A warning names
> it after every home if it is still too small.
**`travel_deg` is a nameplate fact**, and the host divides the measured homing **`travel_deg` is a nameplate fact**, and the host divides the measured homing
travel by it to derive `[Motor] <axis>_counts_per_deg` into `config.ini`. A rough travel by it to derive `[Motor] <axis>_counts_per_deg` into `config.ini`. A rough
angle gives a proportionally rough degree scale - 1 % out here is 1 % out on every angle gives a proportionally rough degree scale - 1 % out here is 1 % out on every

View File

@ -299,6 +299,24 @@ do_vimba() {
# and a non-numeric value makes the program refuse to start with a message about # and a non-numeric value makes the program refuse to start with a message about
# a config key. Absent, it starts and reports the axis as undeclared - naming the # a config key. Absent, it starts and reports the axis as undeclared - naming the
# missing keys and which axis will not move, which is the useful failure. # missing keys and which axis will not move, which is the useful failure.
# The centre lives in config.ini, not here - it is calibration, and hardware.ini
# is merged OVER config.ini, so a shared key would silently discard whatever
# `gimbal heading setup` had saved. All this file can do is say what the value
# has to be, which it can work out from the travel.
#
# The range is centre +/- half the travel, so a centre below half-travel puts the
# negative end under zero. The long-standing 500000 default suits the Delta's
# ~89k half-travel and does not survive a gear ratio several times higher.
center_note() {
local steps="$1" ax="$2"
[[ -z $steps ]] && return 0
local half=$(( steps / 2 ))
local want=$(( (half / 1000000 + 1) * 1000000 ))
echo "; NOTE: half this travel is $half counts, so config.ini needs"
echo "; [Motor] ${ax}_center_count = $want (or anything above $half)"
echo "; The 500000 default would put the negative end at $(( 500000 - half ))."
}
kv_or_todo() { kv_or_todo() {
local key="$1" value="$2" why="$3" local key="$1" value="$2" why="$3"
if [[ -n $value ]]; then if [[ -n $value ]]; then
@ -400,6 +418,7 @@ EOF
"mechanical angle between the hard stops. Rough is fine to start; every bearing inherits its error proportionally until 'gimbal heading setup' supersedes it" "mechanical angle between the hard stops. Rough is fine to start; every bearing inherits its error proportionally until 'gimbal heading setup' supersedes it"
kv_or_todo "steps_per_rev" "$STEPS_PER_REV" \ kv_or_todo "steps_per_rev" "$STEPS_PER_REV" \
"measured full travel IN COUNTS - read it off a homing run. The only guard against a PHANTOM endstop" "measured full travel IN COUNTS - read it off a homing run. The only guard against a PHANTOM endstop"
center_note "$STEPS_PER_REV" "$ax"
kv_or_todo "sw_mode" "$SW_MODE" \ kv_or_todo "sw_mode" "$SW_MODE" \
"TMC SW_MODE. 0 = no physical switches (StallGuard homing); set the bits if this axis has REFL/REFR wired" "TMC SW_MODE. 0 = no physical switches (StallGuard homing); set the bits if this axis has REFL/REFR wired"
else else

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);
}