fwt_software/config/config.example.ini

387 lines
19 KiB
INI

; Fire Gimbal Control - example configuration.
;
; A tower keeps its config.ini in its own folder, ~/.config/fire_gimbal_control/,
; together with its scan grid and camera profiles. That folder is outside the
; project folder, so a deploy never overwrites or deletes anything in it.
; Create it from this file with:
; scripts/tower-config seed
; The paths below (grid_file, profile_dir) are relative to that folder.
;
; WARNING: do not edit config.ini while the program is running. `gimbal heading
; offset`, `gimbal heading setup` and `gimbal pitch setup` rewrite the [Motor]
; block in place, so concurrent edits would be lost. Stop the program first.
;
; The real config.ini is gitignored because it contains credentials.
; MQTT credentials may instead be supplied via the environment variables
; FGC_MQTT_USER and FGC_MQTT_PW, which take precedence over the values here.
[General]
; Tower identity, substituted into all MQTT topics and CamEvent payloads.
tower_name = ExampleTower
; Desired seconds from one picture to the next (decimals allowed, e.g. 2.5).
image_interval = 5
; 1 = print motor telemetry each loop tick, 0 = quiet.
debug = 0
[Startup]
; What the tower does as it comes up - the boot service passes no flags, so this
; is the one place it is decided.
; home: run the endstop-finding home sequence at every start. The firmware keeps
; no position across a power cycle, so without it the axes stay un-homed and
; refuse every move. A tower in service needs true - and then it HOMES
; UNATTENDED at every boot. Absent => false.
home = true
; mode: manual or auto. manual waits and captures nothing until someone chooses
; a mode (key a, or `mode auto` over MQTT); auto starts sweeping the scan grid
; at once. auto is refused, and the tower stays in manual, if the gimbal is not
; homed by then. pan needs a heading and a span, so it is by command only.
; Absent => manual. Choose auto once the tower is proven.
mode = manual
[Network]
; MQTT broker address (the ground-station / ZKMS server).
zkms_server_ip = 127.0.0.1
; Broker port. 1883 is the MQTT default (RabbitMQ's MQTT plugin included); 8883 for
; TLS. Ignored if zkms_server_ip already carries a port ("host:1884") or is a full
; URI ("tcp://host:1884") - in both cases that value wins.
mqtt_port = 1883
; Prefer the FGC_MQTT_USER / FGC_MQTT_PW environment variables over these.
mqtt_user = CHANGE_ME
mqtt_pw = CHANGE_ME
[Camera]
; Defaults inherited by every camera block below; each may override them.
; JPEG XL encoding. distance: 0 = lossless, ~0.8 = near-lossless; effort 1..9.
jxl_distance = 0.8
jxl_effort = 4
; Raw profiles (PixelFormat BayerRG8 / BayerRG12p in the profile XML): the tower
; converts the sensor mosaic to colour itself. edge_aware (sharper; ~30 ms for
; 16 MP) or bilinear. A 12-bit raw frame is saved as a 12-bit file.
demosaic = edge_aware
; --- One [Camera.<name>] block per camera -----------------------------------
; Nothing is implied by position: the block names the camera, `label` names its
; output folder and its CamEvent "cam" field, and `driver` picks the backend.
;
; The camera's own settings are NOT here. They live in a settings XML exported
; from the VimbaX Viewer - the Viewer validates combinations as you configure,
; which a text file cannot - and the program hands that XML to the SDK. A
; profile named X is <profile_dir>/X.xml. Beside it the program writes X.txt, a
; generated human-readable record of the same settings; it configures nothing.
[Camera.rgb]
enabled = true
driver = vimba ; only vimba is implemented
id = DEV_0000000000 ; USB DEV_ id, or a GigE IP
label = RGB ; output subdir + CamEvent "cam"
profile_dir = camera_profiles/rgb
profile = standard ; <profile_dir>/standard.xml
; Clockwise rotation applied to saved images: 0, 90, 180 or 270.
rotation_deg = 0
; Per-profile timing. Not camera settings - how long the program waits around
; them. After the gimbal reports standstill it holds still settle_wait_ms, then
; keeps a frame according to settle_rule:
; fixed the newest frame, straight away
; brightness the first frame once brightness is in the camera's target band
; and steady for 3 frames (right 21 of 24 times in the tower tests,
; docs/raw12-pipeline-measurements.md); settle_wait_ms is then just
; the mechanical minimum
; frame_timeout_ms bounds the wait after settle_wait_ms; past it the newest frame
; is kept anyway (or the waypoint skipped if the camera delivers nothing).
; With several cameras the scan uses the LONGEST wait across them.
[Camera.rgb.standard]
settle_wait_ms = 500
frame_timeout_ms = 7000
settle_rule = brightness
[Camera.rgb.night]
settle_wait_ms = 2500
frame_timeout_ms = 5000
[Serial]
; Motor-controller serial device and baud rate.
device = /dev/ttyACM0
baud = 115200
[IMU]
; Xsens MTi orientation/inertial sensor. Wired to the LattePanda's RS-232 header
; (an onboard hardware UART), so this is a stable /dev/ttyS* node - NOT a USB
; device and NOT /dev/ttyUSB0-3 (those are the Quectel modem). Find it with
; ls /dev/ttyS* ; dmesg | grep -iE 'ttyS|LPSS|HSUART' (often /dev/ttyS4)
; Enable by declaring it in hardware.ini as [Hardware] imu = <model>; the legacy
; [Features] enable_imu applies only on a tower with no hardware.ini. The host
; reconfigures the MTi to a Euler + calibrated 100 Hz stream at startup.
device =
baud = 115200
; The host puts the MTi into a fixed output configuration at every startup
; (Euler + calibrated at 100 Hz) and then READS IT BACK to check the device
; actually took it. There is deliberately no setting for that configuration: it
; is a wire-format contract the parser depends on, not a preference. A mismatch
; is logged as an error and shown as a red MISCONFIGURED row in the Sensors
; view (press i) - the program keeps running so you can diagnose it.
;
; The MTi measures orientation against its OWN earth frame, and the classic
; unit's is NWU (X North, Y West, Z UP) - which reports pitch FALLING as the nose
; rises. The host re-references it to NED. That correction is not settable here:
; it is a fact about the device, stated once in kMtiReference (MtiProtocol.h),
; and shown as the Reference row in the Sensors view (press i).
;
; It is also NOT the same thing as the mount below, and one cannot substitute for
; the other: the earth reference multiplies from the left, the mount from the
; right. No x/y/z value can correct an inverted pitch.
;
; --- Mounting orientation --------------------------------------------------
; How the IMU is bolted to the gimbal. Both the gimbal and the IMU use a NED
; body frame:
;
; X = forward Y = right Z = down
; roll about X pitch about Y yaw about Z
; (positive follows the right-hand rule about that axis)
;
; x, y and z are DEGREES, and they are the rotation you apply TO THE IMU to
; bring it into alignment with the gimbal - the turns needed to get from the
; IMU's orientation to the gimbal's. So an IMU that physically sits turned 90
; deg to the right is corrected with z = -90.
;
; Applied in Z-X-Y order, each turn about the axes as they stand after the
; previous one: first z about down, then x about the new forward, then y about
; the new right.
;
; Worked examples:
; dead level, facing the same way as the gimbal ..... x=0 y=0 z=0
; upside down, still facing forward ................. x=180 y=0 z=0
; turned 90 deg clockwise seen from above ........... x=0 y=0 z=-90
; lying on its right side, facing forward ........... x=-90 y=0 z=0
; upside down AND turned to face backwards .......... x=180 y=0 z=180
;
; Every reading the program produces - orientation, acceleration, rate of turn
; and magnetic field - is rotated into the gimbal frame by this. Leaving all
; three at 0 changes nothing.
;
; Check it on the rig: press `i` for the Sensors view. Every reading there is
; already corrected, and IMU CONFIG shows the mount being applied. Level the
; gimbal - roll and pitch should read ~0. Tip the nose up - pitch goes
; positive. Roll to the right - roll goes positive. Slew right - yaw increases.
;
; NOTE: `gimbal pitch setup` fits IMU pitch against encoder counts, so a
; calibration made before this was set has the mount's error baked in. Re-run it
; after changing these. Yaw is unaffected - `gimbal heading setup` sights
; landmarks and never consults the IMU.
x = 0
y = 0
z = 0
[Env]
; Ambient temperature/humidity sensor (Adafruit SHT41), wired to the
; LattePanda's OWN native I2C bus - NOT the Arduino/motor serial link. Bus
; number varies by board; confirm with `i2cdetect -y N` (SHT41 shows up at
; 0x44). Enable by declaring it in hardware.ini as [Hardware] env = <model>; the
; legacy [Features] enable_env applies only on a tower with no hardware.ini (or
; run with --mock-env, which implies it). i2c_addr takes decimal or 0x-hex.
i2c_device = /dev/i2c-1
i2c_addr = 0x44
period_ms = 2000
[Motor]
; Degrees<->encoder-counts maps, anchored on the mechanical centre found by
; homing. The firmware speaks only in absolute encoder counts; these map them to
; the heading/elevation degrees used by mode pan, gimbal move and CamEvent.
;
; Homing finds both endstops, moves to the middle of that range and DEFINES the
; midpoint as <axis>_center_count. Anchoring on the midpoint rather than one
; endstop averages the error of both, and a large positive value keeps the whole
; working range positive (the driver misbehaves on negative positions).
;
; Set the heading with `gimbal heading setup` (sight two known landmarks) or
; `gimbal heading offset <deg>` to nudge it. Pitch uses `gimbal pitch setup`,
; which needs the IMU.
;
; Travel limits are NOT configured here - they are derived from the firmware's
; homed soft limits after every home, the only place they can be correct.
;
; Mechanical travel between the hard endstops now belongs in hardware.ini as
; [Motor.<axis>] travel_deg, where homing uses it to derive counts_per_deg below.
; Mechanical travel of this gimbal (between hard endstops, found at homing):
; Yaw ~320 deg (NOT a full revolution: there is an unreachable arc)
; Pitch 40 deg
;
; Yaw starting point: firmware steps_per_rev=177000 over ~180 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
; True bearing (0 = north) the gimbal points at when parked at the centre.
yaw_heading_at_center = 0.0
; Clearance kept clear of each hard endstop, in counts. Sent to the firmware,
; which enforces it. Counts, not degrees: it must apply at homing time, before
; any degree map is trusted.
yaw_soft_margin_counts = 5000
; Pitch (physical endstops). CALIBRATE with `gimbal pitch setup`.
pitch_counts_per_deg = 8333.33
pitch_center_count = 500000
; Elevation above level (+ = up) at the centre. This is NOT 0: the mechanical
; centre is the middle of pitch travel, which sits well above the horizon.
; A yaw-only tower has no pitch travel: set this to its camera's fixed tilt
; (0 if level) - it is the CamEvent pitch there when no IMU is fitted.
pitch_angle_at_center = 10.0
pitch_soft_margin_counts = 5000
; Live encoder-error WARN threshold in counts (0 = off).
enc_error_warn_counts = 400
; How long a full two-end homing may take before the host calls it failed. A
; property of the drivetrain: a high gear ratio searching at a gentle speed takes
; minutes, and too tight a value reports a healthy axis as broken.
homing_timeout_s = 65
[Scan]
; Capture scan grid (the (yaw,pitch) waypoints auto-sweep steps through).
; grid_file is the ONLY source: that CSV is used verbatim (one
; "yaw_deg,pitch_deg" per line, '#' comments allowed). Nothing is generated -
; leave it blank and auto-sweep stays off with a message saying so, rather than
; the tower inventing an arc nobody chose.
; A relative path resolves against the directory of the config file it is
; written in (on a tower ~/.config/fire_gimbal_control/, so: scan.csv there).
; The sweep always runs front to back, then returns to the first waypoint and
; repeats, so every pass captures the waypoints in the same order. The return
; leg is one long slew (the yaw arc cannot wrap), so order the grid with that
; in mind.
grid_file = scan.csv
[Paths]
; Directory for saved .jxl images. Supports leading ~ and $ENV expansion.
; If blank, defaults to $XDG_DATA_HOME/fire_gimbal_control/images
; (i.e. ~/.local/share/fire_gimbal_control/images).
output_dir =
[Features]
; Enable/disable subsystems and select mock (simulated) implementations.
; CLI flags (e.g. --no-mqtt, --mock-camera, --mock-serial) override these.
enable_mqtt = true
; false: open no camera at all - a mock source stands in, no Vimba X needed and
; no profile XMLs required. For bringing a tower up one subsystem at a time.
enable_camera = true
enable_serial = true
; LEGACY. On a tower with a hardware.ini these are derived from [Hardware] imu
; and [Hardware] env - declaring a sensor there is what enables it, and a flag
; here that contradicts the declaration is warned about and ignored. They remain
; only for towers that have not been migrated.
enable_imu = false
enable_env = false
; The mocks are NOT legacy: substituting a simulated device is a choice about
; this run, not a statement about what is bolted to the tower, so it stays here.
mock_camera = false
; The simulated camera's frames: small (640x480 colour) or full (the real
; camera's 5328x3040 12-bit raw - saving then loads the CPU as on a tower).
;mock_camera_size = small
mock_serial = false
mock_imu = false
mock_env = false
; Make the simulated motor controller run the config handshake: emit NEED lines
; at startup and reject HOME/MOVE/ENABLE until every requested key is answered
; from hardware.ini. How the host half is exercised before a firmware that asks
; exists - and afterwards, on a dev box with no rig.
mock_needcfg = false
[Test]
; Hardware self-test command (`test`). `profile` is the default profile used when
; none is named on the command line. `baseline_file` is where `test baseline` pins
; a golden report (empty => <logdir>/test_baseline.txt). Each [TestProfile.*] is a
; named bundle of repetition counts, sampling windows and pass thresholds; modules
; read the keys they need (unknown keys are ignored, so new tests can add knobs
; here without code changes).
profile = standard
baseline_file =
; `test hardware camera` USB preconditions. Both are facts about the machine, not
; tuning. usbfs_memory_mb reverts to 16 on every reboot unless it is on the kernel
; cmdline, and frame delivery stalls below ~1000 (known-issues #19).
usbfs_min_mb = 1000
; The camera port's usb3_lpm_permit node. The path differs per board, so it cannot
; be guessed: left empty, the LPM check reports "not checked" rather than passing.
; Find it with the procedure in known-issues.md, e.g.
; usb3_lpm_path = /sys/devices/.../usb2/2-0:1.0/usb2-port3/usb3_lpm_permit
usb3_lpm_path =
[TestProfile.standard]
reps = 5 ; repetitions for homing / friction / balance
drift_warn_pct = 15 ; flag a metric drifting more than this vs baseline
drift_gates_pass = false ; true => a drift flag also fails the metric
home_spread_max_counts = 80 ; max endstop-count spread across homings
home_ms_min = 4000 ; expected homing-time band (ms)
home_ms_max = 20000
encoder_diag_reps = 2
imu_sample_window_ms = 3000 ; static window for IMU health
imu_drift_window_ms = 60000 ; long static window for the yaw-drift fit
imu_yaw_noise_max_deg = 0.5
imu_yaw_drift_max_deg_min = 1.0
imu_expected_hz = 100
; test camera. frames: how many to grab, and how long the first may take after
; the stream starts. dark: the share of bytes that must be at or below
; camera_black_max at minimum exposure and gain (a U-1240c's floor is 2..17).
; bright: at maximum gain and this exposure, the mean must rise by at least
; camera_bright_min_delta over dark. settle: frames to let pass after a change.
camera_frames = 5
camera_warmup_ms = 10000
camera_settle_frames = 3
camera_clip_min_pct = 99
camera_black_max = 20
camera_bright_exposure_us = 500000
camera_bright_min_delta = 2
; imu_expected_profile = VRU_general ; assert the active XKF profile, if set
backlash_step_counts = 200,500,1000 ; small steps probing reversal dead-band
balance_steps = 10 ; pitch up/down current-profile resolution
balance_asym_max = 4 ; max |up-down| current (CS) before "imbalanced"
cpu_temp_max_c = 85
disk_free_min_gb = 5
ram_avail_min_mb = 256
[TestProfile.quick]
reps = 3
imu_sample_window_ms = 1000
imu_drift_window_ms = 10000
[TestProfile.strict]
reps = 10
drift_warn_pct = 8
drift_gates_pass = true
[UI]
; Full-screen terminal dashboard (sectioned, colored, live status + log pane).
; false => headless line console (default; stdin commands, stdout logs).
; CLI --tui / --no-tui override this. Requires a build with WITH_TUI=ON.
enable_tui = false
[Logging]
; level: trace|debug|info|warn|error|off (default info). --log-level overrides.
; This is the CONSOLE level only. The log file below has its own, so `off` here
; quietens the screen and the tower keeps recording; --no-log-file stops that.
level = info
; Verbatim per-category wire tracing, independent of the level above. Comma list
; of: serial, mqtt, camera, control, all, none. OFF by default. --trace overrides.
; Lines are tagged [SERIAL]/[MQTT]/[CAMERA]/[CONTROL] with TX/RX direction, e.g.
; [SERIAL] TX kd180 / [SERIAL] RX $;1234;0;...
;
; NOTE this also governs what reaches the LOG FILE. The serial trace is ~4 ST
; lines a second, taking steady-state logging from ~5 MB/day to ~41 MB/day -
; enough to churn the whole retained window in a couple of days and age out the
; startup record. Turn it on to chase a problem, off again afterwards.
trace =
; --- the persistent log file ------------------------------------------------
; A tower runs detached for days, and in TUI mode every line goes to a 500-line
; on-screen ring buffer and nowhere else - so the record dies with the session.
; This is that record: <data dir>/logs/fgc_<stamp>.<NNN>.log, beside the test and
; diag reports.
file = true
; The file is normally MORE verbose than the console: a readable dashboard, a
; detailed file. Independent of `level` above.
file_level = debug
; Bounded at file_keep x file_max_mb (200 MB here). A new file per run, rotated
; by size within it, oldest pruned. Steady state is ~5 MB/day.
file_max_mb = 20
file_keep = 10
; Where to put them. Empty => the data log dir, alongside test_*/diag_* reports.
file_dir =